diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000000..c3fcb0ea9591 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,104 @@ +# With infos from +# http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ +# https://packaging.python.org/en/latest/appveyor/ +--- + +# Backslashes in quotes need to be escaped: \ -> "\\" +branches: + except: + - /auto-backport-.*/ + - /^v\d+\.\d+\.[\dx]+-doc$/ + +skip_commits: + message: /\[ci doc\]/ + files: + - doc/ + - galleries/ + +clone_depth: 50 + +image: Visual Studio 2022 + +environment: + + global: + PYTHONFAULTHANDLER: 1 + PYTHONIOENCODING: UTF-8 + PYTEST_ARGS: -rfEsXR --numprocesses=auto --timeout=300 --durations=25 + --cov-report= --cov=lib --log-level=DEBUG + + matrix: + - PYTHON_VERSION: "3.11" + TEST_ALL: "yes" + +# We always use a 64-bit machine, but can build x86 distributions +# with the PYTHON_ARCH variable +platform: + - x64 + +# all our python builds have to happen in tests_script... +build: false + +cache: + - '%LOCALAPPDATA%\pip\Cache' + - '%USERPROFILE%\.cache\matplotlib' + +init: + - ps: + Invoke-Webrequest + -URI https://micro.mamba.pm/api/micromamba/win-64/latest + -OutFile C:\projects\micromamba.tar.bz2 + - ps: C:\PROGRA~1\7-Zip\7z.exe x C:\projects\micromamba.tar.bz2 -aoa -oC:\projects\ + - ps: C:\PROGRA~1\7-Zip\7z.exe x C:\projects\micromamba.tar -ttar -aoa -oC:\projects\ + - 'set PATH=C:\projects\Library\bin;%PATH%' + - micromamba shell init --shell cmd.exe + - micromamba config set always_yes true + - micromamba config prepend channels conda-forge + - micromamba info + +install: + - micromamba env create -f environment.yml python=%PYTHON_VERSION% pywin32 + - micromamba activate mpl-dev + +test_script: + # Now build the thing.. + - set LINK=/LIBPATH:%cd%\lib + - pip install -v --no-build-isolation --editable .[dev] + # this should show no freetype dll... + - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" + - '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' + + # this are optional dependencies so that we don't skip so many tests... + - if x%TEST_ALL% == xyes micromamba install -q ffmpeg inkscape + # miktex is available on conda, but seems to fail with permission errors. + # missing packages on conda-forge for imagemagick + # This install sometimes failed randomly :-( + # - choco install imagemagick + + # Test import of tkagg backend + - python -c + "import matplotlib as m; m.use('tkagg'); + import matplotlib.pyplot as plt; + print(plt.get_backend())" + # tests + - echo The following args are passed to pytest %PYTEST_ARGS% + - pytest %PYTEST_ARGS% + +artifacts: + - path: result_images\* + name: result_images + type: Zip + +on_finish: + - micromamba install codecov + - codecov -e PYTHON_VERSION PLATFORM -n "%PYTHON_VERSION% Windows" + +on_failure: + # Generate a html for visual tests + - python tools/visualize_tests.py --no-browser + - echo zipping images after a failure... + - 7z a result_images.zip result_images\ | grep -v "Compressing" + - appveyor PushArtifact result_images.zip + +matrix: + fast_finish: true diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..40ba933cf0d9 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,262 @@ +# Circle CI configuration file +# https://circleci.com/docs/ +--- +version: 2.1 + + +####################################### +# Define some common steps as commands. +# + +commands: + check-skip: + steps: + - run: + name: Check-skip + command: | + export git_log=$(git log --max-count=1 --pretty=format:"%B" | tr "\n" " ") + echo "Got commit message:" + echo "${git_log}" + if [[ -v CIRCLE_PULL_REQUEST ]] && + [[ $git_log =~ (\[skip circle\]|\[circle skip\]|\[skip doc\]|\[doc skip\]) ]]; then + echo "Skip detected, exiting job ${CIRCLE_JOB} for PR ${CIRCLE_PULL_REQUEST}." + circleci-agent step halt; + fi + + merge: + steps: + - run: + name: Merge with upstream + command: | + if ! git remote -v | grep upstream; then + git remote add upstream https://github.com/matplotlib/matplotlib.git + fi + git fetch upstream + if [[ "$CIRCLE_BRANCH" != "main" ]] && \ + [[ "$CIRCLE_PR_NUMBER" != "" ]]; then + echo "Merging ${CIRCLE_PR_NUMBER}" + git pull --ff-only upstream "refs/pull/${CIRCLE_PR_NUMBER}/merge" + fi + + apt-install: + steps: + - run: + name: Install apt packages + command: | + sudo apt-get -qq update + sudo apt-get install -yy --no-install-recommends \ + cm-super \ + dvipng \ + ffmpeg \ + fonts-crosextra-carlito \ + fonts-freefont-otf \ + fonts-noto-cjk \ + fonts-wqy-zenhei \ + graphviz \ + inkscape \ + lmodern \ + ninja-build \ + optipng \ + texlive-fonts-recommended \ + texlive-latex-base \ + texlive-latex-extra \ + texlive-latex-recommended \ + texlive-pictures \ + texlive-xetex + + fonts-install: + steps: + - restore_cache: + key: fonts-4 + - run: + name: Install custom fonts + command: | + mkdir -p ~/.local/share/fonts + wget -nc \ + https://github.com/google/fonts/blob/master/ofl/felipa/Felipa-Regular.ttf?raw=true \ + -O ~/.local/share/fonts/Felipa-Regular.ttf || true + wget -nc \ + https://github.com/ipython/xkcd-font/blob/master/xkcd-script/font/xkcd-script.ttf?raw=true \ + -O ~/.local/share/fonts/xkcd-Script.ttf || true + fc-cache -f -v + - save_cache: + key: fonts-4 + paths: + - ~/.local/share/fonts/ + + pip-install: + description: Upgrade pip and setuptools and wheel to get as clean an install as possible + steps: + - run: + name: Upgrade pip, setuptools, wheel + command: | + python -m pip install --upgrade --user pip + python -m pip install --upgrade --user wheel + python -m pip install --upgrade --user 'setuptools!=60.6.0' + + doc-deps-install: + parameters: + numpy_version: + type: string + default: "" + steps: + - run: + name: Install Python dependencies + command: | + python -m pip install --user -r requirements/dev/build-requirements.txt + python -m pip install --user \ + numpy<< parameters.numpy_version >> \ + -r requirements/doc/doc-requirements.txt + python -m pip install --no-deps --user \ + git+https://github.com/matplotlib/mpl-sphinx-theme.git + + mpl-install: + steps: + - run: + name: Install Matplotlib + command: | + if [[ "$CIRCLE_BRANCH" == v*-doc ]]; then + # The v*-doc branches must build against the specified release. + version=${CIRCLE_BRANCH%-doc} + version=${version#v} + python -m pip install matplotlib==${version} + else + python -m pip install --user --verbose \ + --no-build-isolation --editable .[dev] + fi + - save_cache: + key: build-deps-2 + paths: + - subprojects/packagecache + + doc-build: + steps: + - restore_cache: + keys: + - sphinx-env-v1-{{ .BuildNum }}-{{ .Environment.CIRCLE_JOB }} + - sphinx-env-v1-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}-{{ .Environment.CIRCLE_JOB }} + - run: + name: Build documentation + command: | + # Set epoch to date of latest tag. + export SOURCE_DATE_EPOCH="$(git log -1 --format=%at $(git describe --abbrev=0))" + # Set release mode only when deploying to devdocs. + if [ "$CIRCLE_PROJECT_USERNAME" = "matplotlib" ] && \ + [ "$CIRCLE_BRANCH" = "main" ] && \ + [ "$CIRCLE_PR_NUMBER" = "" ]; then + export RELEASE_TAG='-t release' + fi + mkdir -p logs + make html O="-T $RELEASE_TAG -j4 -w /tmp/sphinxerrorswarnings.log" + rm -r build/html/_sources + working_directory: doc + - save_cache: + key: sphinx-env-v1-{{ .BuildNum }}-{{ .Environment.CIRCLE_JOB }} + paths: + - doc/build/doctrees + + doc-show-errors-warnings: + steps: + - run: + name: Extract possible build errors and warnings + command: | + (grep "WARNING\|ERROR" /tmp/sphinxerrorswarnings.log || + echo "No errors or warnings") + # Save logs as an artifact, and convert from absolute paths to + # repository-relative paths. + sed "s~$PWD/~~" /tmp/sphinxerrorswarnings.log > \ + doc/logs/sphinx-errors-warnings.log + when: always + - store_artifacts: + path: doc/logs/sphinx-errors-warnings.log + + doc-show-deprecations: + steps: + - run: + name: Extract possible deprecation warnings in examples and tutorials + command: | + (grep -rl DeprecationWarning doc/build/html/gallery || + echo "No deprecation warnings in gallery") + (grep -rl DeprecationWarning doc/build/html/plot_types || + echo "No deprecation warnings in plot_types") + (grep -rl DeprecationWarning doc/build/html/tutorials || + echo "No deprecation warnings in tutorials") + # Save deprecations that are from this absolute directory, and + # convert to repository-relative paths. + (grep -Ero --no-filename "$PWD/.+DeprecationWarning.+$" \ + doc/build/html/{gallery,plot_types,tutorials} || echo) | \ + sed "s~$PWD/~~" > doc/logs/sphinx-deprecations.log + when: always + - store_artifacts: + path: doc/logs/sphinx-deprecations.log + + doc-bundle: + steps: + - run: + name: Bundle sphinx-gallery documentation artifacts + command: > + tar cf doc/build/sphinx-gallery-files.tar.gz + doc/api/_as_gen + doc/gallery + doc/plot_types + doc/tutorials + when: always + - store_artifacts: + path: doc/build/sphinx-gallery-files.tar.gz + + deploy-docs: + steps: + - run: + name: "Deploy new docs" + command: ./.circleci/deploy-docs.sh + + +########################################## +# Here is where the real jobs are defined. +# + +jobs: + docs-python3: + docker: + - image: cimg/python:3.12 + resource_class: large + steps: + - checkout + - check-skip + - merge + + - apt-install + - fonts-install + - pip-install + + - doc-deps-install + - mpl-install + + - doc-build + - doc-show-errors-warnings + - doc-show-deprecations + + - doc-bundle + + - store_artifacts: + path: doc/build/html + - store_test_results: + path: doc/build/test-results + + - add_ssh_keys: + fingerprints: + - "be:c3:c1:d8:fb:a1:0e:37:71:72:d7:a3:40:13:8f:14" + + - deploy-docs + +######################################### +# Defining workflows gets us parallelism. +# + +workflows: + version: 2 + build: + jobs: + # NOTE: If you rename this job, then you must update the `if` condition + # and `circleci-jobs` option in `.github/workflows/circleci.yml`. + - docs-python3 diff --git a/.circleci/deploy-docs.sh b/.circleci/deploy-docs.sh new file mode 100755 index 000000000000..8801d5fd073e --- /dev/null +++ b/.circleci/deploy-docs.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +if [ "$CIRCLE_PROJECT_USERNAME" != "matplotlib" ] || \ + [ "$CIRCLE_BRANCH" != "main" ] || \ + [[ "$CIRCLE_PULL_REQUEST" == https://github.com/matplotlib/matplotlib/pull/* ]]; then + echo "Not uploading docs for ${CIRCLE_SHA1}"\ + "from non-main branch (${CIRCLE_BRANCH})"\ + "or pull request (${CIRCLE_PULL_REQUEST})"\ + "or non-Matplotlib org (${CIRCLE_PROJECT_USERNAME})." + exit +fi + +git clone git@github.com:matplotlib/devdocs.git + +cd devdocs + +git checkout --orphan gh-pages || true +git reset --hard first_commit + +git rm -rf . +cp -R ../doc/build/html/. . +touch .nojekyll + +git config user.email "MatplotlibCircleBot@nomail" +git config user.name "MatplotlibCircleBot" +git config push.default simple + +git add . +git commit -m "Docs build of $CIRCLE_SHA1" + +git push --set-upstream origin gh-pages --force diff --git a/.circleci/fetch_doc_logs.py b/.circleci/fetch_doc_logs.py new file mode 100644 index 000000000000..0a5552a7721c --- /dev/null +++ b/.circleci/fetch_doc_logs.py @@ -0,0 +1,66 @@ +""" +Download artifacts from CircleCI for a documentation build. + +This is run by the :file:`.github/workflows/circleci.yml` workflow in order to +get the warning/deprecation logs that will be posted on commits as checks. Logs +are downloaded from the :file:`docs/logs` artifact path and placed in the +:file:`logs` directory. + +Additionally, the artifact count for a build is produced as a workflow output, +by appending to the file specified by :env:`GITHUB_OUTPUT`. + +If there are no logs, an "ERROR" message is printed, but this is not fatal, as +the initial 'status' workflow runs when the build has first started, and there +are naturally no artifacts at that point. + +This script should be run by passing the CircleCI build URL as its first +argument. In the GitHub Actions workflow, this URL comes from +``github.event.target_url``. +""" +import json +import os +from pathlib import Path +import sys +from urllib.parse import urlparse +from urllib.request import URLError, urlopen + + +if len(sys.argv) != 2: + print('USAGE: fetch_doc_results.py CircleCI-build-url') + sys.exit(1) + +target_url = urlparse(sys.argv[1]) +*_, organization, repository, build_id = target_url.path.split('/') +print(f'Fetching artifacts from {organization}/{repository} for {build_id}') + +artifact_url = ( + f'https://circleci.com/api/v2/project/gh/' + f'{organization}/{repository}/{build_id}/artifacts' +) +print(artifact_url) +try: + with urlopen(artifact_url) as response: + artifacts = json.load(response) +except URLError: + artifacts = {'items': []} +artifact_count = len(artifacts['items']) +print(f'Found {artifact_count} artifacts') + +with open(os.environ['GITHUB_OUTPUT'], 'w+') as fd: + fd.write(f'count={artifact_count}\n') + +logs = Path('logs') +logs.mkdir(exist_ok=True) + +found = False +for item in artifacts['items']: + path = item['path'] + if path.startswith('doc/logs/'): + path = Path(path).name + print(f'Downloading {path} from {item["url"]}') + with urlopen(item['url']) as response: + (logs / path).write_bytes(response.read()) + found = True + +if not found: + print('ERROR: Did not find any artifact logs!') diff --git a/.coveragerc b/.coveragerc index 481486f434ec..f8d90f93e600 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,12 +1,16 @@ [run] -source= +branch = true +source = matplotlib mpl_toolkits -[report] -omit = - lib/matplotlib/tests/* - lib/matplotlib/testing/* - lib/mpl_toolkits/tests/* +omit = matplotlib/_version.py +[report] exclude_lines = + pragma: no cover raise NotImplemented + def __str__ + def __repr__ + if __name__ == .__main__.: + if TYPE_CHECKING: + if typing.TYPE_CHECKING: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..ddec2754d03a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,38 @@ +{ + "hostRequirements": { + "memory": "8gb", + "cpus": 4 + }, + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + "ghcr.io/devcontainers/features/desktop-lite:1": {}, + "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { + "packages": "inkscape,ffmpeg,dvipng,lmodern,cm-super,texlive-latex-base,texlive-latex-extra,texlive-fonts-recommended,texlive-latex-recommended,texlive-pictures,texlive-xetex,fonts-wqy-zenhei,graphviz,fonts-crosextra-carlito,fonts-freefont-otf,fonts-comic-neue,fonts-noto-cjk,optipng" + } + }, + "onCreateCommand": ".devcontainer/setup.sh", + "postCreateCommand": "", + "forwardPorts": [6080], + "portsAttributes": { + "6080": { + "label": "desktop" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "yy0931.mplstyle", + "eamodio.gitlens", + "ms-vscode.live-server" + ], + "settings": {} + }, + "codespaces": { + "openFiles": [ + "README.md", + "doc/devel/codespaces.md" + ] + } + } +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 000000000000..88da5baf69e2 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +"${SHELL}" <(curl -Ls micro.mamba.pm/install.sh) < /dev/null + +conda init --all +micromamba shell init -s bash +micromamba env create -f environment.yml --yes +# Note that `micromamba activate mpl-dev` doesn't work, it must be run by the +# user (same applies to `conda activate`) +echo "envs_dirs: + - /home/codespace/micromamba/envs" > /opt/conda/.condarc diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..611431e707ab --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,17 @@ +# style: end-of-file-fixer pre-commit hook +c1a33a481b9c2df605bcb9bef9c19fe65c3dac21 + +# style: trailing-whitespace pre-commit hook +213061c0804530d04bbbd5c259f10dc8504e5b2b + +# style: check-docstring-first pre-commit hook +046533797725293dfc2a6edb9f536b25f08aa636 + +# chore: fix spelling errors +686c9e5a413e31c46bb049407d5eca285bcab76d + +# chore: pyupgrade --py39-plus +4d306402bb66d6d4c694d8e3e14b91054417070e + +# style: docstring parameter indentation +68daa962de5634753205cba27f21d6edff7be7a2 diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 000000000000..3994ec0a83ea --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true)$ +ref-names: $Format:%D$ diff --git a/.gitattributes b/.gitattributes index 7d11db1dd140..a0c2c8627af7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ * text=auto -lib/matplotlib/_version.py export-subst +*.m diff=objc +*.ppm binary +*.svg binary +*.svg linguist-language=true +.git_archival.txt export-subst diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000000..cb27bbf19d46 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1 @@ +Please refer to the [contributing guide](https://matplotlib.org/devel/index.html). diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000000..a474d51d6f64 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +--- +# These are supported funding model platforms +github: [matplotlib, numfocus] +custom: https://numfocus.org/donate-to-matplotlib diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 98440e26d700..000000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -To help us understand and resolve your issue please check that you have provided -the information below. - -- [ ] Matplotlib version, Python version and Platform (Windows, OSX, Linux ...) -- [ ] How did you install Matplotlib and Python (pip, anaconda, from source ...) -- [ ] If possible please supply a [Short, Self Contained, Correct, Example](http://sscce.org/) - that demonstrates the issue i.e a small piece of code which reproduces the issue - and can be run with out any other (or as few as possible) external dependencies. -- [ ] If this is an image generation bug attach a screenshot demonstrating the issue. -- [ ] If this is a regression (Used to work in an earlier version of Matplotlib), please - note where it used to work. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..f3595d2b7865 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +--- +name: Bug Report +description: Report a bug or issue with Matplotlib. +title: "[Bug]: " +body: + - type: textarea + id: summary + attributes: + label: Bug summary + description: Describe the bug in 1-2 short sentences + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Code for reproduction + description: >- + If possible, please provide a minimum self-contained example. If you + have used generative AI as an aid see + https://matplotlib.org/devdocs/devel/contribute.html#restrictions-on-generative-ai-usage + placeholder: Paste your code here. This field is automatically formatted as Python code. + render: Python + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual outcome + description: >- + Paste the output produced by the code provided above, e.g. + console output, images/videos produced by the code, any relevant screenshots/screencasts, etc. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected outcome + description: Describe (or provide a visual example of) the expected outcome from the code snippet. + validations: + required: true + - type: textarea + id: details + attributes: + label: Additional information + description: | + - What are the conditions under which this bug happens? input parameters, edge cases, etc? + - Has this worked in earlier versions? + - Do you know why this bug is happening? + - Do you maybe even know a fix? + - type: input + id: operating-system + attributes: + label: Operating system + description: Windows, OS/X, Arch, Debian, Ubuntu, etc. + - type: input + id: matplotlib-version + attributes: + label: Matplotlib Version + description: "From Python prompt: `import matplotlib; print(matplotlib.__version__)`" + validations: + required: true + - type: input + id: matplotlib-backend + attributes: + label: Matplotlib Backend + description: "From Python prompt: `import matplotlib; print(matplotlib.get_backend())`" + - type: input + id: python-version + attributes: + label: Python version + description: "In console: `python --version`" + - type: input + id: jupyter-version + attributes: + label: Jupyter version + description: "In console: `jupyter notebook --version` or `jupyter lab --version`" + - type: dropdown + id: install + attributes: + label: Installation + description: How did you install matplotlib? + options: + - pip + - conda + - pixi + - uv + - Linux package manager + - from source (.tar.gz) + - git checkout diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..dc80f6d7c91d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +# Reference: +# https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +--- +blank_issues_enabled: true # default +contact_links: + - name: Question/Support/Other + url: https://discourse.matplotlib.org + about: If you have a usage question + - name: Chat with devs + url: https://gitter.im/matplotlib/matplotlib + about: Ask short questions about contributing to Matplotlib diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 000000000000..5f7a0d6c7176 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,33 @@ +--- +name: Documentation +description: Create a report to help us improve the documentation +title: "[Doc]: " +labels: [Documentation] +body: + - type: input + id: link + attributes: + label: Documentation Link + description: >- + Link to any documentation or examples that you are referencing. Suggested improvements should be based + on [the development version of the docs](https://matplotlib.org/devdocs/) + placeholder: https://matplotlib.org/devdocs/... + - type: textarea + id: problem + attributes: + label: Problem + description: What is missing, unclear, or wrong in the documentation? + placeholder: | + * I found [...] to be unclear because [...] + * [...] made me think that [...] when really it should be [...] + * There is no example showing how to do [...] + validations: + required: true + - type: textarea + id: improvement + attributes: + label: Suggested improvement + placeholder: | + * This line should be be changed to say [...] + * Include a paragraph explaining [...] + * Add a figure showing [...] diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000000..e174fb8994aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,27 @@ +--- +name: Feature Request +description: Suggest something to add to Matplotlib! +title: "[ENH]: " +labels: [New feature] +body: + - type: markdown + attributes: + value: >- + Please search the [issues](https://github.com/matplotlib/matplotlib/issues) for relevant feature + requests before creating a new feature request. + - type: textarea + id: problem + attributes: + label: Problem + description: Briefly describe the problem this feature will solve. (2-4 sentences) + placeholder: | + * I'm always frustrated when [...] because [...] + * I would like it if [...] happened when I [...] because [...] + * Here is a sample image of what I am asking for [...] + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: Describe a way to accomplish the goals of this feature request. diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml new file mode 100644 index 000000000000..6ebb64c0c3e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/maintenance.yml @@ -0,0 +1,18 @@ +--- +name: Maintenance +description: Help improve performance, usability and/or consistency. +title: "[MNT]: " +labels: [Maintenance] +body: + - type: textarea + id: summary + attributes: + label: Summary + description: Please provide 1-2 short sentences that succinctly describes what could be improved. + validations: + required: true + - type: textarea + id: fix + attributes: + label: Proposed fix + description: Please describe how you think this could be improved. diff --git a/.github/ISSUE_TEMPLATE/tag_proposal.yml b/.github/ISSUE_TEMPLATE/tag_proposal.yml new file mode 100644 index 000000000000..2bb856d26be6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tag_proposal.yml @@ -0,0 +1,28 @@ +--- +name: Tag Proposal +description: Suggest a new tag or subcategory for the gallery of examples +title: "[Tag]: " +labels: ["Documentation: tags"] +body: + - type: markdown + attributes: + value: >- + Please search the [tag glossary]() for relevant tags before creating a new tag proposal. + - type: textarea + id: need + attributes: + label: Need + description: Briefly describe the need this tag will fill. (1-4 sentences) + placeholder: | + * A tag is needed for examples that share [...] + * Existing tags do not work because [...] + * Current gallery examples that would use this tag include [...] + * Indicate which subcategory this tag falls under, or whether a new subcategory is proposed. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: >- + What should the tag be? All tags are in the format `subcategory: tag` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..bf483dbdd4f4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ + + +## PR summary + + + +## PR checklist + + +- [ ] "closes #0000" is in the body of the PR description to [link the related issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) +- [ ] new and changed code is [tested](https://matplotlib.org/devdocs/devel/testing.html) +- [ ] *Plotting related* features are demonstrated in an [example](https://matplotlib.org/devdocs/devel/document.html#write-examples-and-tutorials) +- [ ] *New Features* and *API Changes* are noted with a [directive and release note](https://matplotlib.org/devdocs/devel/api_changes.html#announce-changes-deprecations-and-new-features) +- [ ] Documentation complies with [general](https://matplotlib.org/devdocs/devel/document.html#write-rest-pages) and [docstring](https://matplotlib.org/devdocs/devel/document.html#write-docstrings) guidelines + + diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 000000000000..00e7612bd1e6 --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,33 @@ +# codecov used to be able to find this anywhere, now we have to manually +# tell it where to look +--- +comment: false + +codecov: + notify: + require_ci_to_pass: false + +coverage: + status: + patch: + default: + target: 50% + if_no_uploads: error + if_not_found: success + if_ci_failed: error + project: + default: false + library: + target: 50% + if_no_uploads: error + if_not_found: success + if_ci_failed: error + paths: + - '!lib/.*/tests/.*' + tests: + target: auto + if_no_uploads: error + if_not_found: success + if_ci_failed: error + paths: + - 'lib/.*/tests/.*' diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..34902e5236df --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + actions: + patterns: + - "*" diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000000..75adfed57f43 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,282 @@ +--- +"CI: Run cibuildwheel": + - changed-files: + - any-glob-to-any-file: ['.github/workflows/cibuildwheel.yml'] +"CI: Run cygwin": + - changed-files: + - any-glob-to-any-file: ['.github/workflows/cygwin.yml'] + +"backend: agg": + - changed-files: + - any-glob-to-any-file: + - 'extern/agg24-svn/' + - 'lib/matplotlib/backends/_backend_agg.pyi' + - 'lib/matplotlib/backends/backend_agg.py*' + - 'src/_backend_agg*' +"backend: cairo": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_*cairo.py*' +"backend: pdf": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_pdf_ps.py' + - 'lib/matplotlib/backends/backend_pdf.py' +"backend: pgf": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_pgf.py' +"backend: ps": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_pdf_ps.py' + - 'lib/matplotlib/backends/backend_ps.py' +"backend: svg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_svg.py' + +"GUI: gtk": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/_backend_gtk.py*' + - 'lib/matplotlib/backends/backend_gtk*' +"GUI: MacOSX": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_macosx.py*' + - 'src/_macosx.m' +"GUI: nbagg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_nbagg*.py*' + - 'lib/matplotlib/backends/web_backend/js/nbagg_mpl.js' +"GUI: Qt": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_qt*' + - 'lib/matplotlib/backends/qt_compat.py' + - 'lib/matplotlib/backends/qt_editor/**' +"GUI: tk": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*backend_tk*' + - 'lib/matplotlib/backends/_tkagg.pyi' + - 'src/_tkagg.cpp' + - 'src/_tkmini.h' +"GUI: webagg": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/*_webagg*.py*' + - 'lib/matplotlib/backends/web_backend/**' +"GUI: wx": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backends/backend_wx*.py*' + +"Documentation: API": + - all: + - changed-files: + - any-glob-to-any-file: + # Also files in lib/**, but we can't be sure those are only documentation. + - 'doc/api/**' + - all-globs-to-all-files: + - '!doc/api/next_api_changes/**' + +"Documentation: build": + - changed-files: + - any-glob-to-any-file: + - 'doc/conf.py' + - 'doc/Makefile' + - 'doc/make.bat' + - 'doc/sphinxext/**' +"Documentation: devdocs": + - changed-files: + - any-glob-to-any-file: + - 'doc/devel/**' +"Documentation: examples": + - changed-files: + - any-glob-to-any-file: + - 'galleries/examples/**' +"Documentation: plot types": + - changed-files: + - any-glob-to-any-file: + - 'galleries/plot_types/**' +"Documentation: tutorials": + - changed-files: + - any-glob-to-any-file: + - 'galleries/tutorials/**' +"Documentation: user guide": + - all: + - changed-files: + - any-glob-to-any-file: + - 'doc/users/**' + - 'galleries/users_explain/**' + - all-globs-to-all-files: + - '!doc/users/next_whats_new/**' + +"topic: animation": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/animation.py*' + - 'lib/matplotlib/_animation_data.py*' +"topic: axes": + - changed-files: + - any-glob-to-any-file: + # Note, axes.py is not included here because it contains many plotting + # methods, for which changes would not be considered on topic. + - 'lib/matplotlib/axes/_base.py*' +"topic: canvas and figure manager": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backend_bases.py*' +"topic: categorical": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/category.py*' +"topic: collections and mappables": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/collections.py*' +"topic: color/color & colormaps": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/colorbar.py*' + - 'lib/matplotlib/colors.py*' + - 'lib/matplotlib/_color_data.py*' + - 'lib/matplotlib/cm.py*' + - 'lib/matplotlib/_cm.py*' + - 'lib/matplotlib/_cm_listed.py*' +"topic: contour": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/contour.py*' + - 'src/_qhull_wrapper.cpp' +"topic: date handling": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/dates.py*' +"topic: figures and subfigures": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/figure.py*' +"topic: geometry manager": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/_constrained_layout.py*' + - 'lib/matplotlib/_layoutgrid.py*' + - 'lib/matplotlib/_tight_bbox.py*' + - 'lib/matplotlib/_tight_layout.py*' + - 'lib/matplotlib/gridspec.py*' + - 'lib/matplotlib/layout_engine.py*' +"topic: hatch": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/hatch.py*' +"topic: images": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/image.py*' + - 'lib/matplotlib/_image.pyi' + - 'src/_image_*' +"topic: legend": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/legend.py*' + - 'lib/matplotlib/legend_handler.py*' +"topic: markers": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/markers.py*' +"topic: mpl_toolkit": + - all: + - changed-files: + - any-glob-to-any-file: + - 'lib/mpl_toolkits/**' + - all-globs-to-all-files: + - '!lib/mpl_toolkits/mplot3d/**' +"topic: mplot3d": + - changed-files: + - any-glob-to-any-file: + - 'lib/mpl_toolkits/mplot3d/**' +"topic: path handling": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/path.py*' + - 'lib/matplotlib/patheffects.py*' + - 'lib/matplotlib/_path.pyi' + - 'src/*path*' +"topic: polar": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/projections/polar.py*' +"topic: pyplot API": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/pyplot.py' + - 'lib/matplotlib/_pylab_helpers.py*' +"topic: rcparams": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/rcsetup.py*' +"topic: sankey": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/sankey.py*' +"topic: sphinx extension": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/sphinxext/**' +"topic: styles": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/mpl-data/stylelib/**' + - 'lib/matplotlib/style/**' +"topic: table": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/table.py*' +"topic: text": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/text.py*' + - 'lib/matplotlib/textpath.py*' +"topic: text/fonts": + - changed-files: + - any-glob-to-any-file: + - 'src/checkdep_freetype2.c' + - 'src/ft2font*' +"topic: text/mathtext": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/mathtext.py*' + - 'lib/matplotlib/_mathtext.py*' + - 'lib/matplotlib/_mathtext_data.py*' +"topic: ticks axis labels": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/axis.py*' + - 'lib/matplotlib/ticker.py*' +"topic: toolbar": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/backend_managers.py*' + - 'lib/matplotlib/backend_tools.py*' +"topic: transforms and scales": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/scale.py*' + - 'lib/matplotlib/transforms.py*' +"topic: tri": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/tri/**' + - 'src/tri/**' +"topic: units and array ducktypes": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/units.py*' +"topic: widgets/UI": + - changed-files: + - any-glob-to-any-file: + - 'lib/matplotlib/widgets.py*' diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml new file mode 100644 index 000000000000..fd8dc309a61c --- /dev/null +++ b/.github/workflows/cibuildwheel.yml @@ -0,0 +1,194 @@ +--- +name: Build CI wheels + +on: + # Save CI by only running this on release branches or tags. + push: + branches: + - main + - v[0-9]+.[0-9]+.x + tags: + - v* + # Also allow running this action on PRs if requested by applying the + # "Run cibuildwheel" label. + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + +permissions: + contents: read + +jobs: + build_sdist: + if: >- + github.repository == 'matplotlib/matplotlib' && ( + github.event_name == 'push' || + github.event_name == 'pull_request' && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cibuildwheel' + ) || + contains(github.event.pull_request.labels.*.name, + 'CI: Run cibuildwheel') + ) + ) + name: Build sdist + runs-on: ubuntu-latest + outputs: + SDIST_NAME: ${{ steps.sdist.outputs.SDIST_NAME }} + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + name: Install Python + with: + python-version: '3.11' + + # Something changed somewhere that prevents the downloaded-at-build-time + # licenses from being included in built wheels, so pre-download them so + # that they exist before the build and are included. + - name: Pre-download bundled licenses + run: > + curl -Lo LICENSE/LICENSE_QHULL + https://github.com/qhull/qhull/raw/2020.2/COPYING.txt + + - name: Install dependencies + run: python -m pip install build twine + + - name: Build sdist + id: sdist + run: | + python -m build --sdist + python ci/export_sdist_name.py + + - name: Check README rendering for PyPI + run: twine check dist/* + + - name: Upload sdist result + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: cibw-sdist + path: dist/*.tar.gz + if-no-files-found: error + + build_wheels: + if: >- + github.repository == 'matplotlib/matplotlib' && ( + github.event_name == 'push' || + github.event_name == 'pull_request' && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cibuildwheel' + ) || + contains(github.event.pull_request.labels.*.name, + 'CI: Run cibuildwheel') + ) + ) + needs: build_sdist + name: Build wheels on ${{ matrix.os }} for ${{ matrix.cibw_archs }} + runs-on: ${{ matrix.os }} + env: + CIBW_BEFORE_BUILD: >- + rm -rf {package}/build + CIBW_BEFORE_BUILD_WINDOWS: >- + pip install delvewheel && + rm -rf {package}/build + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >- + delvewheel repair -w {dest_dir} {wheel} + CIBW_AFTER_BUILD: >- + twine check {wheel} && + python {package}/ci/check_wheel_licenses.py {wheel} + # On Windows, we explicitly request MSVC compilers (as GitHub Action runners have + # MinGW on PATH that would be picked otherwise), switch to a static build for + # runtimes, but use dynamic linking for `VCRUNTIME140.dll`, `VCRUNTIME140_1.dll`, + # and the UCRT. This avoids requiring specific versions of `MSVCP140.dll`, while + # keeping shared state with the rest of the Python process/extensions. + CIBW_CONFIG_SETTINGS_WINDOWS: >- + setup-args="--vsenv" + setup-args="-Db_vscrt=mt" + setup-args="-Dcpp_link_args=['ucrt.lib','vcruntime.lib','/nodefaultlib:libucrt.lib','/nodefaultlib:libvcruntime.lib']" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_SKIP: "*-musllinux_aarch64" + CIBW_TEST_COMMAND: >- + python {package}/ci/check_version_number.py + MACOSX_DEPLOYMENT_TARGET: "10.12" + strategy: + matrix: + include: + - os: ubuntu-latest + cibw_archs: "x86_64" + - os: ubuntu-24.04-arm + cibw_archs: "aarch64" + - os: windows-latest + cibw_archs: "AMD64" + - os: windows-11-arm + cibw_archs: "ARM64" + - os: macos-15-intel + cibw_archs: "x86_64" + - os: macos-14 + cibw_archs: "arm64" + + steps: + - name: Download sdist + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: cibw-sdist + path: dist/ + + - name: Build wheels for CPython 3.14 + uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp314-* cp314t-*" + CIBW_ENABLE: "cpython-freethreading cpython-prerelease" + CIBW_ARCHS: ${{ matrix.cibw_archs }} + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + + - name: Build wheels for CPython 3.13 + uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp313-* cp313t-*" + CIBW_ENABLE: cpython-freethreading + CIBW_ARCHS: ${{ matrix.cibw_archs }} + + - name: Build wheels for CPython 3.12 + uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp312-*" + CIBW_ARCHS: ${{ matrix.cibw_archs }} + + - name: Build wheels for CPython 3.11 + uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "cp311-*" + CIBW_ARCHS: ${{ matrix.cibw_archs }} + + - name: Build wheels for PyPy + uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1 + with: + package-dir: dist/${{ needs.build_sdist.outputs.SDIST_NAME }} + env: + CIBW_BUILD: "pp311-*" + CIBW_ARCHS: ${{ matrix.cibw_archs }} + CIBW_ENABLE: pypy + if: matrix.cibw_archs != 'aarch64' && matrix.os != 'windows-latest' && matrix.os != 'windows-11-arm' + + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: cibw-wheels-${{ runner.os }}-${{ matrix.cibw_archs }} + path: ./wheelhouse/*.whl + if-no-files-found: error diff --git a/.github/workflows/circleci.yml b/.github/workflows/circleci.yml new file mode 100644 index 000000000000..05a30eb032cb --- /dev/null +++ b/.github/workflows/circleci.yml @@ -0,0 +1,75 @@ +--- +name: "CircleCI artifact handling" +on: [status] +jobs: + circleci_artifacts_redirector_job: + if: "${{ github.event.context == 'ci/circleci: docs-python3' }}" + permissions: + statuses: write + runs-on: ubuntu-latest + name: Run CircleCI artifacts redirector + steps: + - name: GitHub Action step + uses: + scientific-python/circleci-artifacts-redirector-action@5d358ff96e96429a5c64a969bb4a574555439f4f # v1.3.1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + api-token: ${{ secrets.CIRCLECI_TOKEN }} + artifact-path: 0/doc/build/html/index.html + circleci-jobs: docs-python3 + job-title: View the built docs + + post_warnings_as_review: + if: "${{ github.event.context == 'ci/circleci: docs-python3' }}" + permissions: + contents: read + checks: write + pull-requests: write + runs-on: ubuntu-latest + name: Post warnings/errors as review + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Fetch result artifacts + id: fetch-artifacts + env: + target_url: "${{ github.event.target_url }}" + run: | + python .circleci/fetch_doc_logs.py "${target_url}" + + - name: Set up reviewdog + if: "${{ steps.fetch-artifacts.outputs.count != 0 }}" + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + with: + reviewdog_version: latest + + - name: Post review + if: "${{ steps.fetch-artifacts.outputs.count != 0 }}" + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REVIEWDOG_SKIP_DOGHOUSE: "true" + CI_COMMIT: ${{ github.event.sha }} + CI_REPO_OWNER: ${{ github.event.repository.owner.login }} + CI_REPO_NAME: ${{ github.event.repository.name }} + run: | + # The 'status' event does not contain information in the way that + # reviewdog expects, so we unset those so it reads from the + # environment variables we set above. + unset GITHUB_ACTIONS GITHUB_EVENT_PATH + cat logs/sphinx-errors-warnings.log | \ + reviewdog \ + -efm '%f\:%l: %tEBUG: %m' \ + -efm '%f\:%l: %tNFO: %m' \ + -efm '%f\:%l: %tARNING: %m' \ + -efm '%f\:%l: %tRROR: %m' \ + -efm '%f\:%l: %tEVERE: %m' \ + -efm '%f\:%s: %tARNING: %m' \ + -efm '%f\:%s: %tRROR: %m' \ + -name=sphinx -tee -fail-on-error=false \ + -reporter=github-check -filter-mode=nofilter + cat logs/sphinx-deprecations.log | \ + reviewdog \ + -efm '%f\:%l: %m' \ + -name=examples -tee -reporter=github-check -filter-mode=nofilter diff --git a/.github/workflows/clean_pr.yml b/.github/workflows/clean_pr.yml new file mode 100644 index 000000000000..5107bb92fa1c --- /dev/null +++ b/.github/workflows/clean_pr.yml @@ -0,0 +1,54 @@ +--- +name: PR cleanliness +on: [pull_request] + +permissions: + contents: read + +jobs: + pr_clean: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: '0' + persist-credentials: false + - name: Check for added-and-deleted files + run: | + git fetch --quiet origin "$GITHUB_BASE_REF" + base="$(git merge-base "origin/$GITHUB_BASE_REF" 'HEAD^2')" + ad="$(git log "$base..HEAD^2" --pretty=tformat: --name-status --diff-filter=AD | + cut --fields 2 | sort | uniq --repeated)" + if [[ -n "$ad" ]]; then + printf 'The following files were both added and deleted in this PR:\n%s\n' "$ad" + exit 1 + fi + - name: Check for added-and-modified images + run: | + git fetch --quiet origin "$GITHUB_BASE_REF" + base="$(git merge-base "origin/$GITHUB_BASE_REF" 'HEAD^2')" + am="$(git log "$base..HEAD^2" --pretty=tformat: --name-status --diff-filter=AM | + cut --fields 2 | sort | uniq --repeated | + grep -E '\.(png|pdf|ps|eps|svg)' || true)" + if [[ -n "$am" ]]; then + printf 'The following images were both added and modified in this PR:\n%s\n' "$am" + exit 1 + fi + - name: Check for invalid backports to -doc branches + if: endsWith(github.base_ref, '-doc') + run: | + git fetch --quiet origin "$GITHUB_BASE_REF" + base="$(git merge-base "origin/$GITHUB_BASE_REF" 'HEAD^2')" + lib="$(git log "$base..HEAD^2" --pretty=tformat: --name-status -- lib src | + cut --fields 2 | sort || true)" + if [[ -n "$lib" ]]; then + printf 'Changes to the following files have no effect and should not be backported:\n%s\n' "$lib" + exit 1 + fi + - name: Check for branches opened against main + if: github.ref_name == 'main' + run: | + echo 'PR branch should not be main.' + echo 'See https://matplotlib.org/devdocs/devel/development_workflow.html#make-a-new-feature-branch' + exit 1 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000000..dbc336d70002 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,46 @@ +--- +name: "CodeQL" + +on: + push: + branches: [main, v*.x] + pull_request: + # The branches below must be a subset of the branches above + branches: [main] + schedule: + - cron: '45 19 * * 1' + +jobs: + analyze: + if: github.repository == 'matplotlib/matplotlib' + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['c-cpp', 'javascript', 'python'] + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 + with: + languages: ${{ matrix.language }} + + - name: Build compiled code + if: matrix.language == 'c-cpp' + run: | + pip install --user --upgrade pip + pip install --user -v . + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 diff --git a/.github/workflows/conflictcheck.yml b/.github/workflows/conflictcheck.yml new file mode 100644 index 000000000000..f4a687cd28d7 --- /dev/null +++ b/.github/workflows/conflictcheck.yml @@ -0,0 +1,24 @@ +--- +name: "Maintenance" +on: + # So that PRs touching the same files as the push are updated + push: + # So that the `dirtyLabel` is removed if conflicts are resolve + # We recommend `pull_request_target` so that github secrets are available. + # In `pull_request` we wouldn't be able to change labels of fork PRs + pull_request_target: + types: [synchronize] + +jobs: + main: + if: github.repository == 'matplotlib/matplotlib' + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Check if PRs have merge conflicts + uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 + with: + dirtyLabel: "status: needs rebase" + repoToken: "${{ secrets.GITHUB_TOKEN }}" + retryMax: 10 diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml new file mode 100644 index 000000000000..b99fa5a1d5d8 --- /dev/null +++ b/.github/workflows/cygwin.yml @@ -0,0 +1,250 @@ +--- +name: Cygwin Tests +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} + cancel-in-progress: true + +on: + push: + branches: + - main + - v[0-9]+.[0-9]+.[0-9x]+ + tags: + - v* + paths: + - 'src/**' + - '.github/workflows/cygwin.yml' + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + branches-ignore: + - v[0-9]+.[0-9]+.[0-9x]+-doc + paths: + - 'src/**' + - '.github/workflows/cygwin.yml' + schedule: + # 5:47 UTC on Saturdays + - cron: "47 5 * * 6" + workflow_dispatch: + +permissions: + contents: read + +env: + NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. + OPENBLAS_NUM_THREADS: 1 + PYTHONFAULTHANDLER: 1 + SHELLOPTS: igncr + CYGWIN_NOWINPATH: 1 + CHERE_INVOKING: 1 + TMP: /tmp + TEMP: /tmp + +jobs: + + test-cygwin: + runs-on: windows-latest + name: Python 3.${{ matrix.python-minor-version }} on Cygwin + # Enable these when Cygwin has Python 3.12. + if: >- + github.event_name == 'workflow_dispatch' || + (false && github.event_name == 'schedule') || + ( + false && + github.repository == 'matplotlib/matplotlib' && + !contains(github.event.head_commit.message, '[ci skip]') && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip github]') && + !contains(github.event.head_commit.message, '[ci doc]') && + ( + github.event_name == 'push' || + github.event_name == 'pull_request' && + ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'CI: Run cygwin' + ) || + contains(github.event.pull_request.labels.*.name, 'CI: Run cygwin') + ) + ) + ) + strategy: + matrix: + python-minor-version: [12] + + steps: + - name: Fix line endings + run: git config --global core.autocrlf input + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 + with: + packages: >- + ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel + libgeos-devel libQt5Core-devel pkgconf libglib2.0-devel ninja + noto-cjk-fonts + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-pip + python3${{ matrix.python-minor-version }}-wheel + python3${{ matrix.python-minor-version }}-setuptools + python3${{ matrix.python-minor-version }}-cycler + python3${{ matrix.python-minor-version }}-dateutil + python3${{ matrix.python-minor-version }}-fonttools + python3${{ matrix.python-minor-version }}-imaging + python3${{ matrix.python-minor-version }}-kiwisolver + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-packaging + python3${{ matrix.python-minor-version }}-pyparsing + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-sphinx + python-cairo-devel + python3${{ matrix.python-minor-version }}-cairo + python3${{ matrix.python-minor-version }}-gi + python3${{ matrix.python-minor-version }}-matplotlib + xorg-server-extra libxcb-icccm4 libxcb-image0 + libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 + libxcb-xinerama0 + make autoconf autoconf2.5 automake automake1.10 libtool m4 + libqhull-devel libfreetype-devel + libjpeg-devel libwebp-devel + + - name: Set runner username to root and id to 0 + shell: bash.exe -eo pipefail -o igncr "{0}" + # GitHub Actions runs everything as Administrator. I don't + # know how to test for this, so set the uid for the CI job so + # that the existing unix root detection will work. + run: /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd + + - name: Mark test repo safe + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + git.exe config --global --add safe.directory /proc/cygdrive/d/a/matplotlib/matplotlib + git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + C:/cygwin/bin/git.exe config --global --add safe.directory D:/a/matplotlib/matplotlib + /usr/bin/git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + + - name: Use dash for /bin/sh + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + /bin/rm -f /bin/sh.exe || exit 1 + cp -sf /bin/dash.exe /bin/sh.exe || exit 1 + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + # FreeType build fails with bash, succeeds with dash + + - name: Cache pip + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + with: + path: C:\cygwin\home\runneradmin\.cache\pip + key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + restore-keys: ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- + + - name: Cache ccache + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + with: + path: C:\cygwin\home\runneradmin\.ccache + key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} + restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- + + - name: Cache Matplotlib + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + with: + path: | + C:\cygwin\home\runneradmin\.cache\matplotlib + !C:\cygwin\home\runneradmin\.cache\matplotlib\tex.cache + !C:\cygwin\home\runneradmin\.cache\matplotlib\test_cache + key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }} + restore-keys: | + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}- + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl- + + - name: Ensure correct Python version + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} + /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} + + - name: Install Python dependencies + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install kiwisolver 'numpy>=1.22,<1.26' pillow importlib_resources + grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt + python -m pip install meson-python pybind11 + export PATH="/usr/local/bin:$PATH" + python -m pip install --no-build-isolation 'contourpy>=1.0.1' + python -m pip install --upgrade cycler fonttools \ + packaging pyparsing python-dateutil setuptools-scm \ + -r requirements_test.txt sphinx ipython + python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && + python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && + echo 'PyGObject is available' || + echo 'PyGObject is not available' + python -m pip install --upgrade pyqt5 && + python -c 'import PyQt5.QtCore' && + echo 'PyQt5 is available' || + echo 'PyQt5 is not available' + python -mpip install --upgrade pyside2 && + python -c 'import PySide2.QtCore' && + echo 'PySide2 is available' || + echo 'PySide2 is not available' + python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled' + + - name: Install Matplotlib + shell: bash.exe -eo pipefail -o igncr "{0}" + env: + AUTOCONF: /usr/bin/autoconf-2.69 + MAKEFLAGS: dw + run: | + export PATH="/usr/local/bin:$PATH" + ccache -s + git describe + # All dependencies must have been pre-installed, so that the minver + # constraints are held. + python -m pip install --no-deps --no-build-isolation --verbose \ + --config-settings=setup-args="-DrcParams-backend=Agg" \ + --editable .[dev] + + - name: Find DLLs to rebase + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . \ + -name \*.exe -o -name \*.dll -print >files_to_rebase.txt + + - name: Rebase DLL list + shell: ash.exe "{0}" + run: "rebase --database --filelist=files_to_rebase.txt" + # Inplace modification of DLLs to assign non-overlapping load + # addresses so fork() works as expected. Ash is used as it + # does not link against any Cygwin DLLs that might need to be + # rebased. + + - name: Check that Matplotlib imports + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/bin/python -c "import matplotlib as mpl; import matplotlib.pyplot as plt" + + - name: Set ffmpeg path + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())") + echo "${oldmplrc}" + mkdir -p ~/.matplotlib/ + sed -E \ + -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' \ + "${oldmplrc}" >~/.matplotlib/matplotlibrc + + - name: Run pytest + shell: bash.exe -eo pipefail -o igncr "{0}" + id: cygwin-run-pytest + run: | + xvfb-run pytest-3.${{ matrix.python-minor-version }} -rfEsXR -n auto \ + --maxfail=50 --timeout=300 --durations=25 \ + --cov-report=term --cov=lib --log-level=DEBUG --color=yes diff --git a/.github/workflows/do_not_merge.yml b/.github/workflows/do_not_merge.yml new file mode 100644 index 000000000000..0c263623942b --- /dev/null +++ b/.github/workflows/do_not_merge.yml @@ -0,0 +1,31 @@ +--- +name: Do Not Merge + +# action to block merging on specific labels +on: + pull_request: + types: [synchronize, opened, reopened, labeled, unlabeled] + +permissions: {} + +jobs: + do-not-merge: + name: Prevent Merging + runs-on: ubuntu-latest + env: + has_tag: >- + ${{contains(github.event.pull_request.labels.*.name, 'status: needs comment/discussion') || + contains(github.event.pull_request.labels.*.name, 'status: waiting for other PR') || + contains(github.event.pull_request.labels.*.name, 'DO NOT MERGE') }} + steps: + - name: Check for label + if: ${{'true' == env.has_tag}} + run: | + echo "This PR cannot be merged because it has one of the following labels: " + echo "* status: needs comment/discussion" + echo "* status: waiting for other PR" + echo "* DO NOT MERGE" + exit 1 + - name: Allow merging + if: ${{'false' == env.has_tag}} + run: exit 0 diff --git a/.github/workflows/good-first-issue.yml b/.github/workflows/good-first-issue.yml new file mode 100644 index 000000000000..5bb69db0595c --- /dev/null +++ b/.github/workflows/good-first-issue.yml @@ -0,0 +1,30 @@ +--- +name: Add comment on good first issues +on: + issues: + types: + - labeled +jobs: + add-comment: + if: github.event.label.name == 'Good first issue' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Add comment + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + with: + issue-number: ${{ github.event.issue.number }} + body: | + ### Good first issue - notes for new contributors + + This issue is suited to new contributors because it does not require understanding of the + Matplotlib internals. To get started, please see our [contributing + guide](https://matplotlib.org/stable/devel/index). + + **We do not assign issues**. Check the *Development* section in the sidebar for linked pull + requests (PRs). If there are none, feel free to start working on it. If there is an open PR, please + collaborate on the work by reviewing it rather than duplicating it in a competing PR. + + If something is unclear, please reach out on any of our [communication + channels](https://matplotlib.org/stable/devel/contributing.html#get-connected). diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000000..17c4922df054 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,15 @@ +--- +name: "Pull Request Labeler" +on: + - pull_request_target + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + with: + sync-labels: true diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 000000000000..bcc7d406043a --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,100 @@ +--- +name: Linting +on: [pull_request] + +permissions: + contents: read + +jobs: + pre-commit: + name: precommit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.x" + - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: --hook-stage manual --all-files + + ruff: + name: ruff + runs-on: ubuntu-latest + permissions: + checks: write + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Set up Python 3 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Install ruff + run: pip3 install ruff + + - name: Set up reviewdog + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + + - name: Run ruff + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + ruff check --output-format rdjson | \ + reviewdog -f=rdjson \ + -tee -reporter=github-check -filter-mode nofilter + mypy: + name: mypy + runs-on: ubuntu-latest + permissions: + checks: write + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Set up Python 3 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Install mypy + run: pip3 install -r requirements/testing/mypy.txt -r requirements/testing/all.txt + + - name: Set up reviewdog + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + + - name: Run mypy + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + mypy --config pyproject.toml | \ + reviewdog -f=mypy -name=mypy \ + -tee -reporter=github-check -filter-mode nofilter + + + eslint: + name: eslint + runs-on: ubuntu-latest + permissions: + checks: write + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: eslint + uses: reviewdog/action-eslint@556a3fdaf8b4201d4d74d406013386aa4f7dab96 # v1.34.0 + with: + filter_mode: nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-check + workdir: 'lib/matplotlib/backends/web_backend/' diff --git a/.github/workflows/mypy-stubtest.yml b/.github/workflows/mypy-stubtest.yml new file mode 100644 index 000000000000..dad980b4fae9 --- /dev/null +++ b/.github/workflows/mypy-stubtest.yml @@ -0,0 +1,47 @@ +--- +name: Mypy Stubtest +on: [pull_request] + +permissions: + contents: read + +jobs: + mypy-stubtest: + name: mypy-stubtest + runs-on: ubuntu-latest + permissions: + checks: write + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Set up Python 3 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: '3.11' + + - name: Set up reviewdog + uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0 + + - name: Install tox + run: python -m pip install tox + + - name: Run mypy stubtest + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -o pipefail + tox -e stubtest | \ + sed -e "s!.tox/stubtest/lib/python3.11/site-packages!lib!g" | \ + reviewdog \ + -efm '%Eerror: %m' \ + -efm '%CStub: in file %f:%l' \ + -efm '%CStub: in file %f' \ + -efm '%+CRuntime:%.%#' \ + -efm '%+CMISSING' \ + -efm '%+Cdef %.%#' \ + -efm '%+C<%.%#>' \ + -efm '%Z' \ + -reporter=github-check -tee -name=mypy-stubtest \ + -filter-mode=nofilter diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml new file mode 100644 index 000000000000..e8ff8ad7acbc --- /dev/null +++ b/.github/workflows/nightlies.yml @@ -0,0 +1,65 @@ +--- +name: Upload nightly wheels to Anaconda Cloud + +on: + # Run daily at 1:23 UTC to upload nightly wheels to Anaconda Cloud + schedule: + - cron: '23 1 * * *' + # Run on demand with workflow dispatch + workflow_dispatch: + +permissions: + actions: read + +jobs: + upload_nightly_wheels: + name: Upload nightly wheels to Anaconda Cloud + runs-on: ubuntu-latest + defaults: + run: + # The login shell is necessary for the setup-micromamba setup + # to work in subsequent jobs. + # https://github.com/mamba-org/setup-micromamba#about-login-shells + shell: bash -e -l {0} + if: github.repository_owner == 'matplotlib' + + steps: + # https://github.com/actions/download-artifact/issues/3#issuecomment-1017141067 + - name: Download wheel artifacts from last build on 'main' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PROJECT_REPO="matplotlib/matplotlib" + BRANCH="main" + WORKFLOW_NAME="cibuildwheel.yml" + ARTIFACT_PATTERN="cibw-wheels-*" + + gh run --repo "${PROJECT_REPO}" \ + list --branch "${BRANCH}" \ + --workflow "${WORKFLOW_NAME}" \ + --json event,status,conclusion,databaseId > runs.json + RUN_ID=$( + jq --compact-output \ + '[ + .[] | + # Filter on "push" events to main (merged PRs) ... + select(.event == "push") | + # that have completed successfully ... + select(.status == "completed" and .conclusion == "success") + ] | + # and get ID of latest build of wheels. + sort_by(.databaseId) | reverse | .[0].databaseId' runs.json + ) + gh run --repo "${PROJECT_REPO}" view "${RUN_ID}" + gh run --repo "${PROJECT_REPO}" \ + download "${RUN_ID}" --pattern "${ARTIFACT_PATTERN}" + + mkdir dist + mv ${ARTIFACT_PATTERN}/*.whl dist/ + ls -l dist/ + + - name: Upload wheels to Anaconda Cloud as nightlies + uses: scientific-python/upload-nightly-action@5748273c71e2d8d3a61f3a11a16421c8954f9ecf # 0.6.3 + with: + artifacts_path: dist + anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }} diff --git a/.github/workflows/pr_welcome.yml b/.github/workflows/pr_welcome.yml new file mode 100644 index 000000000000..2580b5a008a0 --- /dev/null +++ b/.github/workflows/pr_welcome.yml @@ -0,0 +1,37 @@ +--- +name: PR Greetings + +on: [pull_request_target] + +jobs: + greeting: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/first-interaction@1c4688942c71f71d4f5502a26ea67c331730fa4d # v3.1.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + pr_message: >+ + Thank you for opening your first PR into Matplotlib! + + + If you have not heard from us in a week or so, please leave a new + comment below and that should bring it to our attention. + Most of our reviewers are volunteers and sometimes things fall + through the cracks. + + + You can also join us [on + gitter](https://gitter.im/matplotlib/matplotlib) for real-time + discussion. + + + For details on testing, writing docs, and our review process, + please see [the developer + guide](https://matplotlib.org/devdocs/devel/index.html) + + + We strive to be a welcoming and open project. Please follow our + [Code of + Conduct](https://github.com/matplotlib/matplotlib/blob/main/CODE_OF_CONDUCT.md). diff --git a/.github/workflows/stale-tidy.yml b/.github/workflows/stale-tidy.yml new file mode 100644 index 000000000000..1d0d42ce761b --- /dev/null +++ b/.github/workflows/stale-tidy.yml @@ -0,0 +1,24 @@ +--- +name: 'Close inactive issues' +on: + schedule: + - cron: '30 1 * * 2,4,6' + +jobs: + stale: + if: github.repository == 'matplotlib/matplotlib' + runs-on: ubuntu-latest + steps: + - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + operations-per-run: 300 + days-before-stale: -1 + stale-pr-label: "status: inactive" + days-before-pr-close: -1 + stale-issue-label: "status: inactive" + close-issue-label: "status: closed as inactive" + days-before-issue-close: 30 + ascending: true + exempt-issue-labels: "keep,status: confirmed bug" + exempt-pr-labels: "keep,status: orphaned PR" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..a61a0b2e92c8 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,39 @@ +--- +name: 'Label inactive PRs' +on: + schedule: + - cron: '30 1 * * 1' + +jobs: + stale: + if: github.repository == 'matplotlib/matplotlib' + runs-on: ubuntu-latest + steps: + - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + operations-per-run: 20 + stale-pr-message: >- + Since this Pull Request has not been updated in 60 days, it has been marked "inactive." This does + not mean that it will be closed, though it may be moved to a "Draft" state. This helps maintainers + prioritize their reviewing efforts. You can pick the PR back up anytime - please ping us if you + need a review or guidance to move the PR forward! If you do not plan on continuing the work, please + let us know so that we can either find someone to take the PR over, or close it. + stale-pr-label: "status: inactive" + days-before-pr-stale: 60 + days-before-pr-close: -1 + stale-issue-message: >- + This issue has been marked "inactive" because it has been 365 days since the last comment. If this + issue is still present in recent Matplotlib releases, or the feature request is still wanted, + please leave a comment and this label will be removed. If there are no updates in another 30 days, + this issue will be automatically closed, but you are free to re-open or create a new issue if + needed. We value issue reports, and this procedure is meant to help us resurface and prioritize + issues that have not been addressed yet, not make them disappear. Thanks for your help! + stale-issue-label: "status: inactive" + close-issue-label: "status: closed as inactive" + days-before-issue-stale: 365 + days-before-issue-close: 30 + ascending: true + exempt-issue-labels: "keep" + exempt-pr-labels: "keep,status: orphaned PR" + sort-by: updated diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000000..76e2ad4c777b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,432 @@ +--- +name: Tests +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} + cancel-in-progress: true + +on: + push: + branches-ignore: + - auto-backport-of-pr-[0-9]+ + - v[0-9]+.[0-9]+.[0-9x]+-doc + - dependabot/** + pull_request: + branches-ignore: + - v[0-9]+.[0-9]+.[0-9x]+-doc + paths-ignore: + # Skip running tests if changes are only in documentation directories + - 'doc/**' + - 'galleries/**' + schedule: + # 5:47 UTC on Saturdays + - cron: "47 5 * * 6" + workflow_dispatch: + +env: + NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. + OPENBLAS_NUM_THREADS: 1 + PYTHONFAULTHANDLER: 1 + +jobs: + test: + if: >- + github.event_name == 'workflow_dispatch' || + ( + github.repository == 'matplotlib/matplotlib' && + !contains(github.event.head_commit.message, '[ci skip]') && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip github]') && + !contains(github.event.head_commit.message, '[ci doc]') + ) + permissions: + contents: read + name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}" + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - name-suffix: "(Minimum Versions)" + os: ubuntu-22.04 + python-version: '3.11' + extra-requirements: '-c requirements/testing/minver.txt' + delete-font-cache: true + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - os: ubuntu-22.04 + python-version: '3.11' + CFLAGS: "-fno-lto" # Ensure that disabling LTO works. + extra-requirements: '-r requirements/testing/extra.txt' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - name-suffix: "(Extra TeX packages)" + os: ubuntu-22.04 + python-version: '3.13' + extra-packages: 'texlive-fonts-extra texlive-lang-cyrillic' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - name-suffix: "Free-threaded" + os: ubuntu-22.04 + python-version: '3.13t' + # https://github.com/matplotlib/matplotlib/issues/29844 + pygobject-ver: '<3.52.0' + - os: ubuntu-24.04 + python-version: '3.12' + - os: ubuntu-24.04 + python-version: '3.14' + - os: ubuntu-24.04-arm + python-version: '3.12' + - os: macos-14 # This runner is on M1 (arm64) chips. + python-version: '3.11' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + - os: macos-14 # This runner is on M1 (arm64) chips. + python-version: '3.12' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + - os: macos-15 # This runner is on M1 (arm64) chips. + python-version: '3.13' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + - os: macos-15 # This runner is on M1 (arm64) chips. + python-version: '3.14' + # https://github.com/matplotlib/matplotlib/issues/29732 + pygobject-ver: '<3.52.0' + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - name: Install OS dependencies + run: | + case "${{ runner.os }}" in + Linux) + echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries + sudo apt-get update -yy + sudo apt-get install -yy --no-install-recommends \ + ccache \ + cm-super \ + dvipng \ + fonts-freefont-otf \ + fonts-noto-cjk \ + fonts-wqy-zenhei \ + gdb \ + gir1.2-gtk-3.0 \ + gir1.2-gtk-4.0 \ + graphviz \ + inkscape \ + language-pack-de \ + lcov \ + libcairo2 \ + libcairo2-dev \ + libffi-dev \ + libgeos-dev \ + libnotify4 \ + libsdl2-2.0-0 \ + libxkbcommon-x11-0 \ + libxcb-cursor0 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-xinerama0 \ + lmodern \ + ninja-build \ + pkg-config \ + qtbase5-dev \ + texlive-fonts-recommended \ + texlive-latex-base \ + texlive-latex-extra \ + texlive-latex-recommended \ + texlive-luatex \ + texlive-pictures \ + texlive-xetex \ + ${{ matrix.extra-packages }} + if [[ "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then + sudo apt-get install -yy --no-install-recommends ffmpeg poppler-utils + fi + if [[ "${{ matrix.os }}" = ubuntu-22.04 ]]; then + sudo apt-get install -yy --no-install-recommends \ + libgirepository1.0-dev + else # ubuntu-24.04 + sudo apt-get install -yy --no-install-recommends \ + libgirepository-2.0-dev + fi + ;; + macOS) + brew update + # Periodically, Homebrew updates Python and fails to overwrite the + # existing not-managed-by-Homebrew copy without explicitly being told + # to do so. GitHub/Azure continues to avoid fixing their runner images: + # https://github.com/actions/runner-images/issues/9966 + # so force an overwrite even if there are no Python updates. + # We don't even care about Homebrew's Python because we use the one + # from actions/setup-python. + for python_package in $(brew list | grep python@); do + brew unlink ${python_package} + brew link --overwrite ${python_package} + done + # Workaround for https://github.com/actions/runner-images/issues/10984 + brew uninstall --ignore-dependencies --force pkg-config@0.29.2 + brew install ccache ffmpeg ghostscript gobject-introspection gtk4 imagemagick ninja + brew install --cask font-noto-sans-cjk font-noto-sans-cjk-sc inkscape + ;; + esac + + - name: Cache pip + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + if: startsWith(runner.os, 'Linux') + with: + path: ~/.cache/pip + key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + restore-keys: | + ${{ matrix.os }}-py${{ matrix.python-version }}-pip- + - name: Cache pip + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + if: startsWith(runner.os, 'macOS') + with: + path: ~/Library/Caches/pip + key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + restore-keys: | + ${{ matrix.os }}-py${{ matrix.python-version }}-pip- + - name: Cache ccache + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + with: + path: | + ~/.ccache + key: ${{ matrix.os }}-py${{ matrix.python-version }}-ccache-${{ hashFiles('src/*') }} + restore-keys: | + ${{ matrix.os }}-py${{ matrix.python-version }}-ccache- + - name: Cache Matplotlib + uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 + with: + path: | + ~/.cache/matplotlib + !~/.cache/matplotlib/tex.cache + !~/.cache/matplotlib/test_cache + key: 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} + restore-keys: | + 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- + 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl- + + - name: Install Python dependencies + run: | + # Upgrade pip and setuptools and wheel to get as clean an install as + # possible. + python -m pip install --upgrade pip setuptools wheel + + # Install pre-release versions during our weekly upcoming dependency tests. + if [[ "${{ github.event_name }}" == 'schedule' + && "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then + PRE="--pre" + fi + + # Install dependencies from PyPI. + # Preinstall build requirements to enable no-build-isolation builds. + python -m pip install --upgrade $PRE \ + 'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \ + packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ + 'meson-python>=0.13.1' 'pybind11>=2.13.2' \ + -r requirements/testing/all.txt \ + ${{ matrix.extra-requirements }} + + # Install optional dependencies from PyPI. + # Sphinx is needed to run sphinxext tests + python -m pip install --upgrade sphinx!=6.1.2 + + if [[ "${{ matrix.python-version }}" != '3.13t' ]]; then + # GUI toolkits are pip-installable only for some versions of Python + # so don't fail if we can't install them. Make it easier to check + # whether the install was successful by trying to import the toolkit + # (sometimes, the install appears to be successful but shared + # libraries cannot be loaded at runtime, so an actual import is a + # better check). + python -m pip install --upgrade pycairo 'cairocffi>=0.8' 'PyGObject${{ matrix.pygobject-ver }}' && + ( + python -c 'import gi; gi.require_version("Gtk", "4.0"); from gi.repository import Gtk' && + echo 'PyGObject 4 is available' || echo 'PyGObject 4 is not available' + ) && ( + python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && + echo 'PyGObject 3 is available' || echo 'PyGObject 3 is not available' + ) + + # PyQt5 does not have any wheels for ARM on Linux. + if [[ "${{ matrix.os }}" != 'ubuntu-24.04-arm' ]]; then + python -mpip install --upgrade --only-binary :all: pyqt5 && + python -c 'import PyQt5.QtCore' && + echo 'PyQt5 is available' || + echo 'PyQt5 is not available' + fi + # Even though PySide2 wheels can be installed on Python 3.12+, they are broken and since PySide2 is + # deprecated, they are unlikely to be fixed. For the same deprecation reason, there are no wheels + # on M1 macOS, so don't bother there either. + if [[ "${{ matrix.os }}" != 'macos-14' && "${{ matrix.python-version }}" == '3.11' + ]]; then + python -mpip install --upgrade pyside2 && + python -c 'import PySide2.QtCore' && + echo 'PySide2 is available' || + echo 'PySide2 is not available' + fi + python -mpip install --upgrade --only-binary :all: pyqt6 && + python -c 'import PyQt6.QtCore' && + echo 'PyQt6 is available' || + echo 'PyQt6 is not available' + python -mpip install --upgrade --only-binary :all: pyside6 && + python -c 'import PySide6.QtCore' && + echo 'PySide6 is available' || + echo 'PySide6 is not available' + + python -mpip install --upgrade --only-binary :all: \ + -f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \ + wxPython && + python -c 'import wx' && + echo 'wxPython is available' || + echo 'wxPython is not available' + + fi # Skip backends on Python 3.13t. + + - name: Install the nightly dependencies + # Only install the nightly dependencies during the scheduled event + if: github.event_name == 'schedule' && matrix.name-suffix != '(Minimum Versions)' + run: | + python -m pip install pytz tzdata # Must be installed for Pandas. + python -m pip install \ + --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \ + --upgrade --only-binary=:all: numpy pandas + + - name: Install Matplotlib + run: | + ccache -s + git describe + + # Set flag in a delayed manner to avoid issues with installing other + # packages + if [[ "${{ runner.os }}" == 'macOS' ]]; then + export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw' + export CPPFLAGS="$CPPFLAGS -fcoverage-mapping" + else + export CPPFLAGS='--coverage -fprofile-abs-path' + fi + + python -m pip install --no-deps --no-build-isolation --verbose \ + --config-settings=setup-args="-DrcParams-backend=Agg" \ + --editable .[dev] + + if [[ "${{ runner.os }}" != 'macOS' ]]; then + unset CPPFLAGS + fi + + - name: Clear font cache + run: | + rm -rf ~/.cache/matplotlib + if: matrix.delete-font-cache + + - name: Run pytest + run: | + if [[ "${{ matrix.python-version }}" == '3.13t' ]]; then + export PYTHON_GIL=0 + fi + pytest -rfEsXR -n auto \ + --maxfail=50 --timeout=300 --durations=25 \ + --cov-report=xml --cov=lib --log-level=DEBUG --color=yes + + - name: Cleanup non-failed image files + if: failure() + run: | + find ./result_images -name "*-expected*.png" | while read file; do + if [[ $file == *-expected_???.png ]]; then + extension=${file: -7:3} + base=${file%*-expected_$extension.png}_$extension + else + extension="png" + base=${file%-expected.png} + fi + if [[ ! -e ${base}-failed-diff.png ]]; then + indent="" + list=($file $base.png) + if [[ $extension != "png" ]]; then + list+=(${base%_$extension}-expected.$extension ${base%_$extension}.$extension) + fi + for to_remove in "${list[@]}"; do + if [[ -e $to_remove ]]; then + rm $to_remove + echo "${indent}Removed $to_remove" + fi + indent+=" " + done + fi + done + + if [ "$(find ./result_images -mindepth 1 -type d)" ]; then + find ./result_images/* -type d -empty -delete + fi + + - name: Filter C coverage + if: ${{ !cancelled() && github.event_name != 'schedule' }} + run: | + if [[ "${{ runner.os }}" != 'macOS' ]]; then + LCOV_IGNORE_ERRORS=',' # do not ignore any lcov errors by default + if [[ "${{ matrix.os }}" = ubuntu-24.04 || "${{ matrix.os }}" = ubuntu-24.04-arm ]]; then + # filter mismatch and unused-entity errors detected by lcov 2.x + LCOV_IGNORE_ERRORS='mismatch,unused' + fi + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --capture --directory . --output-file coverage.info + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --output-file coverage.info --extract coverage.info $PWD/src/'*' $PWD/lib/'*' + lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ + --list coverage.info + find . -name '*.gc*' -delete + else + xcrun llvm-profdata merge -sparse default.*.profraw \ + -o default.profdata + xcrun llvm-cov export -format="lcov" build/*/src/*.so \ + -instr-profile default.profdata > info.lcov + fi + - name: Upload code coverage + if: ${{ !cancelled() && github.event_name != 'schedule' }} + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + with: + name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}" + token: ${{ secrets.CODECOV_TOKEN }} + + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + if: failure() + with: + name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" + path: ./result_images + + # Separate dependent job to only upload one issue from the matrix of jobs + create-issue: + if: ${{ failure() && github.event_name == 'schedule' }} + needs: [test] + permissions: + issues: write + runs-on: ubuntu-latest + name: "Create issue on failure" + + steps: + - name: Create issue on failure + uses: imjohnbo/issue-bot@572eed14422c4d6ca37e870f97e7da209422f5bd # v3.4.4 + with: + title: "[TST] Upcoming dependency test failures" + body: | + The weekly build with nightly wheels from numpy and pandas + has failed. Check the logs for any updates that need to be + made in matplotlib. + https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + + pinned: false + close-previous: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index c2edc8499b08..0460152a792f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ .pydevproject *.swp .idea +.vscode/ # Compiled source # ################### @@ -28,18 +29,23 @@ # Python files # ################ -# setup.py working directory +# meson-python working directory build -# sphinx build directory -doc/_build -# setup.py dist directory +.mesonpy* + +# meson-python/build frontend dist directory dist # Egg metadata *.egg-info .eggs +# wheel metadata +pip-wheel-metadata/* # tox testing tool .tox -setup.cfg +# build subproject files +subprojects/*/ +subprojects/.* +!subprojects/packagefiles/ # OS generated files # ###################### @@ -52,35 +58,74 @@ Thumbs.db # Things specific to this project # ################################### -lib/matplotlib/mpl-data/matplotlib.conf -lib/matplotlib/mpl-data/matplotlibrc +galleries/tutorials/intermediate/CL01.png +galleries/tutorials/intermediate/CL02.png # Documentation generated files # ################################# +# sphinx build directory +doc/_build +doc/api/_as_gen +# autogenerated by sphinx-gallery doc/examples -doc/_templates/gallery.html -doc/users/installing.rst -doc/_static/matplotlibrc +doc/gallery +doc/modules +doc/plot_types doc/pyplots/tex_demo.png -doc/api/_as_gen +doc/tutorials +doc/users/explain lib/dateutil -examples/*/*.pdf -examples/*/*.png -examples/tests/* -!examples/tests/backend_driver.py -texput.log -texput.aux +galleries/examples/*/*.bmp +galleries/examples/*/*.eps +galleries/examples/*/*.pdf +galleries/examples/*/*.png +galleries/examples/*/*.svg +galleries/examples/*/*.svgz result_images +doc/_static/constrained_layout*.png +doc/.mpl_skip_subdirs.yaml +doc/_tags +sg_execution_times.rst -# Nose generated files # -######################## +# Nose/Pytest generated files # +############################### +.pytest_cache/ +.cache/ .coverage .coverage.* +*.py,cover cover/ .noseids +__pycache__ # Conda files # ############### __conda_version__.txt lib/png.lib lib/z.lib + +# uv files # +############ +uv.lock + +# Environments # +################ +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Jupyter files # +################# + +.ipynb_checkpoints/ + +# Vendored dependencies # +######################### +lib/matplotlib/backends/web_backend/node_modules/ +lib/matplotlib/backends/web_backend/package-lock.json + +LICENSE/LICENSE_QHULL diff --git a/.mailmap b/.mailmap index f638116e560f..44005da6e2d8 100644 --- a/.mailmap +++ b/.mailmap @@ -3,6 +3,14 @@ Adam Ortiz Adrien F. Vincent Adrien F. Vincent +Aleksey Bilogur + +Alexander Rudy + +Alon Hershenhorn + +Alvaro Sanchez + Andrew Dawson anykraus @@ -13,16 +21,31 @@ Ben Cohen Ben Root Benjamin Root +Benedikt Daurer + Benjamin Congdon Benjamin Congdon bcongdon +Bruno Zohreh + +Carsten Schelp + Casper van der Wel +Chris Holdgraf + +Cho Yin Yong + +Chris + Christoph Gohlke cgohlke Christoph Gohlke C. Gohlke +Christoph Gohlke Cimarron Mittelsteadt Cimarron +cldssty + Conner R. Phillips Dan Hickstein @@ -34,6 +57,10 @@ David Kua Devashish Deshpande +Dietmar Schwertberger + +Dora Fraeman Caswell + endolith Eric Dill @@ -43,16 +70,28 @@ Erik Bray Eric Ma Eric Ma +esvhd + Filipe Fernandes Florian Le Bourdais Francesco Montesano montefra +Gauravjeet + +Hajoon Choi + hannah Hans Moritz Günther +Harshal Prakash Patankar + +Harshit Patni + +ImportanceOfBeingErnest + J. Goutin JGoutin Jack Kelly @@ -73,6 +112,8 @@ Jeffrey Bingham Jens Hedegaard Nielsen Jens Hedegaard Nielsen +Joel Frederico <458871+joelfrederico@users.noreply.github.com> + John Hunter Jorrit Wronski @@ -82,6 +123,8 @@ Joseph Fox-Rabinovitz Joseph Fox-Rabinovitz +Julien Lhermitte + Julien Schueller Julien Schueller @@ -94,24 +137,43 @@ Klara Gerlei klaragerlei Kristen M. Thyng +Kyle Sunden + Leeonadoh Lennart Fricke Levi Kilcher +Leon Yin + Lion Krischer +Manan Kevadiya +Manan Kevadiya <43081866+manan2501@users.noreply.github.com> + +Manuel Nuno Melo + +Marco Gorelli +Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> + +Marek Rudnicki + Martin Fitzpatrick +Matt Newville + Matthew Emmett Matthew Emmett Matthias Bussonnier +Matthias Bussonnier Matthias Lüthi Matthias Lüthi +Matti Picus + Michael Droettboom Michael Droettboom Michael Droettboom @@ -133,12 +195,23 @@ Nicolas P. Rougier OceanWolf +Olivier Castany <1868182+ocastany@users.noreply.github.com> +Olivier Castany <1868182+ocastany@users.noreply.github.com> +Olivier Castany <1868182+ocastany@users.noreply.github.com> + +Om Sitapara + Patrick Chen +Paul Ganssle +Paul Ganssle + Paul Hobson Paul Hobson vagrant +Paul Ivanov Paul Ivanov +Paul Ivanov Per Parker @@ -151,13 +224,21 @@ Phil Elson productivememberofsociety666 none +Rishikesh + RyanPan +Samesh Lakhotia +Samesh Lakhotia <43701530+sameshl@users.noreply.github.com> ' + Scott Lasley Sebastian Raschka Sebastian Raschka +Sidharth Bansal +Sidharth Bansal <20972099+SidharthBansal@users.noreply.github.com> + Simon Cross Slav Basharov @@ -168,13 +249,36 @@ Stefan van der Walt switham switham +Taehoon Lee + +Ted Drain + +Taras Kuzyo + +Terence Honles + Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell Thomas A Caswell <“tcaswell@gmail.com”> +Thomas A Caswell Thomas A Caswell + +Till Stensitzki Trish Gillett-Kawamoto +Tuan Dung Tran + +Víctor Zabalza + +Vidur Satija + +WANG Aiyong + +Zhili (Jerry) Pan + Werner F Bruhin Yunfei Yang Yunfei Yang Yunfei Yang Yunfei Yang + +Zac Hatfield-Dodds diff --git a/.matplotlib-repo b/.matplotlib-repo new file mode 100644 index 000000000000..0b1d699bcdb1 --- /dev/null +++ b/.matplotlib-repo @@ -0,0 +1,3 @@ +The existence of this file signals that the code is a matplotlib source repo +and not an installed version. We use this in __init__.py for gating version +detection. diff --git a/.meeseeksdev.yml b/.meeseeksdev.yml new file mode 100644 index 000000000000..f9d44d44cfdf --- /dev/null +++ b/.meeseeksdev.yml @@ -0,0 +1,5 @@ +--- +users: + Carreau: + can: + - backport diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..11499188509e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,121 @@ +--- +ci: + autofix_prs: false + autoupdate_schedule: 'quarterly' +exclude: | + (?x)^( + extern| + LICENSE| + lib/matplotlib/mpl-data| + doc/devel/gitwash| + doc/release/prev| + doc/api/prev| + lib/matplotlib/tests/data/tinypages + ) +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-docstring-first + exclude: lib/matplotlib/typing.py # docstring used for attribute flagged by check + - id: end-of-file-fixer + exclude_types: [svg] + - id: mixed-line-ending + - id: name-tests-test + args: ["--pytest-test-first"] + - id: no-commit-to-branch # Default is master and main. + - id: trailing-whitespace + exclude_types: [svg] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy + additional_dependencies: + - pandas-stubs + - types-pillow + - types-python-dateutil + - types-psutil + - types-docutils + - types-PyYAML + args: ["--config-file=pyproject.toml", "lib/matplotlib"] + files: lib/matplotlib # Only run when files in lib/matplotlib are changed. + pass_filenames: false + + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.11.5 + hooks: + # Run the linter. + - id: ruff + args: [--fix, --show-fixes] + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + files: ^.*\.(py|c|cpp|h|m|md|rst|yml)$ + args: + - "--ignore-words" + - "ci/codespell-ignore-words.txt" + - "--skip" + - "doc/project/credits.rst" + - repo: https://github.com/pycqa/isort + rev: 6.0.1 + hooks: + - id: isort + name: isort (python) + files: ^galleries/tutorials/|^galleries/examples/|^galleries/plot_types/ + - repo: https://github.com/rstcheck/rstcheck + rev: v6.2.4 + hooks: + - id: rstcheck + additional_dependencies: + - sphinx>=1.8.1 + - tomli + - repo: https://github.com/adrienverge/yamllint + rev: v1.37.0 + hooks: + - id: yamllint + args: ["--strict", "--config-file=.yamllint.yml"] + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.0 + hooks: + # TODO: Re-enable this when https://github.com/microsoft/azure-pipelines-vscode/issues/567 is fixed. + # - id: check-azure-pipelines + - id: check-dependabot + - id: check-github-workflows + # NOTE: If any of the below schema files need to be changed, be sure to + # update the `ci/vendor_schemas.py` script. + - id: check-jsonschema + name: "Validate AppVeyor config" + files: ^\.appveyor\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/appveyor.json"] + - id: check-jsonschema + name: "Validate CircleCI config" + files: ^\.circleci/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/circleciconfig.json"] + - id: check-jsonschema + name: "Validate GitHub funding file" + files: ^\.github/FUNDING\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-funding.json"] + - id: check-jsonschema + name: "Validate GitHub issue config" + files: ^\.github/ISSUE_TEMPLATE/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-issue-config.json"] + - id: check-jsonschema + name: "Validate GitHub issue templates" + files: ^\.github/ISSUE_TEMPLATE/.*\.yml$ + exclude: ^\.github/ISSUE_TEMPLATE/config\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/github-issue-forms.json"] + - id: check-jsonschema + name: "Validate CodeCov config" + files: ^\.github/codecov\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/codecov.json"] + - id: check-jsonschema + name: "Validate GitHub labeler config" + files: ^\.github/labeler\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/pull-request-labeler-5.json"] + - id: check-jsonschema + name: "Validate Conda environment file" + files: ^environment\.yml$ + args: ["--verbose", "--schemafile", "ci/schemas/conda-environment.json"] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d6f2154a6e44..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,227 +0,0 @@ -language: python - -sudo: false - -cache: - pip: true - directories: - - $HOME/.ccache # https://github.com/travis-ci/travis-ci/issues/5853 - - $HOME/.cache/matplotlib - -addons: - apt: - packages: - - inkscape - - libav-tools - - gdb - - mencoder - - dvipng - - pgf - - lmodern - - cm-super - - texlive-latex-base - - texlive-latex-extra - - texlive-fonts-recommended - - texlive-latex-recommended - - texlive-xetex - - graphviz - - libgeos-dev - - otf-freefont -# - fonts-humor-sans -# sources: -# - debian-sid - -env: - global: - - ARTIFACTS_AWS_REGION=us-east-1 - - ARTIFACTS_S3_BUCKET=matplotlib-test-results - - secure: RgJI7BBL8aX5FTOQe7xiXqWHMxWokd6GNUWp1NUV2mRLXPb9dI0RXqZt3UJwKTAzf1z/OtlHDmEkBoTVK81E9iUxK5npwyyjhJ8yTJmwfQtQF2n51Q1Ww9p+XSLORrOzZc7kAo6Kw6FIXN1pfctgYq2bQkrwJPRx/oPR8f6hcbY= - - secure: E7OCdqhZ+PlwJcn+Hd6ns9TDJgEUXiUNEI0wu7xjxB2vBRRIKtZMbuaZjd+iKDqCKuVOJKu0ClBUYxmgmpLicTwi34CfTUYt6D4uhrU+8hBBOn1iiK51cl/aBvlUUrqaRLVhukNEBGZcyqAjXSA/Qsnp2iELEmAfOUa92ZYo1sk= - - secure: "dfjNqGKzQG5bu3FnDNwLG8H/C4QoieFo4PfFmZPdM2RY7WIzukwKFNT6kiDfOrpwt+2bR7FhzjOGlDECGtlGOtYPN8XuXGjhcP4a4IfakdbDfF+D3NPIpf5VlE6776k0VpvcZBTMYJKNFIMc7QPkOwjvNJ2aXyfe3hBuGlKJzQU=" - - BUILD_DOCS=false - - NUMPY=numpy - - OPENBLAS_NUM_THREADS=1 - - PANDAS= - - NPROC=2 - - TEST_ARGS=--no-pep8 - - NOSE_ARGS="--processes=$NPROC --process-timeout=300" - - PYTEST_ARGS="-ra --timeout=300 --durations=25 --cov-report= --cov=lib" # -n $NPROC - - PYTHON_ARGS= - - DELETE_FONT_CACHE= - - USE_PYTEST=false - #- PYTHONHASHSEED=0 # Workaround for pytest-xdist flaky colletion order - # # https://github.com/pytest-dev/pytest/issues/920 - # # https://github.com/pytest-dev/pytest/issues/1075 - -matrix: - include: - - python: 2.7 - env: MOCK=mock NUMPY=numpy==1.6 - - python: 3.4 - env: PYTHON_ARGS=-OO - - python: 3.5 - env: PANDAS=pandas NOSE_ARGS=--with-coverage DELETE_FONT_CACHE=1 - - python: 3.5 - env: TEST_ARGS=--pep8 - - python: 3.5 - env: BUILD_DOCS=true - - python: 3.5 - env: USE_PYTEST=true PANDAS=pandas DELETE_FONT_CACHE=1 TEST_ARGS= - - python: "3.6-dev" - env: PRE=--pre - - python: "nightly" - env: PRE=--pre - - os: osx - osx_image: xcode7.3 - language: generic # https://github.com/travis-ci/travis-ci/issues/2312 - env: MOCK=mock NOSE_ARGS= - cache: - # As for now travis caches only "$HOME/.cache/pip" - # https://docs.travis-ci.com/user/caching/#pip-cache - pip: false - directories: - - $HOME/Library/Caches/pip - # `cache` does not support `env`-like `global` so copy-paste from top - - $HOME/.ccache # https://github.com/travis-ci/travis-ci/issues/5853 - - $HOME/.cache/matplotlib - allow_failures: - - python: "nightly" - -before_install: - - | - # Install into our own pristine virtualenv - if [[ $TRAVIS_OS_NAME != 'osx' ]]; then - pip install --upgrade virtualenv - python -m virtualenv venv - source venv/bin/activate - export PATH=/usr/lib/ccache:$PATH - else - brew update - brew tap homebrew/gui - brew install python libpng ffmpeg imagemagick mplayer ccache - # We could install ghostscript and inkscape here to test svg and pdf - # but this makes the test time really long. - # brew install ghostscript inkscape - export PATH=/usr/local/opt/ccache/libexec:$PATH - fi - -install: - - | - # Setup environment - ccache -s - # Upgrade pip and setuptools and wheel to get as clean an install as possible - pip install --upgrade pip - pip install --upgrade wheel - pip install --upgrade setuptools - - | - # Install dependencies from pypi - pip install $PRE python-dateutil $NUMPY pyparsing!=2.1.6 $PANDAS pep8 cycler coveralls coverage $MOCK - pip install $PRE -r doc-requirements.txt - - # Install nose from a build which has partial - # support for python36 and suport for coverage output suppressing - pip install git+https://github.com/jenshnielsen/nose.git@matplotlibnose - # pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124 - pip install $PRE pytest 'pytest-cov>=2.3.1' pytest-timeout pytest-xdist pytest-faulthandler - - # We manually install humor sans using the package from Ubuntu 14.10. Unfortunatly humor sans is not - # availible in the Ubuntu version used by Travis but we can manually install the deb from a later - # version since is it basically just a .ttf file - # The current Travis Ubuntu image is to old to search .local/share/fonts so we store fonts in .fonts - if [[ $BUILD_DOCS == true ]]; then - wget https://github.com/google/fonts/blob/master/ofl/felipa/Felipa-Regular.ttf?raw=true -O Felipa-Regular.ttf - wget http://mirrors.kernel.org/ubuntu/pool/universe/f/fonts-humor-sans/fonts-humor-sans_1.0-1_all.deb - mkdir -p tmp - mkdir -p ~/.fonts - dpkg -x fonts-humor-sans_1.0-1_all.deb tmp - cp tmp/usr/share/fonts/truetype/humor-sans/Humor-Sans.ttf ~/.fonts - cp Felipa-Regular.ttf ~/.fonts - fc-cache -f -v - else - # Use the special local version of freetype for testing - cp ci/travis/setup.cfg . - fi; - - | - # Install matplotlib - pip install -e . - - | - -script: - # The number of processes is hardcoded, because using too many causes the - # Travis VM to run out of memory (since so many copies of inkscape and - # ghostscript are running at the same time). - - | - echo Testing import of tkagg backend - MPLBACKEND="tkagg" python -c 'import matplotlib.pyplot as plt; print(plt.get_backend())' - if [[ $BUILD_DOCS == false ]]; then - if [[ $DELETE_FONT_CACHE == 1 ]]; then - rm -rf ~/.cache/matplotlib - fi - export MPL_REPO_DIR=$PWD # needed for pep8-conformance test of the examples - if [[ $USE_PYTEST == false ]]; then - echo The following args are passed to nose $NOSE_ARGS - if [[ $TRAVIS_OS_NAME == 'osx' ]]; then - python tests.py $NOSE_ARGS $TEST_ARGS - else - gdb -return-child-result -batch -ex r -ex bt --args python $PYTHON_ARGS tests.py $NOSE_ARGS $TEST_ARGS - fi - else - echo The following args are passed to pytest $PYTEST_ARGS - py.test $PYTEST_ARGS $TEST_ARGS - fi - else - cd doc - python make.py html -n 2 - # We don't build the LaTeX docs here, so linkchecker will complain - touch build/html/Matplotlib.pdf - # Linkchecker only works with python 2.7 for the time being - deactivate - source ~/virtualenv/python2.7/bin/activate - pip install pip --upgrade - # linkchecker is currently broken with requests 2.10.0 so force an earlier version - pip install $PRE requests==2.9.2 linkchecker - linkchecker build/html/index.html - fi - - rm -rf $HOME/.cache/matplotlib/tex.cache - - rm -rf $HOME/.cache/matplotlib/test_cache - -after_failure: - - | - if [[ $BUILD_DOCS == false && $TRAVIS_PULL_REQUEST == false && $TRAVIS_REPO_SLUG == 'matplotlib/matplotlib' ]]; then - gem install travis-artifacts - cd $TRAVIS_BUILD_DIR/../tmp_test_dir - tar cjf result_images.tar.bz2 result_images - travis-artifacts upload --path result_images.tar.bz2 - echo https://s3.amazonaws.com/matplotlib-test-results/artifacts/${TRAVIS_BUILD_NUMBER}/${TRAVIS_JOB_NUMBER}/result_images.tar.bz2 - else - echo "The result images will only be uploaded if they are on the matplotlib/matplotlib repo - this is for security reasons to prevent arbitrary PRs echoing security details." - fi - -after_success: - - | - if [[ $TRAVIS_PULL_REQUEST == false && $TRAVIS_REPO_SLUG == 'matplotlib/matplotlib' && $BUILD_DOCS == true && $TRAVIS_BRANCH == 'master' ]]; then - cd $TRAVIS_BUILD_DIR - echo "Uploading documentation" - openssl aes-256-cbc -K $encrypted_cc802e084cd0_key -iv $encrypted_cc802e084cd0_iv -in ci/travis/matplotlibDeployKey.enc -out ci/travis/matplotlibDeployKey -d - eval `ssh-agent -s` - chmod 600 ci/travis/matplotlibDeployKey - ssh-add ci/travis/matplotlibDeployKey - cd .. - git clone git@github.com:matplotlib/devdocs.git - cd devdocs - git checkout --orphan gh-pages - git reset --hard first_commit - cp -R ../matplotlib/doc/build/html/. . - touch .nojekyll - git config --global user.email "MatplotlibTravisBot@nomail" - git config --global user.name "MatplotlibTravisBot" - git config --global push.default simple - git add . - git commit -m "Docs build of $TRAVIS_COMMIT" - git push --set-upstream origin gh-pages --force - else - echo "Will only deploy docs build from matplotlib master branch" - fi - if [[ $NOSE_ARGS =~ "--with-coverage" || $USE_PYTEST == true ]]; then - coveralls - fi diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 000000000000..2be81b28c7fb --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,9 @@ +--- +extends: default + +rules: + line-length: + max: 120 + allow-non-breakable-words: true + truthy: + check-keys: false diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index ce177318dce1..000000000000 --- a/CHANGELOG +++ /dev/null @@ -1,5399 +0,0 @@ - -2015-11-16 Levels passed to contour(f) and tricontour(f) must be in increasing - order. - -2015-10-21 Added TextBox widget - - -2015-10-21 Added get_ticks_direction() - -2015-02-27 Added the rcParam 'image.composite_image' to permit users - to decide whether they want the vector graphics backends to combine - all images within a set of axes into a single composite image. - (If images do not get combined, users can open vector graphics files - in Adobe Illustrator or Inkscape and edit each image individually.) - -2015-02-19 Rewrite of C++ code that calculates contours to add support for - corner masking. This is controlled by the 'corner_mask' keyword - in plotting commands 'contour' and 'contourf'. - IMT - -2015-01-23 Text bounding boxes are now computed with advance width rather than - ink area. This may result in slightly different placement of text. - -2014-10-27 Allowed selection of the backend using the `MPLBACKEND` environment - variable. Added documentation on backend selection methods. - -2014-09-27 Overhauled `colors.LightSource`. Added `LightSource.hillshade` to - allow the independent generation of illumination maps. Added new - types of blending for creating more visually appealing shaded relief - plots (e.g. `blend_mode="overlay"`, etc, in addition to the legacy - "hsv" mode). - -2014-06-10 Added Colorbar.remove() - -2014-06-07 Fixed bug so radial plots can be saved as ps in py3k. - -2014-06-01 Changed the fmt kwarg of errorbar to support the - the mpl convention that "none" means "don't draw it", - and to default to the empty string, so that plotting - of data points is done with the plot() function - defaults. Deprecated use of the None object in place - "none". - -2014-05-22 Allow the linscale keyword parameter of symlog scale to be - smaller than one. - -2014-05-20 Added logic to in FontManager to invalidate font-cache if - if font-family rcparams have changed. - -2014-05-16 Fixed the positioning of multi-line text in the PGF backend. - -2014-05-14 Added Axes.add_image() as the standard way to add AxesImage - instances to Axes. This improves the consistency with - add_artist(), add_collection(), add_container(), add_line(), - add_patch(), and add_table(). - -2014-05-02 Added colorblind-friendly colormap, named 'Wistia'. - -2014-04-27 Improved input clean up in Axes.{h|v}lines - Coerce input into a 1D ndarrays (after dealing with units). - -2014-04-27 removed un-needed cast to float in stem - -2014-04-23 Updated references to "ipython -pylab" - The preferred method for invoking pylab is now using the - "%pylab" magic. - -Chris G. - -2014-04-22 Added (re-)generate a simple automatic legend to "Figure Options" - dialog of the Qt4Agg backend. - -2014-04-22 Added an example showing the difference between - interpolation = 'none' and interpolation = 'nearest' in - `imshow()` when saving vector graphics files. - -2014-04-22 Added violin plotting functions. See `Axes.violinplot`, - `Axes.violin`, `cbook.violin_stats` and `mlab.GaussianKDE` for - details. - -2014-04-10 Fixed the triangular marker rendering error. The "Up" triangle was - rendered instead of "Right" triangle and vice-versa. - -2014-04-08 Fixed a bug in parasite_axes.py by making a list out - of a generator at line 263. - -2014-04-02 Added `clipon=False` to patch creation of wedges and shadows - in `pie`. - -2014-02-25 In backend_qt4agg changed from using update -> repaint under - windows. See comment in source near `self._priv_update` for - longer explaination. - -2014-03-27 Added tests for pie ccw parameter. Removed pdf and svg images - from tests for pie linewidth parameter. - -2014-03-24 Changed the behaviour of axes to not ignore leading or trailing - patches of height 0 (or width 0) while calculating the x and y - axis limits. Patches having both height == 0 and width == 0 are - ignored. - -2014-03-24 Added bool kwarg (manage_xticks) to boxplot to enable/disable - the managemnet of the xlimits and ticks when making a boxplot. - Default in True which maintains current behavior by default. - -2014-03-23 Fixed a bug in projections/polar.py by making sure that the theta - value being calculated when given the mouse coordinates stays within - the range of 0 and 2 * pi. - -2014-03-22 Added the keyword arguments wedgeprops and textprops to pie. - Users can control the wedge and text properties of the pie - in more detail, if they choose. - -2014-03-17 Bug was fixed in append_axes from the AxesDivider class would not - append axes in the right location with respect to the reference - locator axes - -2014-03-13 Add parameter 'clockwise' to function pie, True by default. - -2014-02-28 Added 'origin' kwarg to `spy` - -2014-02-27 Implemented separate horizontal/vertical axes padding to the - ImageGrid in the AxesGrid toolkit - -2014-02-27 Allowed markevery property of matplotlib.lines.Line2D to be, an int - numpy fancy index, slice object, or float. The float behaviour - turns on markers at approximately equal display-coordinate-distances - along the line. - -2014-02-25 In backend_qt4agg changed from using update -> repaint under - windows. See comment in source near `self._priv_update` for - longer explaination. - -2014-01-02 `triplot` now returns the artist it adds and support of line and - marker kwargs has been improved. GBY - -2013-12-30 Made streamplot grid size consistent for different types of density - argument. A 30x30 grid is now used for both density=1 and - density=(1, 1). - -2013-12-03 Added a pure boxplot-drawing method that allow a more complete - customization of boxplots. It takes a list of dicts contains stats. - Also created a function (`cbook.boxplot_stats`) that generates the - stats needed. - -2013-11-28 Added qhull extension module to perform Delaunay triangulation more - robustly than before. It is used by tri.Triangulation (and hence - all pyplot.tri* methods) and mlab.griddata. Deprecated - matplotlib.delaunay module. - IMT - -2013-11-05 Add power-law normalization method. This is useful for, - e.g., showing small populations in a "hist2d" histogram. - -2013-10-27 Added get_rlabel_position and set_rlabel_position methods to - PolarAxes to control angular position of radial tick labels. - -2013-10-06 Add stride-based functions to mlab for easy creation of 2D arrays - with less memory. - -2013-10-06 Improve window and detrend functions in mlab, particulart support for - 2D arrays. - -2013-10-06 Improve performance of all spectrum-related mlab functions and plots. - -2013-10-06 Added support for magnitude, phase, and angle spectrums to - axes.specgram, and support for magnitude, phase, angle, and complex - spectrums to mlab-specgram. - -2013-10-06 Added magnitude_spectrum, angle_spectrum, and phase_spectrum plots, - as well as magnitude_spectrum, angle_spectrum, phase_spectrum, - and complex_spectrum functions to mlab - -2013-07-12 Added support for datetime axes to 2d plots. Axis values are passed - through Axes.convert_xunits/Axes.convert_yunits before being used by - contour/contourf, pcolormesh and pcolor. - -2013-07-12 Allowed matplotlib.dates.date2num, matplotlib.dates.num2date, - and matplotlib.dates.datestr2num to accept n-d inputs. Also - factored in support for n-d arrays to matplotlib.dates.DateConverter - and matplotlib.units.Registry. - -2013-06-26 Refactored the axes module: the axes module is now a folder, - containing the following submodule: - - _subplots.py, containing all the subplots helper methods - - _base.py, containing several private methods and a new - _AxesBase class. This _AxesBase class contains all the methods - that are not directly linked to plots of the "old" Axes - - _axes.py contains the Axes class. This class now inherits from - _AxesBase: it contains all "plotting" methods and labelling - methods. - This refactoring should not affect the API. Only private methods - are not importable from the axes module anymore. - -2013-05-18 Added support for arbitrary rasterization resolutions to the - SVG backend. Previously the resolution was hard coded to 72 - dpi. Now the backend class takes a image_dpi argument for - its constructor, adjusts the image bounding box accordingly - and forwards a magnification factor to the image renderer. - The code and results now resemble those of the PDF backend. - - MW - -2013-05-08 Changed behavior of hist when given stacked=True and normed=True. - Histograms are now stacked first, then the sum is normalized. - Previously, each histogram was normalized, then they were stacked. - -2013-04-25 Changed all instances of: - - from matplotlib import MatplotlibDeprecationWarning as mplDeprecation - to: - - from cbook import mplDeprecation - - and removed the import into the matplotlib namespace in __init__.py - Thomas Caswell - -2013-04-15 Added 'axes.xmargin' and 'axes.ymargin' to rpParams to set default - margins on auto-scaleing. - TAC - -2013-04-16 Added patheffect support for Line2D objects. -JJL - -2013-03-31 Added support for arbitrary unstructured user-specified - triangulations to Axes3D.tricontour[f] - Damon McDougall - -2013-03-19 Added support for passing `linestyle` kwarg to `step` so all `plot` - kwargs are passed to the underlying `plot` call. -TAC - -2013-02-25 Added classes CubicTriInterpolator, UniformTriRefiner, TriAnalyzer - to matplotlib.tri module. - GBy - -2013-01-23 Add 'savefig.directory' to rcParams to remember and fill in the last - directory saved to for figure save dialogs - Martin Spacek - -2013-01-13 Add eventplot method to axes and pyplot and EventCollection class - to collections. - -2013-01-08 Added two extra titles to axes which are flush with the left and - right edges of the plot respectively. - Andrew Dawson - -2013-01-07 Add framealpha keyword argument to legend - PO - -2013-01-16 Till Stensitzki added a baseline feature to stackplot - -2012-12-22 Added classes for interpolation within triangular grids - (LinearTriInterpolator) and to find the triangles in which points - lie (TrapezoidMapTriFinder) to matplotlib.tri module. - IMT - -2012-12-05 Added MatplotlibDeprecationWarning class for signaling deprecation. - Matplotlib developers can use this class as follows: - - from matplotlib import MatplotlibDeprecationWarning as mplDeprecation - - In light of the fact that Python builtin DeprecationWarnings are - ignored by default as of Python 2.7, this class was put in to allow - for the signaling of deprecation, but via UserWarnings which are - not ignored by default. - PI - -2012-11-27 Added the *mtext* parameter for supplying matplotlib.text.Text - instances to RendererBase.draw_tex and RendererBase.draw_text. - This allows backends to utilize additional text attributes, like - the alignment of text elements. - pwuertz - -2012-11-26 deprecate matplotlib/mpl.py, which was used only in pylab.py and is - now replaced by the more suitable `import matplotlib as mpl`. - PI - -2012-11-25 Make rc_context available via pyplot interface - PI - -2012-11-16 plt.set_cmap no longer throws errors if there is not already - an active colorable artist, such as an image, and just sets - up the colormap to use from that point forward. - PI - -2012-11-16 Added the funcction _get_rbga_face, which is identical to - _get_rbg_face except it return a (r,g,b,a) tuble, to line2D. - Modified Line2D.draw to use _get_rbga_face to get the markerface - color so that any alpha set by markerfacecolor will respected. - - Thomas Caswell - -2012-11-13 Add a symmetric log normalization class to colors.py. - Also added some tests for the normalization class. - Till Stensitzki - -2012-11-12 Make axes.stem take at least one argument. - Uses a default range(n) when the first arg not provided. - Damon McDougall - -2012-11-09 Make plt.subplot() without arguments act as subplot(111) - PI - -2012-11-08 Replaced plt.figure and plt.subplot calls by the newer, more - convenient single call to plt.subplots() in the documentation - examples - PI - -2012-10-05 Add support for saving animations as animated GIFs. - JVDP - -2012-08-11 Fix path-closing bug in patches.Polygon, so that regardless - of whether the path is the initial one or was subsequently - set by set_xy(), get_xy() will return a closed path if and - only if get_closed() is True. Thanks to Jacob Vanderplas. - EF - -2012-08-05 When a norm is passed to contourf, either or both of the - vmin, vmax attributes of that norm are now respected. - Formerly they were respected only if both were - specified. In addition, vmin and/or vmax can now - be passed to contourf directly as kwargs. - EF - -2012-07-24 Contourf handles the extend kwarg by mapping the extended - ranges outside the normed 0-1 range so that they are - handled by colormap colors determined by the set_under - and set_over methods. Previously the extended ranges - were mapped to 0 or 1 so that the "under" and "over" - colormap colors were ignored. This change also increases - slightly the color contrast for a given set of contour - levels. - EF - -2012-06-24 Make use of mathtext in tick labels configurable - DSD - -2012-06-05 Images loaded through PIL are now ordered correctly - CG - -2012-06-02 Add new Axes method and pyplot function, hist2d. - PO - -2012-05-31 Remove support for 'cairo.' style of backend specification. - Deprecate 'cairo.format' and 'savefig.extension' rcParams and - replace with 'savefig.format'. - Martin Spacek - -2012-05-29 pcolormesh now obeys the passed in "edgecolor" kwarg. - To support this, the "shading" argument to pcolormesh now only - takes "flat" or "gouraud". To achieve the old "faceted" behavior, - pass "edgecolors='k'". - MGD - -2012-05-22 Added radius kwarg to pie charts. - HH - -2012-05-22 Collections now have a setting "offset_position" to select whether - the offsets are given in "screen" coordinates (default, - following the old behavior) or "data" coordinates. This is currently - used internally to improve the performance of hexbin. - - As a result, the "draw_path_collection" backend methods have grown - a new argument "offset_position". - MGD - -2012-05-04 Add a new argument to pie charts - startingangle - that - allows one to specify the angle offset for the first wedge - of the chart. - EP - -2012-05-03 symlog scale now obeys the logarithmic base. Previously, it was - completely ignored and always treated as base e. - MGD - -2012-05-03 Allow linscalex/y keyword to symlog scale that allows the size of - the linear portion relative to the logarithmic portion to be - adjusted. - MGD - -2012-04-14 Added new plot style: stackplot. This new feature supports stacked - area plots. - Damon McDougall - -2012-04-06 When path clipping changes a LINETO to a MOVETO, it also - changes any CLOSEPOLY command to a LINETO to the initial - point. This fixes a problem with pdf and svg where the - CLOSEPOLY would then draw a line to the latest MOVETO - position instead of the intended initial position. - JKS - -2012-03-27 Add support to ImageGrid for placing colorbars only at - one edge of each column/row. - RMM - -2012-03-07 Refactor movie writing into useful classes that make use - of pipes to write image data to ffmpeg or mencoder. Also - improve settings for these and the ability to pass custom - options. - RMM - -2012-02-29 errorevery keyword added to errorbar to enable errorbar - subsampling. fixes issue #600. - -2012-02-28 Added plot_trisurf to the mplot3d toolkit. This supports plotting - three dimensional surfaces on an irregular grid. - Damon McDougall - -2012-01-23 The radius labels in polar plots no longer use a fixed - padding, but use a different alignment depending on the - quadrant they are in. This fixes numerical problems when - (rmax - rmin) gets too small. - MGD - -2012-01-08 Add axes.streamplot to plot streamlines of a velocity field. - Adapted from Tom Flannaghan streamplot implementation. -TSY - -2011-12-29 ps and pdf markers are now stroked only if the line width - is nonzero for consistency with agg, fixes issue #621. - JKS - -2011-12-27 Work around an EINTR bug in some versions of subprocess. - JKS - -2011-10-25 added support for \operatorname to mathtext, - including the ability to insert spaces, such as - $\operatorname{arg\,max}$ - PI - -2011-08-18 Change api of Axes.get_tightbbox and add an optional - keyword parameter *call_axes_locator*. - JJL - -2011-07-29 A new rcParam "axes.formatter.use_locale" was added, that, - when True, will use the current locale to format tick - labels. This means that, for example, in the fr_FR locale, - ',' will be used as a decimal separator. - MGD - -2011-07-15 The set of markers available in the plot() and scatter() - commands has been unified. In general, this gives more - options to both than were previously available, however, - there is one backward-incompatible change to the markers in - scatter: - - "d" used to mean "diamond", it now means "narrow - diamond". "D" can be used for a "diamond". - - -MGD - -2011-07-13 Fix numerical problems in symlog scale, particularly when - linthresh <= 1.0. Symlog plots may look different if one - was depending on the old broken behavior - MGD - -2011-07-10 Fixed argument handling error in tripcolor/triplot/tricontour, - issue #203. - IMT - -2011-07-08 Many functions added to mplot3d.axes3d to bring Axes3D - objects more feature-parity with regular Axes objects. - Significant revisions to the documentation as well. - - BVR - -2011-07-07 Added compatibility with IPython strategy for picking - a version of Qt4 support, and an rcParam for making - the choice explicitly: backend.qt4. - EF - -2011-07-07 Modified AutoMinorLocator to improve automatic choice of - the number of minor intervals per major interval, and - to allow one to specify this number via a kwarg. - EF - -2011-06-28 3D versions of scatter, plot, plot_wireframe, plot_surface, - bar3d, and some other functions now support empty inputs. - BVR - -2011-06-22 Add set_theta_offset, set_theta_direction and - set_theta_zero_location to polar axes to control the - location of 0 and directionality of theta. - MGD - -2011-06-22 Add axes.labelweight parameter to set font weight to axis - labels - MGD. - -2011-06-20 Add pause function to pyplot. - EF - -2011-06-16 Added *bottom* keyword parameter for the stem command. - Also, implemented a legend handler for the stem plot. - - JJL - -2011-06-16 Added legend.frameon rcParams. - Mike Kaufman - -2011-05-31 Made backend_qt4 compatible with PySide . - Gerald Storer - -2011-04-17 Disable keyboard auto-repeat in qt4 backend by ignoring - key events resulting from auto-repeat. This makes - constrained zoom/pan work. - EF - -2011-04-14 interpolation="nearest" always interpolate images. A new - mode "none" is introduced for no interpolation - JJL - -2011-04-03 Fixed broken pick interface to AsteriskCollection objects - used by scatter. - EF - -2011-04-01 The plot directive Sphinx extension now supports all of the - features in the Numpy fork of that extension. These - include doctest formatting, an 'include-source' option, and - a number of new configuration options. - MGD - -2011-03-29 Wrapped ViewVCCachedServer definition in a factory function. - This class now inherits from urllib2.HTTPSHandler in order - to fetch data from github, but HTTPSHandler is not defined - if python was built without SSL support. - DSD - -2011-03-10 Update pytz version to 2011c, thanks to Simon Cross. - JKS - -2011-03-06 Add standalone tests.py test runner script. - JKS - -2011-03-06 Set edgecolor to 'face' for scatter asterisk-type - symbols; this fixes a bug in which these symbols were - not responding to the c kwarg. The symbols have no - face area, so only the edgecolor is visible. - EF - -2011-02-27 Support libpng version 1.5.x; suggestion by Michael - Albert. Changed installation specification to a - minimum of libpng version 1.2. - EF - -2011-02-20 clabel accepts a callable as an fmt kwarg; modified - patch by Daniel Hyams. - EF - -2011-02-18 scatter([], []) is now valid. Also fixed issues - with empty collections - BVR - -2011-02-07 Quick workaround for dviread bug #3175113 - JKS - -2011-02-05 Add cbook memory monitoring for Windows, using - tasklist. - EF - -2011-02-05 Speed up Normalize and LogNorm by using in-place - operations and by using float32 for float32 inputs - and for ints of 2 bytes or shorter; based on - patch by Christoph Gohlke. - EF - -2011-02-04 Changed imshow to use rgba as uint8 from start to - finish, instead of going through an intermediate - step as double precision; thanks to Christoph Gohlke. - EF - -2011-01-13 Added zdir and offset arguments to contourf3d to - bring contourf3d in feature parity with contour3d. - BVR - -2011-01-04 Tag 1.0.1 for release at r8896 - -2011-01-03 Added display of ticker offset to 3d plots. - BVR - -2011-01-03 Turn off tick labeling on interior subplots for - pyplots.subplots when sharex/sharey is True. - JDH - -2010-12-29 Implement axes_divider.HBox and VBox. -JJL - - -2010-11-22 Fixed error with Hammer projection. - BVR - -2010-11-12 Fixed the placement and angle of axis labels in 3D plots. - BVR - -2010-11-07 New rc parameters examples.download and examples.directory - allow bypassing the download mechanism in get_sample_data. - - JKS - -2010-10-04 Fix JPEG saving bug: only accept the kwargs documented - by PIL for JPEG files. - JKS - -2010-09-15 Remove unused _wxagg extension and numerix.h. - EF - -2010-08-25 Add new framework for doing animations with examples.- RM - -2010-08-21 Remove unused and inappropriate methods from Tick classes: - set_view_interval, get_minpos, and get_data_interval are - properly found in the Axis class and don't need to be - duplicated in XTick and YTick. - EF - -2010-08-21 Change Axis.set_view_interval() so that when updating an - existing interval, it respects the orientation of that - interval, and can enlarge but not reduce the interval. - This fixes a bug in which Axis.set_ticks would - change the view limits of an inverted axis. Whether - set_ticks should be affecting the viewLim at all remains - an open question. - EF - -2010-08-16 Handle NaN's correctly in path analysis routines. Fixes a - bug where the best location for a legend was not calculated - correctly when the line contains NaNs. - MGD - -2010-08-14 Fix bug in patch alpha handling, and in bar color kwarg - EF - -2010-08-12 Removed all traces of numerix module after 17 months of - deprecation warnings. - EF - -2010-08-05 Added keyword arguments 'thetaunits' and 'runits' for polar - plots. Fixed PolarAxes so that when it set default - Formatters, it marked them as such. Fixed semilogx and - semilogy to no longer blindly reset the ticker information - on the non-log axis. Axes.arrow can now accept unitized - data. - JRE - -2010-08-03 Add support for MPLSETUPCFG variable for custom setup.cfg - filename. Used by sage buildbot to build an mpl w/ no gui - support - JDH - -2010-08-01 Create directory specified by MPLCONFIGDIR if it does - not exist. - ADS - -2010-07-20 Return Qt4's default cursor when leaving the canvas - DSD - -2010-07-06 Tagging for mpl 1.0 at r8502 - - -2010-07-05 Added Ben Root's patch to put 3D plots in arbitrary axes, - allowing you to mix 3d and 2d in different axes/subplots or - to have multiple 3D plots in one figure. See - examples/mplot3d/subplot3d_demo.py - JDH - -2010-07-05 Preferred kwarg names in set_xlim are now 'left' and - 'right'; in set_ylim, 'bottom' and 'top'; original - kwargs are still accepted without complaint. - EF - -2010-07-05 TkAgg and FltkAgg backends are now consistent with other - interactive backends: when used in scripts from the - command line (not from ipython -pylab), show blocks, - and can be called more than once. - EF - -2010-07-02 Modified CXX/WrapPython.h to fix "swab bug" on solaris so - mpl can compile on Solaris with CXX6 in the trunk. Closes - tracker bug 3022815 - JDH - -2010-06-30 Added autoscale convenience method and corresponding - pyplot function for simplified control of autoscaling; - and changed axis, set_xlim, and set_ylim so that by - default, they turn off the autoscaling on the relevent - axis or axes. Therefore one can call set_xlim before - plotting a line, for example, and the limits will be - retained. - EF - -2010-06-20 Added Axes.tick_params and corresponding pyplot function - to control tick and tick label appearance after an Axes - has been created. - EF - -2010-06-09 Allow Axes.grid to control minor gridlines; allow - Axes.grid and Axis.grid to control major and minor - gridlines in the same method call. - EF - -2010-06-06 Change the way we do split/dividend adjustments in - finance.py to handle dividends and fix the zero division bug reported - in sf bug 2949906 and 2123566. Note that volume is not adjusted - because the Yahoo CSV does not distinguish between share - split and dividend adjustments making it near impossible to - get volume adjustement right (unless we want to guess based - on the size of the adjustment or scrape the html tables, - which we don't) - JDH - -2010-06-06 Updated dateutil to 1.5 and pytz to 2010h. - -2010-06-02 Add error_kw kwarg to Axes.bar(). - EF - -2010-06-01 Fix pcolormesh() and QuadMesh to pass on kwargs as - appropriate. - RM - -2010-05-18 Merge mpl_toolkits.gridspec into the main tree. - JJL - -2010-05-04 Improve backend_qt4 so it displays figures with the - correct size - DSD - -2010-04-20 Added generic support for connecting to a timer for events. This - adds TimerBase, TimerGTK, TimerQT, TimerWx, and TimerTk to - the backends and a new_timer() method to each backend's - canvas to allow ease of creating a new timer. - RM - -2010-04-20 Added margins() Axes method and pyplot function. - EF - -2010-04-18 update the axes_grid documentation. -JJL - -2010-04-18 Control MaxNLocator parameters after instantiation, - and via Axes.locator_params method, with corresponding - pyplot function. -EF - -2010-04-18 Control ScalarFormatter offsets directly and via the - Axes.ticklabel_format() method, and add that to pyplot. -EF - -2010-04-16 Add a close_event to the backends. -RM - -2010-04-06 modify axes_grid examples to use axes_grid1 and axisartist. -JJL - -2010-04-06 rebase axes_grid using axes_grid1 and axisartist modules. -JJL - -2010-04-06 axes_grid toolkit is splitted into two separate modules, - axes_grid1 and axisartist. -JJL - -2010-04-05 Speed up import: import pytz only if and when it is - needed. It is not needed if the rc timezone is UTC. - EF - -2010-04-03 Added color kwarg to Axes.hist(), based on work by - Jeff Klukas. - EF - -2010-03-24 refactor colorbar code so that no cla() is necessary when - mappable is changed. -JJL - -2010-03-22 fix incorrect rubber band during the zoom mode when mouse - leaves the axes. -JJL - -2010-03-21 x/y key during the zoom mode only changes the x/y limits. -JJL - -2010-03-20 Added pyplot.sca() function suggested by JJL. - EF - -2010-03-20 Added conditional support for new Tooltip API in gtk backend. - EF - -2010-03-20 Changed plt.fig_subplot() to plt.subplots() after discussion on - list, and changed its API to return axes as a numpy object array - (with control of dimensions via squeeze keyword). FP. - -2010-03-13 Manually brought in commits from branch - - ------------------------------------------------------------------------ - r8191 | leejjoon | 2010-03-13 17:27:57 -0500 (Sat, 13 Mar 2010) | 1 line - - fix the bug that handles for scatter are incorrectly set when dpi!=72. - Thanks to Ray Speth for the bug report. - - -2010-03-03 Manually brought in commits from branch via diff/patch - (svnmerge is broken) - - ------------------------------------------------------------------------ - r8175 | leejjoon | 2010-03-03 10:03:30 -0800 (Wed, 03 Mar 2010) | 1 line - - fix arguments of allow_rasterization.draw_wrapper - ------------------------------------------------------------------------ - r8174 | jdh2358 | 2010-03-03 09:15:58 -0800 (Wed, 03 Mar 2010) | 1 line - - added support for favicon in docs build - ------------------------------------------------------------------------ - r8173 | jdh2358 | 2010-03-03 08:56:16 -0800 (Wed, 03 Mar 2010) | 1 line - - applied Mattias get_bounds patch - ------------------------------------------------------------------------ - r8172 | jdh2358 | 2010-03-03 08:31:42 -0800 (Wed, 03 Mar 2010) | 1 line - - fix svnmerge download instructions - ------------------------------------------------------------------------ - r8171 | jdh2358 | 2010-03-03 07:47:48 -0800 (Wed, 03 Mar 2010) | 1 line - - - -2010-02-25 add annotation_demo3.py that demonstrates new functionality. -JJL - -2010-02-25 refactor Annotation to support arbitrary Transform as xycoords - or textcoords. Also, if a tuple of two coordinates is provided, - they are interpreted as coordinates for each x and y position. - -JJL - -2010-02-24 Added pyplot.fig_subplot(), to create a figure and a group of - subplots in a single call. This offers an easier pattern than - manually making figures and calling add_subplot() multiple times. FP - -2010-02-17 Added Gokhan's and Mattias' customizable keybindings patch - for the toolbar. You can now set the keymap.* properties - in the matplotlibrc file. Newbindings were added for - toggling log scaling on the x-axis. JDH - -2010-02-16 Committed TJ's filled marker patch for - left|right|bottom|top|full filled markers. See - examples/pylab_examples/filledmarker_demo.py. JDH - -2010-02-11 Added 'bootstrap' option to boxplot. This allows bootstrap - estimates of median confidence intervals. Based on an - initial patch by Paul Hobson. - ADS - -2010-02-06 Added setup.cfg "basedirlist" option to override setting - in setupext.py "basedir" dictionary; added "gnu0" - platform requested by Benjamin Drung. - EF - -2010-02-06 Added 'xy' scaling option to EllipseCollection. - EF - -2010-02-03 Made plot_directive use a custom PlotWarning category, so that - warnings can be turned into fatal errors easily if desired. - FP - -2010-01-29 Added draggable method to Legend to allow mouse drag - placement. Thanks Adam Fraser. JDH - -2010-01-25 Fixed a bug reported by Olle Engdegard, when using - histograms with stepfilled and log=True - MM - -2010-01-16 Upgraded CXX to 6.1.1 - JDH - -2009-01-16 Don't create minor ticks on top of existing major - ticks. Patch by Neil Crighton. -ADS - -2009-01-16 Ensure three minor ticks always drawn (SF# 2924245). Patch - by Neil Crighton. -ADS - -2010-01-16 Applied patch by Ian Thomas to fix two contouring - problems: now contourf handles interior masked regions, - and the boundaries of line and filled contours coincide. - EF - -2009-01-11 The color of legend patch follows the rc parameters - axes.facecolor and axes.edgecolor. -JJL - -2009-01-11 adjustable of Axes can be "box-forced" which allow - sharing axes. -JJL - -2009-01-11 Add add_click and pop_click methods in - BlockingContourLabeler. -JJL - - -2010-01-03 Added rcParams['axes.color_cycle'] - EF - -2010-01-03 Added Pierre's qt4 formlayout editor and toolbar button - JDH - -2009-12-31 Add support for using math text as marker symbols (Thanks to tcb) - - MGD - -2009-12-31 Commit a workaround for a regression in PyQt4-4.6.{0,1} - DSD - -2009-12-22 Fix cmap data for gist_earth_r, etc. -JJL - -2009-12-20 spines: put spines in data coordinates, add set_bounds() - call. -ADS - -2009-12-18 Don't limit notch size in boxplot to q1-q3 range, as this - is effectively making the data look better than it is. - ADS - -2009-12-18 mlab.prctile handles even-length data, such that the median - is the mean of the two middle values. - ADS - -2009-12-15 Add raw-image (unsampled) support for the ps backend. - JJL - -2009-12-14 Add patch_artist kwarg to boxplot, but keep old default. - Convert boxplot_demo2.py to use the new patch_artist. - ADS - -2009-12-06 axes_grid: reimplemented AxisArtist with FloatingAxes support. - Added new examples. - JJL - -2009-12-01 Applied Laurent Dufrechou's patch to improve blitting with - the qt4 backend - DSD - -2009-11-13 The pdf backend now allows changing the contents of - a pdf file's information dictionary via PdfPages.infodict. - JKS - -2009-11-12 font_manager.py should no longer cause EINTR on Python 2.6 - (but will on the 2.5 version of subprocess). Also the - fc-list command in that file was fixed so now it should - actually find the list of fontconfig fonts. - JKS - -2009-11-10 Single images, and all images in renderers with - option_image_nocomposite (i.e. agg, macosx and the svg - backend when rcParams['svg.image_noscale'] is True), are - now drawn respecting the zorder relative to other - artists. (Note that there may now be inconsistencies across - backends when more than one image is drawn at varying - zorders, but this change introduces correct behavior for - the backends in which it's easy to do so.) - -2009-10-21 Make AutoDateLocator more configurable by adding options - to control the maximum and minimum number of ticks. Also - add control of the intervals to be used for ticking. This - does not change behavior but opens previously hard-coded - behavior to runtime modification`. - RMM - -2009-10-19 Add "path_effects" support for Text and Patch. See - examples/pylab_examples/patheffect_demo.py -JJL - -2009-10-19 Add "use_clabeltext" option to clabel. If True, clabels - will be created with ClabelText class, which recalculates - rotation angle of the label during the drawing time. -JJL - -2009-10-16 Make AutoDateFormatter actually use any specified - timezone setting.This was only working correctly - when no timezone was specified. - RMM - -2009-09-27 Beginnings of a capability to test the pdf backend. - JKS - -2009-09-27 Add a savefig.extension rcparam to control the default - filename extension used by savefig. - JKS - -=============================================== -2009-09-21 Tagged for release 0.99.1 - -2009-09-20 Fix usetex spacing errors in pdf backend. - JKS - -2009-09-20 Add Sphinx extension to highlight IPython console sessions, - originally authored (I think) by Michael Droetboom. - FP - -2009-09-20 Fix off-by-one error in dviread.Tfm, and additionally protect - against exceptions in case a dvi font is missing some metrics. - JKS - -2009-09-15 Implement draw_text and draw_tex method of backend_base using - the textpath module. Implement draw_tex method of the svg - backend. - JJL - -2009-09-15 Don't fail on AFM files containing floating-point bounding boxes - JKS - -2009-09-13 AxesGrid : add modified version of colorbar. Add colorbar - location howto. - JJL - -2009-09-07 AxesGrid : implemented axisline style. - Added a demo examples/axes_grid/demo_axisline_style.py- JJL - -2009-09-04 Make the textpath class as a separate moduel - (textpath.py). Add support for mathtext and tex.- JJL - -2009-09-01 Added support for Gouraud interpolated triangles. - pcolormesh now accepts shading='gouraud' as an option. - MGD - -2009-08-29 Added matplotlib.testing package, which contains a Nose - plugin and a decorator that lets tests be marked as - KnownFailures - ADS - -2009-08-20 Added scaled dict to AutoDateFormatter for customized - scales - JDH - -2009-08-15 Pyplot interface: the current image is now tracked at the - figure and axes level, addressing tracker item 1656374. - EF - -2009-08-15 Docstrings are now manipulated with decorators defined - in a new module, docstring.py, thanks to Jason Coombs. - EF - -2009-08-14 Add support for image filtering for agg back end. See the example - demo_agg_filter.py. -JJL - -2009-08-09 AnnotationBbox added. Similar to Annotation, but works with - OffsetBox instead of Text. See the example - demo_annotation_box.py. -JJL - -2009-08-07 BboxImage implemented. Two examples, demo_bboximage.py and - demo_ribbon_box.py added. - JJL - -2009-08-07 In an effort to simplify the backend API, all clipping rectangles - and paths are now passed in using GraphicsContext objects, even - on collections and images. Therefore: - - draw_path_collection(self, master_transform, cliprect, clippath, - clippath_trans, paths, all_transforms, offsets, - offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds, urls) - - becomes: - - draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls) - - - - draw_quad_mesh(self, master_transform, cliprect, clippath, - clippath_trans, meshWidth, meshHeight, coordinates, - offsets, offsetTrans, facecolors, antialiased, - showedges) - - becomes: - - draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, - coordinates, offsets, offsetTrans, facecolors, - antialiased, showedges) - - - - draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None) - - becomes: - - draw_image(self, gc, x, y, im) - - - MGD - -2009-08-06 Tagging the 0.99.0 release at svn r7397 - JDH - - * fixed an alpha colormapping bug posted on sf 2832575 - - * fix typo in axes_divider.py. use nanmin, nanmax in angle_helper.py - (patch by Christoph Gohlke) - - * remove dup gui event in enter/leave events in gtk - - * lots of fixes for os x binaries (Thanks Russell Owen) - - * attach gtk events to mpl events -- fixes sf bug 2816580 - - * applied sf patch 2815064 (middle button events for wx) and - patch 2818092 (resize events for wx) - - * fixed boilerplate.py so it doesn't break the ReST docs. - - * removed a couple of cases of mlab.load - - * fixed rec2csv win32 file handle bug from sf patch 2831018 - - * added two examples from Josh Hemann: examples/pylab_examples/barchart_demo2.py - and examples/pylab_examples/boxplot_demo2.py - - * handled sf bugs 2831556 and 2830525; better bar error messages and - backend driver configs - - * added miktex win32 patch from sf patch 2820194 - - * apply sf patches 2830233 and 2823885 for osx setup and 64 bit; thanks Michiel - -2009-08-04 Made cbook.get_sample_data make use of the ETag and Last-Modified - headers of mod_dav_svn. - JKS - -2009-08-03 Add PathCollection; modify contourf to use complex - paths instead of simple paths with cuts. - EF - - -2009-08-03 Fixed boilerplate.py so it doesn't break the ReST docs. - JKS - -2009-08-03 pylab no longer provides a load and save function. These - are available in matplotlib.mlab, or you can use - numpy.loadtxt and numpy.savetxt for text files, or np.save - and np.load for binary numpy arrays. - JDH - -2009-07-31 Added cbook.get_sample_data for urllib enabled fetching and - cacheing of data needed for examples. See - examples/misc/sample_data_demo.py - JDH - -2009-07-31 Tagging 0.99.0.rc1 at 7314 - MGD - -2009-07-30 Add set_cmap and register_cmap, and improve get_cmap, - to provide convenient handling of user-generated - colormaps. Reorganized _cm and cm modules. - EF - -2009-07-28 Quiver speed improved, thanks to tip by Ray Speth. -EF - -2009-07-27 Simplify argument handling code for plot method. -EF - -2009-07-25 Allow "plot(1, 2, 'r*')" to work. - EF - -2009-07-22 Added an 'interp' keyword to griddata so the faster linear - interpolation method can be chosen. Default is 'nn', so - default behavior (using natural neighbor method) is unchanged (JSW) - -2009-07-22 Improved boilerplate.py so that it generates the correct - signatures for pyplot functions. - JKS - -2009-07-19 Fixed the docstring of Axes.step to reflect the correct - meaning of the kwargs "pre" and "post" - See SF bug - https://sourceforge.net/tracker/index.php?func=detail&aid=2823304&group_id=80706&atid=560720 - - JDH - -2009-07-18 Fix support for hatches without color fills to pdf and svg - backends. Add an example of that to hatch_demo.py. - JKS - -2009-07-17 Removed fossils from swig version of agg backend. - EF - -2009-07-14 initial submission of the annotation guide. -JJL - -2009-07-14 axes_grid : minor improvements in anchored_artists and - inset_locator. -JJL - -2009-07-14 Fix a few bugs in ConnectionStyle algorithms. Add - ConnectionPatch class. -JJL - -2009-07-11 Added a fillstyle Line2D property for half filled markers - -- see examples/pylab_examples/fillstyle_demo.py JDH - -2009-07-08 Attempt to improve performance of qt4 backend, do not call - qApp.processEvents while processing an event. Thanks Ole - Streicher for tracking this down - DSD - -2009-06-24 Add withheader option to mlab.rec2csv and changed - use_mrecords default to False in mlab.csv2rec since this is - partially broken - JDH - -2009-06-24 backend_agg.draw_marker quantizes the main path (as in the - draw_path). - JJL - -2009-06-24 axes_grid: floating axis support added. - JJL - -2009-06-14 Add new command line options to backend_driver.py to support - running only some directories of tests - JKS - -2009-06-13 partial cleanup of mlab and its importation in pylab - EF - -2009-06-13 Introduce a rotation_mode property for the Text artist. See - examples/pylab_examples/demo_text_rotation_mode.py -JJL - -2009-06-07 add support for bz2 files per sf support request 2794556 - - JDH - -2009-06-06 added a properties method to the artist and inspector to - return a dict mapping property name -> value; see sf - feature request 2792183 - JDH - -2009-06-06 added Neil's auto minor tick patch; sf patch #2789713 - JDH - -2009-06-06 do not apply alpha to rgba color conversion if input is - already rgba - JDH - -2009-06-03 axes_grid : Initial check-in of curvelinear grid support. See - examples/axes_grid/demo_curvelinear_grid.py - JJL - -2009-06-01 Add set_color method to Patch - EF - -2009-06-01 Spine is now derived from Patch - ADS - -2009-06-01 use cbook.is_string_like() instead of isinstance() for spines - ADS - -2009-06-01 cla() support for spines - ADS - -2009-06-01 Removed support for gtk < 2.4. - EF - -2009-05-29 Improved the animation_blit_qt4 example, which was a mix - of the object-oriented and pylab interfaces. It is now - strictly object-oriented - DSD - -2009-05-28 Fix axes_grid toolkit to work with spine patch by ADS. - JJL - -2009-05-28 Applied fbianco's patch to handle scroll wheel events in - the qt4 backend - DSD - -2009-05-26 Add support for "axis spines" to have arbitrary location. -ADS - -2009-05-20 Add an empty matplotlibrc to the tests/ directory so that running - tests will use the default set of rcparams rather than the user's - config. - RMM - -2009-05-19 Axis.grid(): allow use of which='major,minor' to have grid - on major and minor ticks. -ADS - -2009-05-18 Make psd(), csd(), and cohere() wrap properly for complex/two-sided - versions, like specgram() (SF #2791686) - RMM - -2009-05-18 Fix the linespacing bug of multiline text (#1239682). See - examples/pylab_examples/multiline.py -JJL - -2009-05-18 Add *annotation_clip* attr. for text.Annotation class. - If True, annotation is only drawn when the annotated point is - inside the axes area. -JJL - -2009-05-17 Fix bug(#2749174) that some properties of minor ticks are - not conserved -JJL - -2009-05-17 applied Michiel's sf patch 2790638 to turn off gtk event - loop in setupext for pygtk>=2.15.10 - JDH - -2009-05-17 applied Michiel's sf patch 2792742 to speed up Cairo and - macosx collections; speedups can be 20x. Also fixes some - bugs in which gc got into inconsistent state - -====================================================================== - -2008-05-17 Release 0.98.5.3 at r7107 from the branch - JDH - -2009-05-13 An optional offset and bbox support in restore_bbox. - Add animation_blit_gtk2.py. -JJL - -2009-05-13 psfrag in backend_ps now uses baseline-alignment - when preview.sty is used ((default is - bottom-alignment). Also, a small api imporvement - in OffsetBox-JJL - -2009-05-13 When the x-coordinate of a line is monotonically - increasing, it is now automatically clipped at - the stage of generating the transformed path in - the draw method; this greatly speeds up zooming and - panning when one is looking at a short segment of - a long time series, for example. - EF - -2009-05-11 aspect=1 in log-log plot gives square decades. -JJL - -2009-05-08 clabel takes new kwarg, rightside_up; if False, labels - will not be flipped to keep them rightside-up. This - allows the use of clabel to make streamfunction arrows, - as requested by Evan Mason. - EF - -2009-05-07 'labelpad' can now be passed when setting x/y labels. This - allows controlling the spacing between the label and its - axis. - RMM - -2009-05-06 print_ps now uses mixed-mode renderer. Axes.draw rasterize - artists whose zorder smaller than rasterization_zorder. - -JJL - -2009-05-06 Per-artist Rasterization, originally by Eric Bruning. -JJ - -2009-05-05 Add an example that shows how to make a plot that updates - using data from another process. Thanks to Robert - Cimrman - RMM - -2009-05-05 Add Axes.get_legend_handles_labels method. - JJL - -2009-05-04 Fix bug that Text.Annotation is still drawn while set to - not visible. - JJL - -2009-05-04 Added TJ's fill_betweenx patch - JDH - -2009-05-02 Added options to plotfile based on question from - Joseph Smidt and patch by Matthias Michler. - EF - - -2009-05-01 Changed add_artist and similar Axes methods to - return their argument. - EF - -2009-04-30 Incorrect eps bbox for landscape mode fixed - JJL - -2009-04-28 Fixed incorrect bbox of eps output when usetex=True. - JJL - -2009-04-24 Changed use of os.open* to instead use subprocess.Popen. - os.popen* are deprecated in 2.6 and are removed in 3.0. - RMM - -2009-04-20 Worked on axes_grid documentation. Added - axes_grid.inset_locator. - JJL - -2009-04-17 Initial check-in of the axes_grid toolkit. - JJL - -2009-04-17 Added a support for bbox_to_anchor in - offsetbox.AnchoredOffsetbox. Improved a documentation. - - JJL - -2009-04-16 Fixed a offsetbox bug that multiline texts are not - correctly aligned. - JJL - -2009-04-16 Fixed a bug in mixed mode renderer that images produced by - an rasterizing backend are placed with incorrect size. - - JJL - -2009-04-14 Added Jonathan Taylor's Reinier Heeres' port of John - Porters' mplot3d to svn trunk. Package in - mpl_toolkits.mplot3d and demo is examples/mplot3d/demo.py. - Thanks Reiner - -2009-04-06 The pdf backend now escapes newlines and linefeeds in strings. - Fixes sf bug #2708559; thanks to Tiago Pereira for the report. - -2009-04-06 texmanager.make_dvi now raises an error if LaTeX failed to - create an output file. Thanks to Joao Luis Silva for reporting - this. - JKS - -2009-04-05 _png.read_png() reads 12 bit PNGs (patch from - Tobias Wood) - ADS - -2009-04-04 Allow log axis scale to clip non-positive values to - small positive value; this is useful for errorbars. - EF - -2009-03-28 Make images handle nan in their array argument. - A helper, cbook.safe_masked_invalid() was added. - EF - -2009-03-25 Make contour and contourf handle nan in their Z argument. - EF - -2009-03-20 Add AuxTransformBox in offsetbox.py to support some transformation. - anchored_text.py example is enhanced and renamed - (anchored_artists.py). - JJL - -2009-03-20 Add "bar" connection style for annotation - JJL - -2009-03-17 Fix bugs in edge color handling by contourf, found - by Jae-Joon Lee. - EF - -2009-03-14 Added 'LightSource' class to colors module for - creating shaded relief maps. shading_example.py - added to illustrate usage. - JSW - -2009-03-11 Ensure wx version >= 2.8; thanks to Sandro Tosi and - Chris Barker. - EF - -2009-03-10 Fix join style bug in pdf. - JKS - -2009-03-07 Add pyplot access to figure number list - EF - -2009-02-28 hashing of FontProperties accounts current rcParams - JJL - -2009-02-28 Prevent double-rendering of shared axis in twinx, twiny - EF - -2009-02-26 Add optional bbox_to_anchor argument for legend class - JJL - -2009-02-26 Support image clipping in pdf backend. - JKS - -2009-02-25 Improve tick location subset choice in FixedLocator. - EF - -2009-02-24 Deprecate numerix, and strip out all but the numpy - part of the code. - EF - -2009-02-21 Improve scatter argument handling; add an early error - message, allow inputs to have more than one dimension. - EF - -2009-02-16 Move plot_directive.py to the installed source tree. Add - support for inline code content - MGD - -2009-02-16 Move mathmpl.py to the installed source tree so it is - available to other projects. - MGD - -2009-02-14 Added the legend title support - JJL - -2009-02-10 Fixed a bug in backend_pdf so it doesn't break when the setting - pdf.use14corefonts=True is used. Added test case in - unit/test_pdf_use14corefonts.py. - NGR - -2009-02-08 Added a new imsave function to image.py and exposed it in - the pyplot interface - GR - -2009-02-04 Some reorgnization of the legend code. anchored_text.py - added as an example. - JJL - -2009-02-04 Add extent keyword arg to hexbin - ADS - -2009-02-04 Fix bug in mathtext related to \dots and \ldots - MGD - -2009-02-03 Change default joinstyle to round - MGD - -2009-02-02 Reduce number of marker XObjects in pdf output - JKS - -2009-02-02 Change default resolution on polar plot to 1 - MGD - -2009-02-02 Avoid malloc errors in ttconv for fonts that don't have - e.g., PostName (a version of Tahoma triggered this) - JKS - -2009-01-30 Remove support for pyExcelerator in exceltools -- use xlwt - instead - JDH - -2009-01-29 Document 'resolution' kwarg for polar plots. Support it - when using pyplot.polar, not just Figure.add_axes. - MGD - -2009-01-29 Rework the nan-handling/clipping/quantizing/simplification - framework so each is an independent part of a pipeline. - Expose the C++-implementation of all of this so it can be - used from all Python backends. Add rcParam - "path.simplify_threshold" to control the threshold of - similarity below which vertices will be removed. - -2009-01-26 Improved tight bbox option of the savefig. - JJL - -2009-01-26 Make curves and NaNs play nice together - MGD - -2009-01-21 Changed the defaults of acorr and xcorr to use - usevlines=True, maxlags=10 and normed=True since these are - the best defaults - -2009-01-19 Fix bug in quiver argument handling. - EF - -2009-01-19 Fix bug in backend_gtk: don't delete nonexistent toolbar. - EF - -2009-01-16 Implement bbox_inches option for savefig. If bbox_inches is - "tight", try to determine the tight bounding box. - JJL - -2009-01-16 Fix bug in is_string_like so it doesn't raise an - unnecessary exception. - EF - -2009-01-16 Fix an infinite recursion in the unit registry when searching - for a converter for a sequence of strings. Add a corresponding - test. - RM - -2009-01-16 Bugfix of C typedef of MPL_Int64 that was failing on - Windows XP 64 bit, as reported by George Goussard on numpy - mailing list. - ADS - -2009-01-16 Added helper function LinearSegmentedColormap.from_list to - facilitate building simple custom colomaps. See - examples/pylab_examples/custom_cmap_fromlist.py - JDH - -2009-01-16 Applied Michiel's patch for macosx backend to fix rounding - bug. Closed sf bug 2508440 - JSW - -2009-01-10 Applied Michiel's hatch patch for macosx backend and - draw_idle patch for qt. Closes sf patched 2497785 and - 2468809 - JDH - -2009-01-10 Fix bug in pan/zoom with log coordinates. - EF - -2009-01-06 Fix bug in setting of dashed negative contours. - EF - -2009-01-06 Be fault tolerant when len(linestyles)>NLev in contour. - MM - -2009-01-06 Added marginals kwarg to hexbin to plot marginal densities - JDH - -2009-01-06 Change user-visible multipage pdf object to PdfPages to - avoid accidents with the file-like PdfFile. - JKS - -2009-01-05 Fix a bug in pdf usetex: allow using non-embedded fonts. - JKS - -2009-01-05 optional use of preview.sty in usetex mode. - JJL - -2009-01-02 Allow multipage pdf files. - JKS - -2008-12-31 Improve pdf usetex by adding support for font effects - (slanting and extending). - JKS - -2008-12-29 Fix a bug in pdf usetex support, which occurred if the same - Type-1 font was used with different encodings, e.g., with - Minion Pro and MnSymbol. - JKS - -2008-12-20 fix the dpi-dependent offset of Shadow. - JJL - -2008-12-20 fix the hatch bug in the pdf backend. minor update - in docs and example - JJL - -2008-12-19 Add axes_locator attribute in Axes. Two examples are added. - - JJL - -2008-12-19 Update Axes.legend documnetation. /api/api_changes.rst is also - updated to describe chages in keyword parameters. - Issue a warning if old keyword parameters are used. - JJL - -2008-12-18 add new arrow style, a line + filled triangles. -JJL - -================================================================== -2008-12-18 Re-Released 0.98.5.2 from v0_98_5_maint at r6679 - Released 0.98.5.2 from v0_98_5_maint at r6667 - -2008-12-18 Removed configobj, experimental traits and doc/mpl_data link - JDH - -2008-12-18 Fix bug where a line with NULL data limits prevents - subsequent data limits from calculating correctly - MGD - -2008-12-17 Major documentation generator changes - MGD - -2008-12-17 Applied macosx backend patch with support for path - collections, quadmesh, etc... - JDH - -2008-12-17 fix dpi-dependent behavior of text bbox and arrow in annotate - -JJL - -2008-12-17 Add group id support in artist. Two examples which - demostrate svg filter are added. -JJL - -2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL - -2008-12-16 Fixed dpi-dependent behavior of Legend and fancybox in Text. - -2008-12-16 Added markevery property to Line2D to support subsampling - of markers - JDH -2008-12-15 Removed mpl_data symlink in docs. On platforms that do not - support symlinks, these become copies, and the font files - are large, so the distro becomes unneccessarily bloaded. - Keeping the mpl_examples dir because relative links are - harder for the plot directive and the *.py files are not so - large. - JDH - -2008-12-15 Fix \$ in non-math text with usetex off. Document - differences between usetex on/off - MGD - -2008-12-15 Fix anti-aliasing when auto-snapping - MGD - -2008-12-15 Fix grid lines not moving correctly during pan and zoom - MGD - -2008-12-12 Preparations to eliminate maskedarray rcParams key: its - use will now generate a warning. Similarly, importing - the obsolote numerix.npyma will generate a warning. - EF - -2008-12-12 Added support for the numpy.histogram() weights parameter - to the axes hist() method. Docs taken from numpy - MM - -2008-12-12 Fixed warning in hist() with numpy 1.2 - MM - -2008-12-12 Removed external packages: configobj and enthought.traits - which are only required by the experimental traited config - and are somewhat out of date. If needed, install them - independently, see: - - http://code.enthought.com/projects/traits - - and: - - http://www.voidspace.org.uk/python/configobj.html - -2008-12-12 Added support to asign labels to histograms of multiple - data. - MM - -================================================================= -2008-12-11 Released 0.98.5 at svn r6573 - -2008-12-11 Use subprocess.Popen instead of os.popen in dviread - (Windows problem reported by Jorgen Stenarson) - JKS - -2008-12-10 Added Michael's font_manager fix and Jae-Joon's - figure/subplot fix. Bumped version number to 0.98.5 - JDH - -================================================================= -2008-12-09 Released 0.98.4 at svn r6536 - -2008-12-08 Added mdehoon's native macosx backend from sf patch 2179017 - JDH - -2008-12-08 Removed the prints in the set_*style commands. Return the - list of pprinted strings instead - JDH - -2008-12-08 Some of the changes Michael made to improve the output of - the property tables in the rest docs broke of made - difficult to use some of the interactive doc helpers, e.g., - setp and getp. Having all the rest markup in the ipython - shell also confused the docstrings. I added a new rc param - docstring.harcopy, to format the docstrings differently for - hardcopy and other use. Ther ArtistInspector could use a - little refactoring now since there is duplication of effort - between the rest out put and the non-rest output - JDH - -2008-12-08 Updated spectral methods (psd, csd, etc.) to scale one-sided - densities by a factor of 2 and, optionally, scale all densities - by the sampling frequency. This gives better MatLab - compatibility. -RM - -2008-12-08 Fixed alignment of ticks in colorbars. -MGD - -2008-12-07 drop the deprecated "new" keyword of np.histogram() for - numpy 1.2 or later. -JJL - -2008-12-06 Fixed a bug in svg backend that new_figure_manager() - ignores keywords arguments such as figsize, etc. -JJL - -2008-12-05 Fixed a bug that the handlelength of the new legend class - set too short when numpoints=1 -JJL - -2008-12-04 Added support for data with units (e.g., dates) to - Axes.fill_between. -RM - -2008-12-04 Added fancybox keyword to legend. Also applied some changes - for better look, including baseline adjustment of the - multiline texts so that it is center aligned. -JJL - -2008-12-02 The transmuter classes in the patches.py are reorganized as - subclasses of the Style classes. A few more box and arrow - styles are added. -JJL - -2008-12-02 Fixed a bug in the new legend class that didn't allowed - a tuple of coordinate vlaues as loc. -JJL - -2008-12-02 Improve checks for external dependencies, using subprocess - (instead of deprecated popen*) and distutils (for version - checking) - DSD - -2008-11-30 Reimplementaion of the legend which supports baseline alignement, - multi-column, and expand mode. - JJL - -2008-12-01 Fixed histogram autoscaling bug when bins or range are given - explicitly (fixes Debian bug 503148) - MM - -2008-11-25 Added rcParam axes.unicode_minus which allows plain hypen - for minus when False - JDH - -2008-11-25 Added scatterpoints support in Legend. patch by Erik - Tollerud - JJL - -2008-11-24 Fix crash in log ticking. - MGD - -2008-11-20 Added static helper method BrokenHBarCollection.span_where - and Axes/pyplot method fill_between. See - examples/pylab/fill_between.py - JDH - -2008-11-12 Add x_isdata and y_isdata attributes to Artist instances, - and use them to determine whether either or both - coordinates are used when updating dataLim. This is - used to fix autoscaling problems that had been triggered - by axhline, axhspan, axvline, axvspan. - EF - -2008-11-11 Update the psd(), csd(), cohere(), and specgram() methods - of Axes and the csd() cohere(), and specgram() functions - in mlab to be in sync with the changes to psd(). - In fact, under the hood, these all call the same core - to do computations. - RM - -2008-11-11 Add 'pad_to' and 'sides' parameters to mlab.psd() to - allow controlling of zero padding and returning of - negative frequency components, respecitively. These are - added in a way that does not change the API. - RM - -2008-11-10 Fix handling of c kwarg by scatter; generalize - is_string_like to accept numpy and numpy.ma string - array scalars. - RM and EF - -2008-11-09 Fix a possible EINTR problem in dviread, which might help - when saving pdf files from the qt backend. - JKS - -2008-11-05 Fix bug with zoom to rectangle and twin axes - MGD - -2008-10-24 Added Jae Joon's fancy arrow, box and annotation - enhancements -- see - examples/pylab_examples/annotation_demo2.py - -2008-10-23 Autoscaling is now supported with shared axes - EF - -2008-10-23 Fixed exception in dviread that happened with Minion - JKS - -2008-10-21 set_xlim, ylim now return a copy of the viewlim array to - avoid modify inplace surprises - -2008-10-20 Added image thumbnail generating function - matplotlib.image.thumbnail. See - examples/misc/image_thumbnail.py - JDH - -2008-10-20 Applied scatleg patch based on ideas and work by Erik - Tollerud and Jae-Joon Lee. - MM - -2008-10-11 Fixed bug in pdf backend: if you pass a file object for - output instead of a filename, e.g., in a wep app, we now - flush the object at the end. - JKS - -2008-10-08 Add path simplification support to paths with gaps. - EF - -2008-10-05 Fix problem with AFM files that don't specify the font's - full name or family name. - JKS - -2008-10-04 Added 'scilimits' kwarg to Axes.ticklabel_format() method, - for easy access to the set_powerlimits method of the - major ScalarFormatter. - EF - -2008-10-04 Experimental new kwarg borderpad to replace pad in legend, - based on suggestion by Jae-Joon Lee. - EF - -2008-09-27 Allow spy to ignore zero values in sparse arrays, based - on patch by Tony Yu. Also fixed plot to handle empty - data arrays, and fixed handling of markers in figlegend. - EF - -2008-09-24 Introduce drawstyles for lines. Transparently split linestyles - like 'steps--' into drawstyle 'steps' and linestyle '--'. - Legends always use drawstyle 'default'. - MM - -2008-09-18 Fixed quiver and quiverkey bugs (failure to scale properly - when resizing) and added additional methods for determining - the arrow angles - EF - -2008-09-18 Fix polar interpolation to handle negative values of theta - MGD - -2008-09-14 Reorganized cbook and mlab methods related to numerical - calculations that have little to do with the goals of those two - modules into a separate module numerical_methods.py - Also, added ability to select points and stop point selection - with keyboard in ginput and manual contour labeling code. - Finally, fixed contour labeling bug. - DMK - -2008-09-11 Fix backtick in Postscript output. - MGD - -2008-09-10 [ 2089958 ] Path simplification for vector output backends - Leverage the simplification code exposed through - path_to_polygons to simplify certain well-behaved paths in - the vector backends (PDF, PS and SVG). "path.simplify" - must be set to True in matplotlibrc for this to work. - - MGD - -2008-09-10 Add "filled" kwarg to Path.intersects_path and - Path.intersects_bbox. - MGD - -2008-09-07 Changed full arrows slightly to avoid an xpdf rendering - problem reported by Friedrich Hagedorn. - JKS - -2008-09-07 Fix conversion of quadratic to cubic Bezier curves in PDF - and PS backends. Patch by Jae-Joon Lee. - JKS - -2008-09-06 Added 5-point star marker to plot command - EF - -2008-09-05 Fix hatching in PS backend - MGD - -2008-09-03 Fix log with base 2 - MGD - -2008-09-01 Added support for bilinear interpolation in - NonUniformImage; patch by Gregory Lielens. - EF - -2008-08-28 Added support for multiple histograms with data of - different length - MM - -2008-08-28 Fix step plots with log scale - MGD - -2008-08-28 Fix masked arrays with markers in non-Agg backends - MGD - -2008-08-28 Fix clip_on kwarg so it actually works correctly - MGD - -2008-08-25 Fix locale problems in SVG backend - MGD - -2008-08-22 fix quiver so masked values are not plotted - JSW - -2008-08-18 improve interactive pan/zoom in qt4 backend on windows - DSD - -2008-08-11 Fix more bugs in NaN/inf handling. In particular, path simplification - (which does not handle NaNs or infs) will be turned off automatically - when infs or NaNs are present. Also masked arrays are now converted - to arrays with NaNs for consistent handling of masks and NaNs - - MGD and EF - -================================================================= -2008-08-03 Released 0.98.3 at svn r5947 - -2008-08-01 Backported memory leak fixes in _ttconv.cpp - MGD - -2008-07-31 Added masked array support to griddata. - JSW - -2008-07-26 Added optional C and reduce_C_function arguments to - axes.hexbin(). This allows hexbin to accumulate the values - of C based on the x,y coordinates and display in hexagonal - bins. - ADS - -2008-07-24 Deprecated (raise NotImplementedError) all the mlab2 - functions from matplotlib.mlab out of concern that some of - them were not clean room implementations. JDH - -2008-07-24 Rewrite of a significant portion of the clabel code (class - ContourLabeler) to improve inlining. - DMK - -2008-07-22 Added Barbs polygon collection (similar to Quiver) for plotting - wind barbs. Added corresponding helpers to Axes and pyplot as - well. (examples/pylab_examples/barb_demo.py shows it off.) - RMM - -2008-07-21 Added scikits.delaunay as matplotlib.delaunay. Added griddata - function in matplotlib.mlab, with example (griddata_demo.py) in - pylab_examples. griddata function will use mpl_toolkits._natgrid - if installed. - JSW - -2008-07-21 Re-introduced offset_copy that works in the context of the - new transforms. - MGD - -2008-07-21 Committed patch by Ryan May to add get_offsets and - set_offsets to Collections base class - EF - -2008-07-21 Changed the "asarray" strategy in image.py so that - colormapping of masked input should work for all - image types (thanks Klaus Zimmerman) - EF - -2008-07-20 Rewrote cbook.delete_masked_points and corresponding - unit test to support rgb color array inputs, datetime - inputs, etc. - EF - -2008-07-20 Renamed unit/axes_unit.py to cbook_unit.py and modified - in accord with Ryan's move of delete_masked_points from - axes to cbook. - EF - -2008-07-18 Check for nan and inf in axes.delete_masked_points(). - This should help hexbin and scatter deal with nans. - ADS - -2008-07-17 Added ability to manually select contour label locations. - Also added a waitforbuttonpress function. - DMK - -2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for - the report) - MGD - -2008-07-16 Improve error handling in texmanager, thanks to Ian Henry - for reporting - DSD - -2008-07-12 Added support for external backends with the - "module://my_backend" syntax - JDH - -2008-07-11 Fix memory leak related to shared axes. Grouper should - store weak references. - MGD - -2008-07-10 Bugfix: crash displaying fontconfig pattern - MGD - -2008-07-10 Bugfix: [ 2013963 ] update_datalim_bounds in Axes not works - MGD - -2008-07-10 Bugfix: [ 2014183 ] multiple imshow() causes gray edges - MGD - -2008-07-09 Fix rectangular axes patch on polar plots bug - MGD - -2008-07-09 Improve mathtext radical rendering - MGD - -2008-07-08 Improve mathtext superscript placement - MGD - -2008-07-07 Fix custom scales in pcolormesh (thanks Matthew Turk) - MGD - -2008-07-03 Implemented findobj method for artist and pyplot - see - examples/pylab_examples/findobj_demo.py - JDH - -2008-06-30 Another attempt to fix TextWithDash - DSD - -2008-06-30 Removed Qt4 NavigationToolbar2.destroy -- it appears to - have been unnecessary and caused a bug reported by P. - Raybaut - DSD - -2008-06-27 Fixed tick positioning bug - MM - -2008-06-27 Fix dashed text bug where text was at the wrong end of the - dash - MGD - -2008-06-26 Fix mathtext bug for expressions like $x_{\leftarrow}$ - MGD - -2008-06-26 Fix direction of horizontal/vertical hatches - MGD - -2008-06-25 Figure.figurePatch renamed Figure.patch, Axes.axesPatch - renamed Axes.patch, Axes.axesFrame renamed Axes.frame, - Axes.get_frame, which returns Axes.patch, is deprecated. - Examples and users guide updated - JDH - -2008-06-25 Fix rendering quality of pcolor - MGD - -================================================================= -2008-06-24 Released 0.98.2 at svn r5667 - (source only for debian) JDH - -2008-06-24 Added "transparent" kwarg to savefig. - MGD - -2008-06-24 Applied Stefan's patch to draw a single centered marker over - a line with numpoints==1 - JDH - -2008-06-23 Use splines to render circles in scatter plots - MGD - -=============================================================== -2008-06-22 Released 0.98.1 at revision 5637 - -2008-06-22 Removed axes3d support and replaced it with a - NotImplementedError for one release cycle - -2008-06-21 fix marker placement bug in backend_ps - DSD - -2008-06-20 [ 1978629 ] scale documentation missing/incorrect for log - MGD - -2008-06-20 Added closed kwarg to PolyCollection. Fixes bug [ 1994535 - ] still missing lines on graph with svn (r 5548). - MGD - -2008-06-20 Added set/get_closed method to Polygon; fixes error - in hist - MM - -2008-06-19 Use relative font sizes (e.g., 'medium' and 'large') in - rcsetup.py and matplotlibrc.template so that text will - be scaled by default when changing rcParams['font.size'] - - EF - -2008-06-17 Add a generic PatchCollection class that can contain any - kind of patch. - MGD - -2008-06-13 Change pie chart label alignment to avoid having labels - overwrite the pie - MGD - -2008-06-12 Added some helper functions to the mathtext parser to - return bitmap arrays or write pngs to make it easier to use - mathtext outside the context of an mpl figure. modified - the mathpng sphinxext to use the mathtext png save - functionality - see examples/api/mathtext_asarray.py - JDH - -2008-06-11 Use matplotlib.mathtext to render math expressions in - online docs - MGD - -2008-06-11 Move PNG loading/saving to its own extension module, and - remove duplicate code in _backend_agg.cpp and _image.cpp - that does the same thing - MGD - -2008-06-11 Numerous mathtext bugfixes, primarily related to - dpi-independence - MGD - -2008-06-10 Bar now applies the label only to the first patch only, and - sets '_nolegend_' for the other patch labels. This lets - autolegend work as expected for hist and bar - see - https://sourceforge.net/tracker/index.php?func=detail&aid=1986597&group_id=80706&atid=560720 - JDH - -2008-06-10 Fix text baseline alignment bug. [ 1985420 ] Repair of - baseline alignment in Text._get_layout. Thanks Stan West - - MGD - -2008-06-09 Committed Gregor's image resample patch to downsampling - images with new rcparam image.resample - JDH - -2008-06-09 Don't install Enthought.Traits along with matplotlib. For - matplotlib developers convenience, it can still be - installed by setting an option in setup.cfg while we figure - decide if there is a future for the traited config - DSD - -2008-06-09 Added range keyword arg to hist() - MM - -2008-06-07 Moved list of backends to rcsetup.py; made use of lower - case for backend names consistent; use validate_backend - when importing backends subpackage - EF - -2008-06-06 hist() revision, applied ideas proposed by Erik Tollerud and - Olle Engdegard: make histtype='step' unfilled by default - and introduce histtype='stepfilled'; use default color - cycle; introduce reverse cumulative histogram; new align - keyword - MM - -2008-06-06 Fix closed polygon patch and also provide the option to - not close the polygon - MGD - -2008-06-05 Fix some dpi-changing-related problems with PolyCollection, - as called by Axes.scatter() - MGD - -2008-06-05 Fix image drawing so there is no extra space to the right - or bottom - MGD - -2006-06-04 Added a figure title command suptitle as a Figure method - and pyplot command -- see examples/figure_title.py - JDH - -2008-06-02 Added support for log to hist with histtype='step' and fixed - a bug for log-scale stacked histograms - MM - -=============================================================== -2008-05-29 Released 0.98.0 at revision 5314 - -2008-05-29 matplotlib.image.imread now no longer always returns RGBA - -- if the image is luminance or RGB, it will return a MxN - or MxNx3 array if possible. Also uint8 is no longer always - forced to float. - -2008-05-29 Implement path clipping in PS backend - JDH - -2008-05-29 Fixed two bugs in texmanager.py: - improved comparison of dvipng versions - fixed a bug introduced when get_grey method was added - - DSD - -2008-05-28 Fix crashing of PDFs in xpdf and ghostscript when two-byte - characters are used with Type 3 fonts - MGD - -2008-05-28 Allow keyword args to configure widget properties as - requested in - http://sourceforge.net/tracker/index.php?func=detail&aid=1866207&group_id=80706&atid=560722 - - JDH - -2008-05-28 Replaced '-' with u'\u2212' for minus sign as requested in - http://sourceforge.net/tracker/index.php?func=detail&aid=1962574&group_id=80706&atid=560720 - -2008-05-28 zero width/height Rectangles no longer influence the - autoscaler. Useful for log histograms with empty bins - - JDH - -2008-05-28 Fix rendering of composite glyphs in Type 3 conversion - (particularly as evidenced in the Eunjin.ttf Korean font) - Thanks Jae-Joon Lee for finding this! - -2008-05-27 Rewrote the cm.ScalarMappable callback infrastructure to - use cbook.CallbackRegistry rather than custom callback - handling. Amy users of add_observer/notify of the - cm.ScalarMappable should uae the - cm.ScalarMappable.callbacksSM CallbackRegistry instead. JDH - -2008-05-27 Fix TkAgg build on Ubuntu 8.04 (and hopefully a more - general solution for other platforms, too.) - -2008-05-24 Added PIL support for loading images to imread (if PIL is - available) - JDH - -2008-05-23 Provided a function and a method for controlling the - plot color cycle. - EF - -2008-05-23 Major revision of hist(). Can handle 2D arrays and create - stacked histogram plots; keyword 'width' deprecated and - rwidth (relative width) introduced; align='edge' changed - to center of bin - MM - -2008-05-22 Added support for ReST-based doumentation using Sphinx. - Documents are located in doc/, and are broken up into - a users guide and an API reference. To build, run the - make.py files. Sphinx-0.4 is needed to build generate xml, - which will be useful for rendering equations with mathml, - use sphinx from svn until 0.4 is released - DSD - -2008-05-21 Fix segfault in TkAgg backend - MGD - -2008-05-21 Fix a "local variable unreferenced" bug in plotfile - MM - -2008-05-19 Fix crash when Windows can not access the registry to - determine font path [Bug 1966974, thanks Patrik Simons] - MGD - -2008-05-16 removed some unneeded code w/ the python 2.4 requirement. - cbook no longer provides compatibility for reversed, - enumerate, set or izip. removed lib/subprocess, mpl1, - sandbox/units, and the swig code. This stuff should remain - on the maintenance branch for archival purposes. JDH - -2008-05-16 Reorganized examples dir - JDH - -2008-05-16 Added 'elinewidth' keyword arg to errorbar, based on patch - by Christopher Brown - MM - -2008-05-16 Added 'cumulative' keyword arg to hist to plot cumulative - histograms. For normed hists, this is normalized to one - MM - -2008-05-15 Fix Tk backend segfault on some machines - MGD - -2008-05-14 Don't use stat on Windows (fixes font embedding problem) - MGD - -2008-05-09 Fix /singlequote (') in Postscript backend - MGD - -2008-05-08 Fix kerning in SVG when embedding character outlines - MGD - -2008-05-07 Switched to future numpy histogram semantic in hist - MM - -2008-05-06 Fix strange colors when blitting in QtAgg and Qt4Agg - MGD - -2008-05-05 pass notify_axes_change to the figure's add_axobserver - in the qt backends, like we do for the other backends. - Thanks Glenn Jones for the report - DSD - -2008-05-02 Added step histograms, based on patch by Erik Tollerud. - MM - -2008-05-02 On PyQt <= 3.14 there is no way to determine the underlying - Qt version. [1851364] - MGD - -2008-05-02 Don't call sys.exit() when pyemf is not found [1924199] - - MGD - -2008-05-02 Update _subprocess.c from upstream Python 2.5.2 to get a - few memory and reference-counting-related bugfixes. See - bug 1949978. - MGD - -2008-04-30 Added some record array editing widgets for gtk -- see - examples/rec_edit*.py - JDH - -2008-04-29 Fix bug in mlab.sqrtm - MM - -2008-04-28 Fix bug in SVG text with Mozilla-based viewers (the symbol - tag is not supported) - MGD - -2008-04-27 Applied patch by Michiel de Hoon to add hexbin - axes method and pyplot function - EF - -2008-04-25 Enforce python >= 2.4; remove subprocess build - EF - -2008-04-25 Enforce the numpy requirement at build time - JDH - -2008-04-24 Make numpy 1.1 and python 2.3 required when importing - matplotlib - EF - -2008-04-24 Fix compilation issues on VS2003 (Thanks Martin Spacek for - all the help) - MGD - -2008-04-24 Fix sub/superscripts when the size of the font has been - changed - MGD - -2008-04-22 Use "svg.embed_char_paths" consistently everywhere - MGD - -2008-04-20 Add support to MaxNLocator for symmetric axis autoscaling. - EF - -2008-04-20 Fix double-zoom bug. - MM - -2008-04-15 Speed up color mapping. - EF - -2008-04-12 Speed up zooming and panning of dense images. - EF - -2008-04-11 Fix global font rcParam setting after initialization - time. - MGD - -2008-04-11 Revert commits 5002 and 5031, which were intended to - avoid an unnecessary call to draw(). 5002 broke saving - figures before show(). 5031 fixed the problem created in - 5002, but broke interactive plotting. Unnecessary call to - draw still needs resolution - DSD - -2008-04-07 Improve color validation in rc handling, suggested - by Lev Givon - EF - -2008-04-02 Allow to use both linestyle definition arguments, '-' and - 'solid' etc. in plots/collections - MM - -2008-03-27 Fix saving to Unicode filenames with Agg backend - (other backends appear to already work...) - (Thanks, Christopher Barker) - MGD - -2008-03-26 Fix SVG backend bug that prevents copying and pasting in - Inkscape (thanks Kaushik Ghose) - MGD - -2008-03-24 Removed an unnecessary call to draw() in the backend_qt* - mouseReleaseEvent. Thanks to Ted Drain - DSD - -2008-03-23 Fix a pdf backend bug which sometimes caused the outermost - gsave to not be balanced with a grestore. - JKS - -2008-03-20 Fixed a minor bug in ContourSet._process_linestyles when - len(linestyles)==Nlev - MM - -2008-03-19 Changed ma import statements to "from numpy import ma"; - this should work with past and future versions of - numpy, whereas "import numpy.ma as ma" will work only - with numpy >= 1.05, and "import numerix.npyma as ma" - is obsolete now that maskedarray is replacing the - earlier implementation, as of numpy 1.05. - -2008-03-14 Removed an apparently unnecessary call to - FigureCanvasAgg.draw in backend_qt*agg. Thanks to Ted - Drain - DSD - -2008-03-10 Workaround a bug in backend_qt4agg's blitting due to a - buffer width/bbox width mismatch in _backend_agg's - copy_from_bbox - DSD - -2008-02-29 Fix class Wx toolbar pan and zoom functions (Thanks Jeff - Peery) - MGD - -2008-02-16 Added some new rec array functionality to mlab - (rec_summarize, rec2txt and rec_groupby). See - examples/rec_groupby_demo.py. Thanks to Tim M for rec2txt. - -2008-02-12 Applied Erik Tollerud's span selector patch - JDH - -2008-02-11 Update plotting() doc string to refer to getp/setp. - JKS - -2008-02-10 Fixed a problem with square roots in the pdf backend with - usetex. - JKS - -2008-02-08 Fixed minor __str__ bugs so getp(gca()) works. - JKS - -2008-02-05 Added getters for title, xlabel, ylabel, as requested - by Brandon Kieth - EF - -2008-02-05 Applied Gael's ginput patch and created - examples/ginput_demo.py - JDH - -2008-02-03 Expose interpnames, a list of valid interpolation - methods, as an AxesImage class attribute. - EF - -2008-02-03 Added BoundaryNorm, with examples in colorbar_only.py - and image_masked.py. - EF - -2008-02-03 Force dpi=72 in pdf backend to fix picture size bug. - JKS - -2008-02-01 Fix doubly-included font problem in Postscript backend - MGD - -2008-02-01 Fix reference leak in ft2font Glyph objects. - MGD - -2008-01-31 Don't use unicode strings with usetex by default - DSD - -2008-01-31 Fix text spacing problems in PDF backend with *some* fonts, - such as STIXGeneral. - -2008-01-31 Fix \sqrt with radical number (broken by making [ and ] - work below) - MGD - -2008-01-27 Applied Martin Teichmann's patch to improve the Qt4 - backend. Uses Qt's builtin toolbars and statusbars. - See bug 1828848 - DSD - -2008-01-10 Moved toolkits to mpl_toolkits, made mpl_toolkits - a namespace package - JSWHIT - -2008-01-10 Use setup.cfg to set the default parameters (tkagg, - numpy) when building windows installers - DSD - -2008-01-10 Fix bug displaying [ and ] in mathtext - MGD - -2008-01-10 Fix bug when displaying a tick value offset with scientific - notation. (Manifests itself as a warning that the \times - symbol can not be found). - MGD - -2008-01-10 Use setup.cfg to set the default parameters (tkagg, - numpy) when building windows installers - DSD - -=============================================================== -2008-01-06 Released 0.91.2 at revision 4802 - -2007-12-26 Reduce too-late use of matplotlib.use() to a warning - instead of an exception, for backwards compatibility - EF - -2007-12-25 Fix bug in errorbar, identified by Noriko Minakawa - EF - -2007-12-25 Changed masked array importing to work with the upcoming - numpy 1.05 (now the maskedarray branch) as well as with - earlier versions. - EF - -2007-12-16 rec2csv saves doubles without losing precision. Also, it - does not close filehandles passed in open. - JDH,ADS - -2007-12-13 Moved rec2gtk to matplotlib.toolkits.gtktools and rec2excel - to matplotlib.toolkits.exceltools - JDH - -2007-12-12 Support alpha-blended text in the Agg and Svg backends - - MGD - -2007-12-10 Fix SVG text rendering bug. - MGD - -2007-12-10 Increase accuracy of circle and ellipse drawing by using an - 8-piece bezier approximation, rather than a 4-piece one. - Fix PDF, SVG and Cairo backends so they can draw paths - (meaning ellipses as well). - MGD - -2007-12-07 Issue a warning when drawing an image on a non-linear axis. - MGD - -2007-12-06 let widgets.Cursor initialize to the lower x and y bounds - rather than 0,0, which can cause havoc for dates and other - transforms - DSD - -2007-12-06 updated references to mpl data directories for py2exe - DSD - -2007-12-06 fixed a bug in rcsetup, see bug 1845057 - DSD - -2007-12-05 Fix how fonts are cached to avoid loading the same one multiple times. - (This was a regression since 0.90 caused by the refactoring of - font_manager.py) - MGD - -2007-12-05 Support arbitrary rotation of usetex text in Agg backend. - MGD - -2007-12-04 Support '|' as a character in mathtext - MGD - -=============================================================== -2007-11-27 Released 0.91.1 at revision 4517 - -=============================================================== -2007-11-27 Released 0.91.0 at revision 4478 - -2007-11-13 All backends now support writing to a file-like object, not - just a regular file. savefig() can be passed a file-like - object in place of a file path. - MGD - -2007-11-13 Improved the default backend selection at build time: - SVG -> Agg -> TkAgg -> WXAgg -> GTK -> GTKAgg. The last usable - backend in this progression will be chosen in the default - config file. If a backend is defined in setup.cfg, that will - be the default backend - DSD - -2007-11-13 Improved creation of default config files at build time for - traited config package - DSD - -2007-11-12 Exposed all the build options in setup.cfg. These options are - read into a dict called "options" by setupext.py. Also, added - "-mpl" tags to the version strings for packages provided by - matplotlib. Versions provided by mpl will be identified and - updated on subsequent installs - DSD - -2007-11-12 Added support for STIX fonts. A new rcParam, - mathtext.fontset, can be used to choose between: - - 'cm': - The TeX/LaTeX Computer Modern fonts - - 'stix': - The STIX fonts (see stixfonts.org) - - 'stixsans': - The STIX fonts, using sans-serif glyphs by default - - 'custom': - A generic Unicode font, in which case the mathtext font - must be specified using mathtext.bf, mathtext.it, - mathtext.sf etc. - - Added a new example, stix_fonts_demo.py to show how to access - different fonts and unusual symbols. - - - MGD - -2007-11-12 Options to disable building backend extension modules moved - from setup.py to setup.cfg - DSD - -2007-11-09 Applied Martin Teichmann's patch 1828813: a QPainter is used in - paintEvent, which has to be destroyed using the method end(). If - matplotlib raises an exception before the call to end - and it - does if you feed it with bad data - this method end() is never - called and Qt4 will start spitting error messages - -2007-11-09 Moved pyparsing back into matplotlib namespace. Don't use - system pyparsing, API is too variable from one release - to the next - DSD - -2007-11-08 Made pylab use straight numpy instead of oldnumeric - by default - EF - -2007-11-08 Added additional record array utilites to mlab (rec2excel, - rec2gtk, rec_join, rec_append_field, rec_drop_field) - JDH - -2007-11-08 Updated pytz to version 2007g - DSD - -2007-11-08 Updated pyparsing to version 1.4.8 - DSD - -2007-11-08 Moved csv2rec to recutils and added other record array - utilities - JDH - -2007-11-08 If available, use existing pyparsing installation - DSD - -2007-11-07 Removed old enthought.traits from lib/matplotlib, added - Gael Varoquaux's enthought.traits-2.6b1, which is stripped - of setuptools. The package is installed to site-packages - if not already available - DSD - -2007-11-05 Added easy access to minor tick properties; slight mod - of patch by Pierre G-M - EF - -2007-11-02 Commited Phil Thompson's patch 1599876, fixes to Qt4Agg - backend and qt4 blitting demo - DSD - -2007-11-02 Commited Phil Thompson's patch 1599876, fixes to Qt4Agg - backend and qt4 blitting demo - DSD - -2007-10-31 Made log color scale easier to use with contourf; - automatic level generation now works. - EF - -2007-10-29 TRANSFORMS REFACTORING - - The primary goal of this refactoring was to make it easier - to extend matplotlib to support new kinds of projections. - This is primarily an internal improvement, and the possible - user-visible changes it allows are yet to come. - - The transformation framework was completely rewritten in - Python (with Numpy). This will make it easier to add news - kinds of transformations without writing C/C++ code. - - Transforms are composed into a 'transform tree', made of - transforms whose value depends on other transforms (their - children). When the contents of children change, their - parents are automatically updated to reflect those changes. - To do this an "invalidation" method is used: when children - change, all of their ancestors are marked as "invalid". - When the value of a transform is accessed at a later time, - its value is recomputed only if it is invalid, otherwise a - cached value may be used. This prevents unnecessary - recomputations of transforms, and contributes to better - interactive performance. - - The framework can be used for both affine and non-affine - transformations. However, for speed, we want use the - backend renderers to perform affine transformations - whenever possible. Therefore, it is possible to perform - just the affine or non-affine part of a transformation on a - set of data. The affine is always assumed to occur after - the non-affine. For any transform: - - full transform == non-affine + affine - - Much of the drawing has been refactored in terms of - compound paths. Therefore, many methods have been removed - from the backend interface and replaced with a a handful to - draw compound paths. This will make updating the backends - easier, since there is less to update. It also should make - the backends more consistent in terms of functionality. - - User visible changes: - - - POLAR PLOTS: Polar plots are now interactively zoomable, - and the r-axis labels can be interactively rotated. - Straight line segments are now interpolated to follow the - curve of the r-axis. - - - Non-rectangular clipping works in more backends and with - more types of objects. - - - Sharing an axis across figures is now done in exactly - the same way as sharing an axis between two axes in the - same figure: - - fig1 = figure() - fig2 = figure() - - ax1 = fig1.add_subplot(111) - ax2 = fig2.add_subplot(111, sharex=ax1, sharey=ax1) - - - linestyles now include steps-pre, steps-post and - steps-mid. The old step still works and is equivalent to - step-pre. - - - Multiple line styles may be provided to a collection. - - See API_CHANGES for more low-level information about this - refactoring. - -2007-10-24 Added ax kwarg to Figure.colorbar and pyplot.colorbar - EF - -2007-10-19 Removed a gsave/grestore pair surrounding _draw_ps, which - was causing a loss graphics state info (see "EPS output - problem - scatter & edgecolors" on mpl-dev, 2007-10-29) - - DSD - -2007-10-15 Fixed a bug in patches.Ellipse that was broken for - aspect='auto'. Scale free ellipses now work properly for - equal and auto on Agg and PS, and they fall back on a - polygonal approximation for nonlinear transformations until - we convince oursleves that the spline approximation holds - for nonlinear transformations. Added - unit/ellipse_compare.py to compare spline with vertex - approx for both aspects. JDH - -2007-10-05 remove generator expressions from texmanager and mpltraits. - generator expressions are not supported by python-2.3 - DSD - -2007-10-01 Made matplotlib.use() raise an exception if called after - backends has been imported. - EF - -2007-09-30 Modified update* methods of Bbox and Interval so they - work with reversed axes. Prior to this, trying to - set the ticks on a reversed axis failed with an - uninformative error message. - EF - -2007-09-30 Applied patches to axes3d to fix index error problem - EF - -2007-09-24 Applied Eike Welk's patch reported on mpl-dev on 2007-09-22 - Fixes a bug with multiple plot windows in the qt backend, - ported the changes to backend_qt4 as well - DSD - -2007-09-21 Changed cbook.reversed to yield the same result as the - python reversed builtin - DSD - -2007-09-13 The usetex support in the pdf backend is more usable now, - so I am enabling it. - JKS - -2007-09-12 Fixed a Axes.bar unit bug - JDH - -2007-09-10 Made skiprows=1 the default on csv2rec - JDH - -2007-09-09 Split out the plotting part of pylab and put it in - pyplot.py; removed numerix from the remaining pylab.py, - which imports everything from pyplot.py. The intention - is that apart from cleanups, the result of importing - from pylab is nearly unchanged, but there is the - new alternative of importing from pyplot to get - the state-engine graphics without all the numeric - functions. - Numpified examples; deleted two that were obsolete; - modified some to use pyplot. - EF - -2007-09-08 Eliminated gd and paint backends - EF - -2007-09-06 .bmp file format is now longer an alias for .raw - -2007-09-07 Added clip path support to pdf backend. - JKS - -2007-09-06 Fixed a bug in the embedding of Type 1 fonts in PDF. - Now it doesn't crash Preview.app. - JKS - -2007-09-06 Refactored image saving code so that all GUI backends can - save most image types. See FILETYPES for a matrix of - backends and their supported file types. - Backend canvases should no longer write their own print_figure() - method -- instead they should write a print_xxx method for - each filetype they can output and add an entry to their - class-scoped filetypes dictionary. - MGD - -2007-09-05 Fixed Qt version reporting in setupext.py - DSD - -2007-09-04 Embedding Type 1 fonts in PDF, and thus usetex support - via dviread, sort of works. To test, enable it by - renaming _draw_tex to draw_tex. - JKS - -2007-09-03 Added ability of errorbar show limits via caret or - arrowhead ends on the bars; patch by Manual Metz. - EF - -2007-09-03 Created type1font.py, added features to AFM and FT2Font - (see API_CHANGES), started work on embedding Type 1 fonts - in pdf files. - JKS - -2007-09-02 Continued work on dviread.py. - JKS - -2007-08-16 Added a set_extent method to AxesImage, allow data extent - to be modified after initial call to imshow - DSD - -2007-08-14 Fixed a bug in pyqt4 subplots-adjust. Thanks to - Xavier Gnata for the report and suggested fix - DSD - -2007-08-13 Use pickle to cache entire fontManager; change to using - font_manager module-level function findfont wrapper for - the fontManager.findfont method - EF - -2007-08-11 Numpification and cleanup of mlab.py and some examples - EF - -2007-08-06 Removed mathtext2 - -2007-07-31 Refactoring of distutils scripts. - - Will not fail on the entire build if an optional Python - package (e.g., Tkinter) is installed but its development - headers are not (e.g., tk-devel). Instead, it will - continue to build all other extensions. - - Provide an overview at the top of the output to display - what dependencies and their versions were found, and (by - extension) what will be built. - - Use pkg-config, when available, to find freetype2, since - this was broken on Mac OS-X when using MacPorts in a non- - standard location. - -2007-07-30 Reorganized configuration code to work with traited config - objects. The new config system is located in the - matplotlib.config package, but it is disabled by default. - To enable it, set NEWCONFIG=True in matplotlib.__init__.py. - The new configuration system will still use the old - matplotlibrc files by default. To switch to the experimental, - traited configuration, set USE_TRAITED_CONFIG=True in - config.__init__.py. - -2007-07-29 Changed default pcolor shading to flat; added aliases - to make collection kwargs agree with setter names, so - updating works; related minor cleanups. - Removed quiver_classic, scatter_classic, pcolor_classic. - EF - -2007-07-26 Major rewrite of mathtext.py, using the TeX box layout model. - - There is one (known) backward incompatible change. The - font commands (\cal, \rm, \it, \tt) now behave as TeX does: - they are in effect until the next font change command or - the end of the grouping. Therefore uses of $\cal{R}$ - should be changed to ${\cal R}$. Alternatively, you may - use the new LaTeX-style font commands (\mathcal, \mathrm, - \mathit, \mathtt) which do affect the following group, - e.g., $\mathcal{R}$. - - Other new features include: - - - Math may be interspersed with non-math text. Any text - with an even number of $'s (non-escaped) will be sent to - the mathtext parser for layout. - - - Sub/superscripts are less likely to accidentally overlap. - - - Support for sub/superscripts in either order, e.g., $x^i_j$ - and $x_j^i$ are equivalent. - - - Double sub/superscripts (e.g., $x_i_j$) are considered - ambiguous and raise an exception. Use braces to disambiguate. - - - $\frac{x}{y}$ can be used for displaying fractions. - - - $\sqrt[3]{x}$ can be used to display the radical symbol - with a root number and body. - - - $\left(\frac{x}{y}\right)$ may be used to create - parentheses and other delimiters that automatically - resize to the height of their contents. - - - Spacing around operators etc. is now generally more like - TeX. - - - Added support (and fonts) for boldface (\bf) and - sans-serif (\sf) symbols. - - - Log-like function name shortcuts are supported. For - example, $\sin(x)$ may be used instead of ${\rm sin}(x)$ - - - Limited use of kerning for the easy case (same font) - - Behind the scenes, the pyparsing.py module used for doing - the math parsing was updated to the latest stable version - (1.4.6). A lot of duplicate code was refactored out of the - Font classes. - - - MGD - -2007-07-19 completed numpification of most trivial cases - NN - -2007-07-19 converted non-numpy relicts throughout the code - NN - -2007-07-19 replaced the Python code in numerix/ by a minimal wrapper around - numpy that explicitly mentions all symbols that need to be - addressed for further numpification - NN - -2007-07-18 make usetex respect changes to rcParams. texmanager used to - only configure itself when it was created, now it - reconfigures when rcParams are changed. Thank you Alexander - Schmolck for contributing a patch - DSD - -2007-07-17 added validation to setting and changing rcParams - DSD - -2007-07-17 bugfix segfault in transforms module. Thanks Ben North for - the patch. - ADS - -2007-07-16 clean up some code in ticker.ScalarFormatter, use unicode to - render multiplication sign in offset ticklabel - DSD - -2007-07-16 fixed a formatting bug in ticker.ScalarFormatter's scientific - notation (10^0 was being rendered as 10 in some cases) - DSD - -2007-07-13 Add MPL_isfinite64() and MPL_isinf64() for testing - doubles in (the now misnamed) MPL_isnan.h. - ADS - -2007-07-13 The matplotlib._isnan module removed (use numpy.isnan) - ADS - -2007-07-13 Some minor cleanups in _transforms.cpp - ADS - -2007-07-13 Removed the rest of the numerix extension code detritus, - numpified axes.py, and cleaned up the imports in axes.py - - JDH - -2007-07-13 Added legend.loc as configurable option that could in - future default to 'best'. - NN - -2007-07-12 Bugfixes in mlab.py to coerce inputs into numpy arrays. -ADS - -2007-07-11 Added linespacing kwarg to text.Text - EF - -2007-07-11 Added code to store font paths in SVG files. - MGD - -2007-07-10 Store subset of TTF font as a Type 3 font in PDF files. - MGD - -2007-07-09 Store subset of TTF font as a Type 3 font in PS files. - MGD - -2007-07-09 Applied Paul's pick restructure pick and add pickers, - sourceforge patch 1749829 - JDH - - -2007-07-09 Applied Allan's draw_lines agg optimization. JDH - - -2007-07-08 Applied Carl Worth's patch to fix cairo draw_arc - SC - -2007-07-07 fixed bug 1712099: xpdf distiller on windows - DSD - -2007-06-30 Applied patches to tkagg, gtk, and wx backends to reduce - memory leakage. Patches supplied by Mike Droettboom; - see tracker numbers 1745400, 1745406, 1745408. - Also made unit/memleak_gui.py more flexible with - command-line options. - EF - -2007-06-30 Split defaultParams into separate file rcdefaults (together with - validation code). Some heavy refactoring was necessary to do so, - but the overall behavior should be the same as before. - NN - -2007-06-27 Added MPLCONFIGDIR for the default location for mpl data - and configuration. useful for some apache installs where - HOME is not writable. Tried to clean up the logic in - _get_config_dir to support non-writable HOME where are - writable HOME/.matplotlib already exists - JDH - -2007-06-27 Fixed locale bug reported at - http://sourceforge.net/tracker/index.php?func=detail&aid=1744154&group_id=80706&atid=560720 - by adding a cbook.unicode_safe function - JDH - -2007-06-27 Applied Micheal's tk savefig bugfix described at - http://sourceforge.net/tracker/index.php?func=detail&aid=1716732&group_id=80706&atid=560720 - Thanks Michael! - - -2007-06-27 Patch for get_py2exe_datafiles() to work with new directory - layout. (Thanks Tocer and also Werner Bruhin.) -ADS - - -2007-06-27 Added a scroll event to the mpl event handling system and - implemented it for backends GTK* -- other backend - users/developers/maintainers, please add support for your - backend. - JDH - -2007-06-25 Changed default to clip=False in colors.Normalize; - modified ColorbarBase for easier colormap display - EF - -2007-06-13 Added maskedarray option to rc, numerix - EF - -2007-06-11 Python 2.5 compatibility fix for mlab.py - EF - -2007-06-10 In matplotlibrc file, use 'dashed' | 'solid' instead - of a pair of floats for contour.negative_linestyle - EF - -2007-06-08 Allow plot and fill fmt string to be any mpl string - colorspec - EF - -2007-06-08 Added gnuplot file plotfile function to pylab -- see - examples/plotfile_demo.py - JDH - -2007-06-07 Disable build of numarray and Numeric extensions for - internal MPL use and the numerix layer. - ADS - -2007-06-07 Added csv2rec to matplotlib.mlab to support automatically - converting csv files to record arrays using type - introspection, and turned on native datetime support using - the new units support in matplotlib.dates. See - examples/loadrec.py ! JDH - -2007-06-07 Simplified internal code of _auto_legend_data - NN - -2007-06-04 Added labeldistance arg to Axes.pie to control the raidal - distance of the wedge labels - JDH - -2007-06-03 Turned mathtext in SVG into single with multiple - objects (easier to edit in inkscape). - NN - -=============================================================== -2007-06-02 Released 0.90.1 at revision 3352 - -2007-06-02 Display only meaningful labels when calling legend() - without args. - NN - -2007-06-02 Have errorbar follow the color cycle even if line is not plotted. - Suppress plotting of errorbar caps for capsize=0. - NN - -2007-06-02 Set markers to same alpha value as line. - NN - -2007-06-02 Fix mathtext position in svg backend. - NN - -2007-06-01 Deprecate Numeric and numarray for use as numerix. Props to - Travis -- job well done. - ADS - -2007-05-18 Added LaTeX unicode support. Enable with the - 'text.latex.unicode' rcParam. This requires the ucs and - inputenc LaTeX packages. - ADS - -2007-04-23 Fixed some problems with polar -- added general polygon - clipping to clip the lines a nd grids to the polar axes. - Added support for set_rmax to easily change the maximum - radial grid. Added support for polar legend - JDH - -2007-04-16 Added Figure.autofmt_xdate to handle adjusting the bottom - and rotating the tick labels for date plots when the ticks - often overlap - JDH - -2007-04-09 Beginnings of usetex support for pdf backend. -JKS - -2007-04-07 Fixed legend/LineCollection bug. Added label support - to collections. - EF - -2007-04-06 Removed deprecated support for a float value as a gray-scale; - now it must be a string, like '0.5'. Added alpha kwarg to - ColorConverter.to_rgba_list. - EF - -2007-04-06 Fixed rotation of ellipses in pdf backend - (sf bug #1690559) -JKS - -2007-04-04 More matshow tweaks; documentation updates; new method - set_bounds() for formatters and locators. - EF - -2007-04-02 Fixed problem with imshow and matshow of integer arrays; - fixed problems with changes to color autoscaling. - EF - -2007-04-01 Made image color autoscaling work correctly with - a tracking colorbar; norm.autoscale now scales - unconditionally, while norm.autoscale_None changes - only None-valued vmin, vmax. - EF - -2007-03-31 Added a qt-based subplot-adjustment dialog - DSD - -2007-03-30 Fixed a bug in backend_qt4, reported on mpl-dev - DSD - -2007-03-26 Removed colorbar_classic from figure.py; fixed bug in - Figure.clf() in which _axobservers was not getting - cleared. Modernization and cleanups. - EF - -2007-03-26 Refactored some of the units support -- units now live in - the respective x and y Axis instances. See also - API_CHANGES for some alterations to the conversion - interface. JDH - -2007-03-25 Fix masked array handling in quiver.py for numpy. (Numeric - and numarray support for masked arrays is broken in other - ways when using quiver. I didn't pursue that.) - ADS - -2007-03-23 Made font_manager.py close opened files. - JKS - -2007-03-22 Made imshow default extent match matshow - EF - -2007-03-22 Some more niceties for xcorr -- a maxlags option, normed - now works for xcorr as well as axorr, usevlines is - supported, and a zero correlation hline is added. See - examples/xcorr_demo.py. Thanks Sameer for the patch. - - JDH - -2007-03-21 Axes.vlines and Axes.hlines now create and returns a - LineCollection, not a list of lines. This is much faster. - The kwarg signature has changed, so consult the docs. - Modified Axes.errorbar which uses vlines and hlines. See - API_CHANGES; the return signature for these three functions - is now different - -2007-03-20 Refactored units support and added new examples - JDH - -2007-03-19 Added Mike's units patch - JDH - -2007-03-18 Matshow as an Axes method; test version matshow1() in - pylab; added 'integer' Boolean kwarg to MaxNLocator - initializer to force ticks at integer locations. - EF - -2007-03-17 Preliminary support for clipping to paths agg - JDH - -2007-03-17 Text.set_text() accepts anything convertible with '%s' - EF - -2007-03-14 Add masked-array support to hist. - EF - -2007-03-03 Change barh to take a kwargs dict and pass it to bar. - Fixes sf bug #1669506. - -2007-03-02 Add rc parameter pdf.inheritcolor, which disables all - color-setting operations in the pdf backend. The idea is - that you include the resulting file in another program and - set the colors (both stroke and fill color) there, so you - can use the same pdf file for e.g., a paper and a - presentation and have them in the surrounding color. You - will probably not want to draw figure and axis frames in - that case, since they would be filled in the same color. - JKS - -2007-02-26 Prevent building _wxagg.so with broken Mac OS X wxPython. - ADS - -2007-02-23 Require setuptools for Python 2.3 - ADS - -2007-02-22 WXAgg accelerator updates - KM - WXAgg's C++ accelerator has been fixed to use the correct wxBitmap - constructor. - - The backend has been updated to use new wxPython functionality to - provide fast blit() animation without the C++ accelerator. This - requires wxPython 2.8 or later. Previous versions of wxPython can - use the C++ acclerator or the old pure Python routines. - - setup.py no longer builds the C++ accelerator when wxPython >= 2.8 - is present. - - The blit() method is now faster regardless of which agg/wxPython - conversion routines are used. - -2007-02-21 Applied the PDF backend patch by Nicolas Grilly. - This impacts several files and directories in matplotlib: - - - Created the directory lib/matplotlib/mpl-data/fonts/pdfcorefonts, - holding AFM files for the 14 PDF core fonts. These fonts are - embedded in every PDF viewing application. - - - setup.py: Added the directory pdfcorefonts to package_data. - - - lib/matplotlib/__init__.py: Added the default parameter - 'pdf.use14corefonts'. When True, the PDF backend uses - only the 14 PDF core fonts. - - - lib/matplotlib/afm.py: Added some keywords found in - recent AFM files. Added a little workaround to handle - Euro symbol. - - - lib/matplotlib/fontmanager.py: Added support for the 14 - PDF core fonts. These fonts have a dedicated cache (file - pdfcorefont.cache), not the same as for other AFM files - (file .afmfont.cache). Also cleaned comments to conform - to CODING_GUIDE. - - - lib/matplotlib/backends/backend_pdf.py: - Added support for 14 PDF core fonts. - Fixed some issues with incorrect character widths and - encodings (works only for the most common encoding, - WinAnsiEncoding, defined by the official PDF Reference). - Removed parameter 'dpi' because it causes alignment issues. - - -JKS (patch by Nicolas Grilly) - -2007-02-17 Changed ft2font.get_charmap, and updated all the files where - get_charmap is mentioned - ES - -2007-02-13 Added barcode demo- JDH - -2007-02-13 Added binary colormap to cm - JDH - -2007-02-13 Added twiny to pylab - JDH - -2007-02-12 Moved data files into lib/matplotlib so that setuptools' - develop mode works. Re-organized the mpl-data layout so - that this source structure is maintained in the - installation. (i.e., the 'fonts' and 'images' - sub-directories are maintained in site-packages.) Suggest - removing site-packages/matplotlib/mpl-data and - ~/.matplotlib/ttffont.cache before installing - ADS - -2007-02-07 Committed Rob Hetland's patch for qt4: remove - references to text()/latin1(), plus some improvements - to the toolbar layout - DSD - -=============================================================== -2007-02-06 Released 0.90.0 at revision 3003 - -2007-01-22 Extended the new picker API to text, patches and patch - collections. Added support for user customizable pick hit - testing and attribute tagging of the PickEvent - Details - and examples in examples/pick_event_demo.py - JDH - -2007-01-16 Begun work on a new pick API using the mpl event handling - frameowrk. Artists will define their own pick method with - a configurable epsilon tolerance and return pick attrs. - All artists that meet the tolerance threshold will fire a - PickEvent with artist dependent attrs; e.g., a Line2D can set - the indices attribute that shows the indices into the line - that are within epsilon of the pick point. See - examples/pick_event_demo.py. The implementation of pick - for the remaining Artists remains to be done, but the core - infrastructure at the level of event handling is in place - with a proof-of-concept implementation for Line2D - JDH - -2007-01-16 src/_image.cpp: update to use Py_ssize_t (for 64-bit systems). - Use return value of fread() to prevent warning messages - SC. - -2007-01-15 src/_image.cpp: combine buffer_argb32() and buffer_bgra32() into - a new method color_conv(format) - SC - -2007-01-14 backend_cairo.py: update draw_arc() so that - examples/arctest.py looks correct - SC - -2007-01-12 backend_cairo.py: enable clipping. Update draw_image() so that - examples/contour_demo.py looks correct - SC - -2007-01-12 backend_cairo.py: fix draw_image() so that examples/image_demo.py - now looks correct - SC - -2007-01-11 Added Axes.xcorr and Axes.acorr to plot the cross - correlation of x vs y or the autocorrelation of x. pylab - wrappers also provided. See examples/xcorr_demo.py - JDH - -2007-01-10 Added "Subplot.label_outer" method. It will set the - visibility of the ticklabels so that yticklabels are only - visible in the first column and xticklabels are only - visible in the last row - JDH - -2007-01-02 Added additional kwarg documentation - JDH - -2006-12-28 Improved error message for nonpositive input to log - transform; added log kwarg to bar, barh, and hist, - and modified bar method to behave sensibly by default - when the ordinate has a log scale. (This only works - if the log scale is set before or by the call to bar, - hence the utility of the log kwarg.) - EF - -2006-12-27 backend_cairo.py: update draw_image() and _draw_mathtext() to work - with numpy - SC - -2006-12-20 Fixed xpdf dependency check, which was failing on windows. - Removed ps2eps dependency check. - DSD - -2006-12-19 Added Tim Leslie's spectral patch - JDH - -2006-12-17 Added rc param 'axes.formatter.limits' to control - the default threshold for switching to scientific - notation. Added convenience method - Axes.ticklabel_format() for turning scientific notation - on or off on either or both axes. - EF - -2006-12-16 Added ability to turn control scientific notation - in ScalarFormatter - EF - -2006-12-16 Enhanced boxplot to handle more flexible inputs - EF - -2006-12-13 Replaced calls to where() in colors.py with much faster - clip() and putmask() calls; removed inappropriate - uses of getmaskorNone (which should be needed only - very rarely); all in response to profiling by - David Cournapeau. Also fixed bugs in my 2-D - array support from 12-09. - EF - -2006-12-09 Replaced spy and spy2 with the new spy that combines - marker and image capabilities - EF - -2006-12-09 Added support for plotting 2-D arrays with plot: - columns are plotted as in Matlab - EF - -2006-12-09 Added linewidth kwarg to bar and barh; fixed arg - checking bugs - EF - -2006-12-07 Made pcolormesh argument handling match pcolor; - fixed kwarg handling problem noted by Pierre GM - EF - -2006-12-06 Made pcolor support vector X and/or Y instead of - requiring 2-D arrays - EF - -2006-12-05 Made the default Artist._transform None (rather than - invoking identity_transform for each artist only to have it - overridden later). Use artist.get_transform() rather than - artist._transform, even in derived classes, so that the - default transform will be created lazily as needed - JDH - -2006-12-03 Added LogNorm to colors.py as illustrated by - examples/pcolor_log.py, based on suggestion by - Jim McDonald. Colorbar modified to handle LogNorm. - Norms have additional "inverse" method. - EF - -2006-12-02 Changed class names in colors.py to match convention: - normalize -> Normalize, no_norm -> NoNorm. Old names - are still available. - Changed __init__.py rc defaults to match those in - matplotlibrc - EF - -2006-11-22 Fixed bug in set_*lim that I had introduced on 11-15 - EF - -2006-11-22 Added examples/clippedline.py, which shows how to clip line - data based on view limits -- it also changes the marker - style when zoomed in - JDH - -2006-11-21 Some spy bug-fixes and added precision arg per Robert C's - suggestion - JDH - -2006-11-19 Added semi-automatic docstring generation detailing all the - kwargs that functions take using the artist introspection - tools; e.g., 'help text now details the scatter kwargs - that control the Text properties - JDH - -2006-11-17 Removed obsolete scatter_classic, leaving a stub to - raise NotImplementedError; same for pcolor_classic - EF - -2006-11-15 Removed obsolete pcolor_classic - EF - -2006-11-15 Fixed 1588908 reported by Russel Owen; factored - nonsingular method out of ticker.py, put it into - transforms.py as a function, and used it in - set_xlim and set_ylim. - EF - -2006-11-14 Applied patch 1591716 by Ulf Larssen to fix a bug in - apply_aspect. Modified and applied patch - 1594894 by mdehoon to fix bugs and improve - formatting in lines.py. Applied patch 1573008 - by Greg Willden to make psd etc. plot full frequency - range for complex inputs. - EF - -2006-11-14 Improved the ability of the colorbar to track - changes in corresponding image, pcolor, or - contourf. - EF - -2006-11-11 Fixed bug that broke Numeric compatibility; - added support for alpha to colorbar. The - alpha information is taken from the mappable - object, not specified as a kwarg. - EF - -2006-11-05 Added broken_barh function for makring a sequence of - horizontal bars broken by gaps -- see examples/broken_barh.py - -2006-11-05 Removed lineprops and markerprops from the Annotation code - and replaced them with an arrow configurable with kwarg - arrowprops. See examples/annotation_demo.py - JDH - -2006-11-02 Fixed a pylab subplot bug that was causing axes to be - deleted with hspace or wspace equals zero in - subplots_adjust - JDH - -2006-10-31 Applied axes3d patch 1587359 - http://sourceforge.net/tracker/index.php?func=detail&aid=1587359&group_id=80706&atid=560722 - JDH - -=============================================================== -2006-10-26 Released 0.87.7 at revision 2835 - -2006-10-25 Made "tiny" kwarg in Locator.nonsingular much smaller - EF - -2006-10-17 Closed sf bug 1562496 update line props dash/solid/cap/join - styles - JDH - -2006-10-17 Complete overhaul of the annotations API and example code - - See matplotlib.text.Annotation and - examples/annotation_demo.py JDH - -2006-10-12 Committed Manuel Metz's StarPolygon code and - examples/scatter_star_poly.py - JDH - - -2006-10-11 commented out all default values in matplotlibrc.template - Default values should generally be taken from defaultParam in - __init__.py - the file matplotlib should only contain those values - that the user wants to explicitly change from the default. - (see thread "marker color handling" on matplotlib-devel) - -2006-10-10 Changed default comment character for load to '#' - JDH - -2006-10-10 deactivated rcfile-configurability of markerfacecolor - and markeredgecolor. Both are now hardcoded to the special value - 'auto' to follow the line color. Configurability at run-time - (using function arguments) remains functional. - NN - -2006-10-07 introduced dummy argument magnification=1.0 to - FigImage.make_image to satisfy unit test figimage_demo.py - The argument is not yet handled correctly, which should only - show up when using non-standard DPI settings in PS backend, - introduced by patch #1562394. - NN - -2006-10-06 add backend-agnostic example: simple3d.py - NN - -2006-09-29 fix line-breaking for SVG-inline images (purely cosmetic) - NN - -2006-09-29 reworked set_linestyle and set_marker - markeredgecolor and markerfacecolor now default to - a special value "auto" that keeps the color in sync with - the line color - further, the intelligence of axes.plot is cleaned up, - improved and simplified. Complete compatibility cannot be - guaranteed, but the new behavior should be much more predictable - (see patch #1104615 for details) - NN - -2006-09-29 changed implementation of clip-path in SVG to work around a - limitation in inkscape - NN - -2006-09-29 added two options to matplotlibrc: - svg.image_inline - svg.image_noscale - see patch #1533010 for details - NN - -2006-09-29 axes.py: cleaned up kwargs checking - NN - -2006-09-29 setup.py: cleaned up setup logic - NN - -2006-09-29 setup.py: check for required pygtk versions, fixes bug #1460783 - SC - -=============================================================== -2006-09-27 Released 0.87.6 at revision 2783 - -2006-09-24 Added line pointers to the Annotation code, and a pylab - interface. See matplotlib.text.Annotation, - examples/annotation_demo.py and - examples/annotation_demo_pylab.py - JDH - -2006-09-18 mathtext2.py: The SVG backend now supports the same things that - the AGG backend does. Fixed some bugs with rendering, and out of - bounds errors in the AGG backend - ES. Changed the return values - of math_parse_s_ft2font_svg to support lines (fractions etc.) - -2006-09-17 Added an Annotation class to facilitate annotating objects - and an examples file examples/annotation_demo.py. I want - to add dash support as in TextWithDash, but haven't decided - yet whether inheriting from TextWithDash is the right base - class or if another approach is needed - JDH - -=============================================================== -2006-09-05 Released 0.87.5 at revision 2761 - -2006-09-04 Added nxutils for some numeric add-on extension code -- - specifically a better/more efficient inside polygon tester (see - unit/inside_poly_*.py) - JDH - -2006-09-04 Made bitstream fonts the rc default - JDH - -2006-08-31 Fixed alpha-handling bug in ColorConverter, affecting - collections in general and contour/contourf in - particular. - EF - -2006-08-30 ft2font.cpp: Added draw_rect_filled method (now used by mathtext2 - to draw the fraction bar) to FT2Font - ES - -2006-08-29 setupext.py: wrap calls to tk.getvar() with str(). On some - systems, getvar returns a Tcl_Obj instead of a string - DSD - -2006-08-28 mathtext2.py: Sub/superscripts can now be complex (i.e. - fractions etc.). The demo is also updated - ES - -2006-08-28 font_manager.py: Added /usr/local/share/fonts to list of - X11 font directories - DSD - -2006-08-28 mahtext2.py: Initial support for complex fractions. Also, - rendering is now completely separated from parsing. The - sub/superscripts now work better. - Updated the mathtext2_demo.py - ES - -2006-08-27 qt backends: don't create a QApplication when backend is - imported, do it when the FigureCanvasQt is created. Simplifies - applications where mpl is embedded in qt. Updated - embedding_in_qt* examples - DSD - -2006-08-27 mahtext2.py: Now the fonts are searched in the OS font dir and - in the mpl-data dir. Also env is not a dict anymore. - ES - -2006-08-26 minor changes to __init__.py, mathtex2_demo.py. Added matplotlibrc - key "mathtext.mathtext2" (removed the key "mathtext2") - ES - -2006-08-21 mathtext2.py: Initial support for fractions - Updated the mathtext2_demo.py - _mathtext_data.py: removed "\" from the unicode dicts - mathtext.py: Minor modification (because of _mathtext_data.py)- ES - -2006-08-20 Added mathtext2.py: Replacement for mathtext.py. Supports _ ^, - \rm, \cal etc., \sin, \cos etc., unicode, recursive nestings, - inline math mode. The only backend currently supported is Agg - __init__.py: added new rc params for mathtext2 - added mathtext2_demo.py example - ES - -2006-08-19 Added embedding_in_qt4.py example - DSD - -2006-08-11 Added scale free Ellipse patch for Agg - CM - -2006-08-10 Added converters to and from julian dates to matplotlib.dates - (num2julian and julian2num) - JDH - -2006-08-08 Fixed widget locking so multiple widgets could share the - event handling - JDH - -2006-08-07 Added scale free Ellipse patch to SVG and PS - CM - -2006-08-05 Re-organized imports in numerix for numpy 1.0b2 -- TEO - -2006-08-04 Added draw_markers to PDF backend. - JKS - -2006-08-01 Fixed a bug in postscript's rendering of dashed lines - DSD - -2006-08-01 figure.py: savefig() update docstring to add support for 'format' - argument. - backend_cairo.py: print_figure() add support 'format' argument. - SC - -2006-07-31 Don't let postscript's xpdf distiller compress images - DSD - -2006-07-31 Added shallowcopy() methods to all Transformations; - removed copy_bbox_transform and copy_bbox_transform_shallow - from transforms.py; - added offset_copy() function to transforms.py to - facilitate positioning artists with offsets. - See examples/transoffset.py. - EF - -2006-07-31 Don't let postscript's xpdf distiller compress images - DSD - -2006-07-29 Fixed numerix polygon bug reported by Nick Fotopoulos. - Added inverse_numerix_xy() transform method. - Made autoscale_view() preserve axis direction - (e.g., increasing down).- EF - -2006-07-28 Added shallow bbox copy routine for transforms -- mainly - useful for copying transforms to apply offset to. - JDH - -2006-07-28 Added resize method to FigureManager class - for Qt and Gtk backend - CM - -2006-07-28 Added subplots_adjust button to Qt backend - CM - -2006-07-26 Use numerix more in collections. - Quiver now handles masked arrays. - EF - -2006-07-22 Fixed bug #1209354 - DSD - -2006-07-22 make scatter() work with the kwarg "color". Closes bug - 1285750 - DSD - -2006-07-20 backend_cairo.py: require pycairo 1.2.0. - print_figure() update to output SVG using cairo. - -2006-07-19 Added blitting for Qt4Agg - CM - -2006-07-19 Added lasso widget and example examples/lasso_demo.py - JDH - -2006-07-18 Added blitting for QtAgg backend - CM - -2006-07-17 Fixed bug #1523585: skip nans in semilog plots - DSD - -2006-07-12 Add support to render the scientific notation label - over the right-side y-axis - DSD - -=============================================================== -2006-07-11 Released 0.87.4 at revision 2558 - -2006-07-07 Fixed a usetex bug with older versions of latex - DSD - -2006-07-07 Add compatibility for NumPy 1.0 - TEO - -2006-06-29 Added a Qt4Agg backend. Thank you James Amundson - DSD - -2006-06-26 Fixed a usetex bug. On windows, usetex will prcess - postscript output in the current directory rather than - in a temp directory. This is due to the use of spaces - and tildes in windows paths, which cause problems with - latex. The subprocess module is no longer used. - DSD - -2006-06-22 Various changes to bar(), barh(), and hist(). - Added 'edgecolor' keyword arg to bar() and barh(). - The x and y args in barh() have been renamed to width - and bottom respectively, and their order has been swapped - to maintain a (position, value) order ala matlab. left, - height, width and bottom args can now all be scalars or - sequences. barh() now defaults to edge alignment instead - of center alignment. Added a keyword arg 'align' to bar(), - barh() and hist() that controls between edge or center bar - alignment. Fixed ignoring the rcParams['patch.facecolor'] - for bar color in bar() and barh(). Fixed ignoring the - rcParams['lines.color'] for error bar color in bar() - and barh(). Fixed a bug where patches would be cleared - when error bars were plotted if rcParams['axes.hold'] - was False. - MAS - -2006-06-22 Added support for numerix 2-D arrays as alternatives to - a sequence of (x,y) tuples for specifying paths in - collections, quiver, contour, pcolor, transforms. - Fixed contour bug involving setting limits for - color mapping. Added numpy-style all() to numerix. - EF - -2006-06-20 Added custom FigureClass hook to pylab interface - see - examples/custom_figure_class.py - -2006-06-16 Added colormaps from gist (gist_earth, gist_stern, - gist_rainbow, gist_gray, gist_yarg, gist_heat, gist_ncar) - JW - -2006-06-16 Added a pointer to parent in figure canvas so you can - access the container with fig.canvas.manager. Useful if - you want to set the window title, e.g., in gtk - fig.canvas.manager.window.set_title, though a GUI neutral - method would be preferable JDH - -2006-06-16 Fixed colorbar.py to handle indexed colors (i.e., - norm = no_norm()) by centering each colored region - on its index. - EF - -2006-06-15 Added scalex and scaley to Axes.autoscale_view to support - selective autoscaling just the x or y axis, and supported - these command in plot so you can say plot(something, - scaley=False) and just the x axis will be autoscaled. - Modified axvline and axhline to support this, so for - example axvline will no longer autoscale the y axis. JDH - -2006-06-13 Fix so numpy updates are backward compatible - TEO - -2006-06-12 Updated numerix to handle numpy restructuring of - oldnumeric - TEO - -2006-06-12 Updated numerix.fft to handle numpy restructuring - Added ImportError to numerix.linear_algebra for numpy -TEO - -2006-06-11 Added quiverkey command to pylab and Axes, using - QuiverKey class in quiver.py. Changed pylab and Axes - to use quiver2 if possible, but drop back to the - newly-renamed quiver_classic if necessary. Modified - examples/quiver_demo.py to illustrate the new quiver - and quiverkey. Changed LineCollection implementation - slightly to improve compatibility with PolyCollection. - EF - -2006-06-11 Fixed a usetex bug for windows, running latex on files - with spaces in their names or paths was failing - DSD - -2006-06-09 Made additions to numerix, changes to quiver to make it - work with all numeric flavors. - EF - -2006-06-09 Added quiver2 function to pylab and method to axes, - with implementation via a Quiver class in quiver.py. - quiver2 will replace quiver before the next release; - it is placed alongside it initially to facilitate - testing and transition. See also - examples/quiver2_demo.py. - EF - -2006-06-08 Minor bug fix to make ticker.py draw proper minus signs - with usetex - DSD - -=============================================================== -2006-06-06 Released 0.87.3 at revision 2432 - -2006-05-30 More partial support for polygons with outline or fill, - but not both. Made LineCollection inherit from - ScalarMappable. - EF - -2006-05-29 Yet another revision of aspect-ratio handling. - EF - -2006-05-27 Committed a patch to prevent stroking zero-width lines in - the svg backend - DSD - -2006-05-24 Fixed colorbar positioning bug identified by Helge - Avlesen, and improved the algorithm; added a 'pad' - kwarg to control the spacing between colorbar and - parent axes. - EF - -2006-05-23 Changed color handling so that collection initializers - can take any mpl color arg or sequence of args; deprecated - float as grayscale, replaced by string representation of - float. - EF - -2006-05-19 Fixed bug: plot failed if all points were masked - EF - -2006-05-19 Added custom symbol option to scatter - JDH - -2006-05-18 New example, multi_image.py; colorbar fixed to show - offset text when the ScalarFormatter is used; FixedFormatter - augmented to accept and display offset text. - EF - -2006-05-14 New colorbar; old one is renamed to colorbar_classic. - New colorbar code is in colorbar.py, with wrappers in - figure.py and pylab.py. - Fixed aspect-handling bug reported by Michael Mossey. - Made backend_bases.draw_quad_mesh() run.- EF - -2006-05-08 Changed handling of end ranges in contourf: replaced - "clip-ends" kwarg with "extend". See docstring for - details. -EF - -2006-05-08 Added axisbelow to rc - JDH - -2006-05-08 If using PyGTK require version 2.2+ - SC - -2006-04-19 Added compression support to PDF backend, controlled by - new pdf.compression rc setting. - JKS - -2006-04-19 Added Jouni's PDF backend - -2006-04-18 Fixed a bug that caused agg to not render long lines - -2006-04-16 Masked array support for pcolormesh; made pcolormesh support the - same combinations of X,Y,C dimensions as pcolor does; - improved (I hope) description of grid used in pcolor, - pcolormesh. - EF - -2006-04-14 Reorganized axes.py - EF - -2006-04-13 Fixed a bug Ryan found using usetex with sans-serif fonts and - exponential tick labels - DSD - -2006-04-11 Refactored backend_ps and backend_agg to prevent module-level - texmanager imports. Now these imports only occur if text.usetex - rc setting is true - DSD - -2006-04-10 Committed changes required for building mpl on win32 - platforms with visual studio. This allows wxpython - blitting for fast animations. - CM - -2006-04-10 Fixed an off-by-one bug in Axes.change_geometry. - -2006-04-10 Fixed bug in pie charts where wedge wouldn't have label in - legend. Submitted by Simon Hildebrandt. - ADS - -2006-05-06 Usetex makes temporary latex and dvi files in a temporary - directory, rather than in the user's current working - directory - DSD - -2006-04-05 Apllied Ken's wx deprecation warning patch closing sf patch - #1465371 - JDH - -2006-04-05 Added support for the new API in the postscript backend. - Allows values to be masked using nan's, and faster file - creation - DSD - -2006-04-05 Use python's subprocess module for usetex calls to - external programs. subprocess catches when they exit - abnormally so an error can be raised. - DSD - -2006-04-03 Fixed the bug in which widgets would not respond to - events. This regressed the twinx functionality, so I - also updated subplots_adjust to update axes that share - an x or y with a subplot instance. - CM - -2006-04-02 Moved PBox class to transforms and deleted pbox.py; - made pylab axis command a thin wrapper for Axes.axis; - more tweaks to aspect-ratio handling; fixed Axes.specgram - to account for the new imshow default of unit aspect - ratio; made contour set the Axes.dataLim. - EF - -2006-03-31 Fixed the Qt "Underlying C/C++ object deleted" bug. - JRE - -2006-03-31 Applied Vasily Sulatskov's Qt Navigation Toolbar enhancement. - JRE - -2006-03-31 Ported Norbert's rewriting of Halldor's stineman_interp - algorithm to make it numerix compatible and added code to - matplotlib.mlab. See examples/interp_demo.py - JDH - -2006-03-30 Fixed a bug in aspect ratio handling; blocked potential - crashes when panning with button 3; added axis('image') - support. - EF - -2006-03-28 More changes to aspect ratio handling; new PBox class - in new file pbox.py to facilitate resizing and repositioning - axes; made PolarAxes maintain unit aspect ratio. - EF - -2006-03-23 Refactored TextWithDash class to inherit from, rather than - delegate to, the Text class. Improves object inspection - and closes bug # 1357969 - DSD - -2006-03-22 Improved aspect ratio handling, including pylab interface. - Interactive resizing, pan, zoom of images and plots - (including panels with a shared axis) should work. - Additions and possible refactoring are still likely. - EF - -2006-03-21 Added another colorbrewer colormap (RdYlBu) - JSWHIT - -2006-03-21 Fixed tickmarks for logscale plots over very large ranges. - Closes bug # 1232920 - DSD - -2006-03-21 Added Rob Knight's arrow code; see examples/arrow_demo.py - JDH - -2006-03-20 Added support for masking values with nan's, using ADS's - isnan module and the new API. Works for *Agg backends - DSD - -2006-03-20 Added contour.negative_linestyle rcParam - ADS - -2006-03-20 Added _isnan extension module to test for nan with Numeric - - ADS - -2006-03-17 Added Paul and Alex's support for faceting with quadmesh - in sf patch 1411223 - JDH - -2006-03-17 Added Charle Twardy's pie patch to support colors=None. - Closes sf patch 1387861 - JDH - -2006-03-17 Applied sophana's patch to support overlapping axes with - toolbar navigation by toggling activation with the 'a' key. - Closes sf patch 1432252 - JDH - -2006-03-17 Applied Aarre's linestyle patch for backend EMF; closes sf - patch 1449279 - JDH - -2006-03-17 Applied Jordan Dawe's patch to support kwarg properties - for grid lines in the grid command. Closes sf patch - 1451661 - JDH - -2006-03-17 Center postscript output on page when using usetex - DSD - -2006-03-17 subprocess module built if Python <2.4 even if subprocess - can be imported from an egg - ADS - -2006-03-17 Added _subprocess.c from Python upstream and hopefully - enabled building (without breaking) on Windows, although - not tested. - ADS - -2006-03-17 Updated subprocess.py to latest Python upstream and - reverted name back to subprocess.py - ADS - -2006-03-16 Added John Porter's 3D handling code - - -=============================================================== -2006-03-16 Released 0.87.2 at revision 2150 - -2006-03-15 Fixed bug in MaxNLocator revealed by daigos@infinito.it. - The main change is that Locator.nonsingular now adjusts - vmin and vmax if they are nearly the same, not just if - they are equal. A new kwarg, "tiny", sets the threshold. - - EF - -2006-03-14 Added import of compatibility library for newer numpy - linear_algebra - TEO - -2006-03-12 Extended "load" function to support individual columns and - moved "load" and "save" into matplotlib.mlab so they can be - used outside of pylab -- see examples/load_converter.py - - JDH - -2006-03-12 Added AutoDateFormatter and AutoDateLocator submitted - by James Evans. Try the load_converter.py example for a - demo. - ADS - -2006-03-11 Added subprocess module from python-2.4 - DSD - -2006-03-11 Fixed landscape orientation support with the usetex - option. The backend_ps print_figure method was - getting complicated, I added a _print_figure_tex - method to maintain some degree of sanity - DSD - -2006-03-11 Added "papertype" savefig kwarg for setting - postscript papersizes. papertype and ps.papersize - rc setting can also be set to "auto" to autoscale - pagesizes - DSD - -2006-03-09 Apply P-J's patch to make pstoeps work on windows - patch report # 1445612 - DSD - -2006-03-09 Make backend rc parameter case-insensitive - DSD - -2006-03-07 Fixed bug in backend_ps related to C0-C6 papersizes, - which were causing problems with postscript viewers. - Supported page sizes include letter, legal, ledger, - A0-A10, and B0-B10 - DSD - -=============================================================== -2006-03-07 Released 0.87.1 - -2006-03-04 backend_cairo.py: - fix get_rgb() bug reported by Keith Briggs. - Require pycairo 1.0.2. - Support saving png to file-like objects. - SC - -2006-03-03 Fixed pcolor handling of vmin, vmax - EF - -2006-03-02 improve page sizing with usetex with the latex - geometry package. Closes bug # 1441629 - DSD - -2006-03-02 Fixed dpi problem with usetex png output. Accepted a - modified version of patch # 1441809 - DSD - -2006-03-01 Fixed axis('scaled') to deal with case xmax < xmin - JSWHIT - -2006-03-01 Added reversed colormaps (with '_r' appended to name) - JSWHIT - -2006-02-27 Improved eps bounding boxes with usetex - DSD - -2006-02-27 Test svn commit, again! - -2006-02-27 Fixed two dependency checking bugs related to usetex - on Windows - DSD - -2006-02-27 Made the rc deprecation warnings a little more human - readable. - -2006-02-26 Update the previous gtk.main_quit() bug fix to use gtk.main_level() - - SC - -2006-02-24 Implemented alpha support in contour and contourf - EF - -2006-02-22 Fixed gtk main quit bug when quit was called before - mainloop. - JDH - -2006-02-22 Small change to colors.py to workaround apparent - bug in numpy masked array module - JSWHIT - -2006-02-22 Fixed bug in ScalarMappable.to_rgba() reported by - Ray Jones, and fixed incorrect fix found by Jeff - Whitaker - EF - -=============================================================== -2006-02-22 Released 0.87 - -2006-02-21 Fixed portrait/landscape orientation in postscript backend - DSD - -2006-02-21 Fix bug introduced in yesterday's bug fix - SC - -2006-02-20 backend_gtk.py FigureCanvasGTK.draw(): fix bug reported by - David Tremouilles - SC - -2006-02-20 Remove the "pygtk.require('2.4')" error from - examples/embedding_in_gtk2.py - SC - -2006-02-18 backend_gtk.py FigureCanvasGTK.draw(): simplify to use (rather than - duplicate) the expose_event() drawing code - SC - -2006-02-12 Added stagger or waterfall plot capability to LineCollection; - illustrated in examples/collections.py. - EF - -2006-02-11 Massive cleanup of the usetex code in the postscript backend. Possibly - fixed the clipping issue users were reporting with older versions of - ghostscript - DSD - -2006-02-11 Added autolim kwarg to axes.add_collection. Changed - collection get_verts() methods accordingly. - EF - -2006-02-09 added a temporary rc parameter text.dvipnghack, to allow Mac users to get nice - results with the usetex option. - DSD - -2006-02-09 Fixed a bug related to setting font sizes with the usetex option. - DSD - -2006-02-09 Fixed a bug related to usetex's latex code. - DSD - -2006-02-09 Modified behavior of font.size rc setting. You should define font.size in pts, - which will set the "medium" or default fontsize. Special text sizes like axis - labels or tick labels can be given relative font sizes like small, large, - x-large, etc. and will scale accordingly. - DSD - -2006-02-08 Added py2exe specific datapath check again. Also added new - py2exe helper function get_py2exe_datafiles for use in py2exe - setup.py scripts. - CM - -2006-02-02 Added box function to pylab - -2006-02-02 Fixed a problem in setupext.py, tk library formatted in unicode - caused build problems - DSD - -2006-02-01 Dropped TeX engine support in usetex to focus on LaTeX. - DSD - -2006-01-29 Improved usetex option to respect the serif, sans-serif, monospace, - and cursive rc settings. Removed the font.latex.package rc setting, - it is no longer required - DSD - -2006-01-29 Fixed tex's caching to include font.family rc information - DSD - -2006-01-29 Fixed subpixel rendering bug in *Agg that was causing - uneven gridlines - JDH - -2006-01-28 Added fontcmd to backend_ps's RendererPS.draw_tex, to support other - font families in eps output - DSD - -2006-01-28 Added MaxNLocator to ticker.py, and changed contour.py to - use it by default. - EF - -2006-01-28 Added fontcmd to backend_ps's RendererPS.draw_tex, to support other - font families in eps output - DSD - -2006-01-27 Buffered reading of matplotlibrc parameters in order to allow - 'verbose' settings to be processed first (allows verbose.report - during rc validation process) - DSD - -2006-01-27 Removed setuptools support from setup.py and created a - separate setupegg.py file to replace it. - CM - -2006-01-26 Replaced the ugly datapath logic with a cleaner approach from - http://wiki.python.org/moin/DistutilsInstallDataScattered. - Overrides the install_data command. - CM - -2006-01-24 Don't use character typecodes in cntr.c --- changed to use - defined typenumbers instead. - TEO - -2006-01-24 Fixed some bugs in usetex's and ps.usedistiller's dependency - -2006-01-24 Added masked array support to scatter - EF - -2006-01-24 Fixed some bugs in usetex's and ps.usedistiller's dependency - checking - DSD - -=============================================================== -2006-01-24 Released 0.86.2 - -2006-01-20 Added a converters dict to pylab load to convert selected - coloumns to float -- especially useful for files with date - strings, uses a datestr2num converter - JDH - -2006-01-20 Added datestr2num to matplotlib dates to convert a string - or sequence of strings to a matplotlib datenum - -2006-01-18 Added quadrilateral pcolormesh patch 1409190 by Alex Mont - and Paul Kienzle -- this is *Agg only for now. See - examples/quadmesh_demo.py - JDH - -2006-01-18 Added Jouni's boxplot patch - JDH - -2006-01-18 Added comma delimiter for pylab save - JDH - -2006-01-12 Added Ryan's legend patch - JDH - - -2006-1-12 Fixed numpy / numeric to use .dtype.char to keep in SYNC with numpy SVN - -=============================================================== -2006-1-11 Released 0.86.1 - -2006-1-11 Fixed setup.py for win32 build and added rc template to the MANIFEST.in - -2006-1-10 Added xpdf distiller option. matplotlibrc ps.usedistiller can now be - none, false, ghostscript, or xpdf. Validation checks for - dependencies. This needs testing, but the xpdf option should produce - the highest-quality output and small file sizes - DSD - -2006-01-10 For the usetex option, backend_ps now does all the LaTeX work in the - os's temp directory - DSD - -2006-1-10 Added checks for usetex dependencies. - DSD - -======================================================================= -2006-1-9 Released 0.86 - -2006-1-4 Changed to support numpy (new name for scipy_core) - TEO - -2006-1-4 Added Mark's scaled axes patch for shared axis - -2005-12-28 Added Chris Barker's build_wxagg patch - JDH - -2005-12-27 Altered numerix/scipy to support new scipy package - structure - TEO -2005-12-20 Fixed Jame's Boyles date tick reversal problem - JDH - -2005-12-20 Added Jouni's rc patch to support lists of keys to set on - - JDH - -2005-12-12 Updated pyparsing and mathtext for some speed enhancements - (Thanks Paul McGuire) and minor fixes to scipy numerix and - setuptools - -2005-12-12 Matplotlib data is now installed as package_data in - the matplotlib module. This gets rid of checking the - many possibilities in matplotlib._get_data_path() - CM - -2005-12-11 Support for setuptools/pkg_resources to build and use - matplotlib as an egg. Still allows matplotlib to exist - using a traditional distutils install. - ADS - -2005-12-03 Modified setup to build matplotlibrc based on compile time - findings. It will set numerix in the order of scipy, - numarray, Numeric depending on which are founds, and - backend as in preference order GTKAgg, WXAgg, TkAgg, GTK, - Agg, PS - -2005-12-03 Modified scipy patch to support Numeric, scipy and numarray - Some work remains to be done because some of the scipy - imports are broken if only the core is installed. e.g., - apparently we need from scipy.basic.fftpack import * rather - than from scipy.fftpack import * - -2005-12-03 Applied some fixes to Nicholas Young's nonuniform image - patch - -2005-12-01 Applied Alex Gontmakher hatch patch - PS only for now - -2005-11-30 Added Rob McMullen's EMF patch - -2005-11-30 Added Daishi's patch for scipy - -2005-11-30 Fixed out of bounds draw markers segfault in agg - -2005-11-28 Got TkAgg blitting working 100% (cross fingers) correctly. - CM - -2005-11-27 Multiple changes in cm.py, colors.py, figure.py, image.py, - contour.py, contour_demo.py; new _cm.py, examples/image_masked.py. - 1) Separated the color table data from cm.py out into - a new file, _cm.py, to make it easier to find the actual - code in cm.py and to add new colormaps. Also added - some line breaks to the color data dictionaries. Everything - from _cm.py is imported by cm.py, so the split should be - transparent. - 2) Enabled automatic generation of a colormap from - a list of colors in contour; see modified - examples/contour_demo.py. - 3) Support for imshow of a masked array, with the - ability to specify colors (or no color at all) for - masked regions, and for regions that are above or - below the normally mapped region. See - examples/image_masked.py. - 4) In support of the above, added two new classes, - ListedColormap, and no_norm, to colors.py, and modified - the Colormap class to include common functionality. Added - a clip kwarg to the normalize class. Reworked color - handling in contour.py, especially in the ContourLabeller - mixin. - - EF - -2005-11-25 Changed text.py to ensure color is hashable. EF - -======================================================================= -2005-11-16 Released 0.85 - -2005-11-16 Changed the default default linewidth in rc to 1.0 - -2005-11-16 Replaced agg_to_gtk_drawable with pure pygtk pixbuf code in - backend_gtkagg. When the equivalent is doe for blit, the - agg extension code will no longer be needed - -2005-11-16 Added a maxdict item to cbook to prevent caches from - growing w/o bounds - -2005-11-15 Fixed a colorup/colordown reversal bug in finance.py -- - Thanks Gilles - -2005-11-15 Applied Jouni K Steppanen's boxplot patch SF patch#1349997 - - JDH - - -2005-11-09 added axisbelow attr for Axes to determine whether ticks and such - are above or below the actors - -2005-11-08 Added Nicolas' irregularly spaced image patch - - -2005-11-08 Deprecated HorizontalSpanSelector and replaced with - SpanSelection that takes a third arg, direction. The - new SpanSelector supports horizontal and vertical span - selection, and the appropriate min/max is returned. - CM - -2005-11-08 Added lineprops dialog for gtk - -2005-11-03 Added FIFOBuffer class to mlab to support real time feeds - and examples/fifo_buffer.py - -2005-11-01 Contributed Nickolas Young's patch for afm mathtext to - support mathtext based upon the standard postscript Symbol - font when ps.usetex = True. - -2005-10-26 Added support for scatter legends - thanks John Gill - -2005-10-20 Fixed image clipping bug that made some tex labels - disappear. JDH - -2005-10-14 Removed sqrt from dvipng 1.6 alpha channel mask. - -2005-10-14 Added width kwarg to hist function - -2005-10-10 Replaced all instances of os.rename with shutil.move - -2005-10-05 Added Michael Brady's ydate patch - -2005-10-04 Added rkern's texmanager patch - -2005-09-25 contour.py modified to use a single ContourSet class - that handles filled contours, line contours, and labels; - added keyword arg (clip_ends) to contourf. - Colorbar modified to work with new ContourSet object; - if the ContourSet has lines rather than polygons, the - colorbar will follow suit. Fixed a bug introduced in - 0.84, in which contourf(...,colors=...) was broken - EF - -======================================================================= -2005-09-19 Released 0.84 - -2005-09-14 Added a new 'resize_event' which triggers a callback with a - backend_bases.ResizeEvent object - JDH - -2005-09-14 font_manager.py: removed chkfontpath from x11FontDirectory() - SC - -2005-09-14 Factored out auto date locator/formatter factory code into - matplotlib.date.date_ticker_factory; applies John Bryne's - quiver patch. - -2005-09-13 Added Mark's axes positions history patch #1286915 - -2005-09-09 Added support for auto canvas resizing with - fig.set_figsize_inches(9,5,forward=True) # inches - OR - fig.resize(400,300) # pixels - -2005-09-07 figure.py: update Figure.draw() to use the updated - renderer.draw_image() so that examples/figimage_demo.py works again. - examples/stock_demo.py: remove data_clipping (which no longer - exists) - SC - -2005-09-06 Added Eric's tick.direction patch: in or out in rc - -2005-09-06 Added Martin's rectangle selector widget - -2005-09-04 Fixed a logic err in text.py that was preventing rgxsuper - from matching - JDH - -2005-08-29 Committed Ken's wx blit patch #1275002 - -2005-08-26 colorbar modifications - now uses contourf instead of imshow - so that colors used by contourf are displayed correctly. - Added two new keyword args (cspacing and clabels) that are - only relevant for ContourMappable images - JSWHIT - -2005-08-24 Fixed a PS image bug reported by Darren - JDH - -2005-08-23 colors.py: change hex2color() to accept unicode strings as well as - normal strings. Use isinstance() instead of types.IntType etc - SC - -2005-08-16 removed data_clipping line and rc property - JDH - -2005-08-22 backend_svg.py: Remove redundant "x=0.0 y=0.0" from svg element. - Increase svg version from 1.0 to 1.1. Add viewBox attribute to svg - element to allow SVG documents to scale-to-fit into an arbitrary - viewport - SC - -2005-08-16 Added Eric's dot marker patch - JDH - -2005-08-08 Added blitting/animation for TkAgg - CM - -2005-08-05 Fixed duplicate tickline bug - JDH - -2005-08-05 Fixed a GTK animation bug that cropped up when doing - animations in gtk//gtkagg canvases that had widgets packed - above them - -2005-08-05 Added Clovis Goldemberg patch to the tk save dialog - -2005-08-04 Removed origin kwarg from backend.draw_image. origin is - handled entirely by the frontend now. - -2005-07-03 Fixed a bug related to TeX commands in backend_ps - -2005-08-03 Fixed SVG images to respect upper and lower origins. - -2005-08-03 Added flipud method to image and removed it from to_str. - -2005-07-29 Modified figure.figaspect to take an array or number; - modified backend_svg to write utf-8 - JDH - -2005-07-30 backend_svg.py: embed png image files in svg rather than linking - to a separate png file, fixes bug #1245306 (thanks to Norbert Nemec - for the patch) - SC - -======================================================================= - -2005-07-29 Released 0.83.2 - -2005-07-27 Applied SF patch 1242648: minor rounding error in - IndexDateFormatter in dates.py - -2005-07-27 Applied sf patch 1244732: Scale axis such that circle - looks like circle - JDH -2005-07-29 Improved message reporting in texmanager and backend_ps - DSD - -2005-07-28 backend_gtk.py: update FigureCanvasGTK.draw() (needed due to the - recent expose_event() change) so that examples/anim.py works in the - usual way - SC - -2005-07-26 Added new widgets Cursor and HorizontalSpanSelector to - matplotlib.widgets. See examples/widgets/cursor.py and - examples/widgets/span_selector.py - JDH - -2005-07-26 added draw event to mpl event hierarchy -- triggered on - figure.draw - -2005-07-26 backend_gtk.py: allow 'f' key to toggle window fullscreen mode - -2005-07-26 backend_svg.py: write "<.../>" elements all on one line and remove - surplus spaces - SC - -2005-07-25 backend_svg.py: simplify code by deleting GraphicsContextSVG and - RendererSVG.new_gc(), and moving the gc.get_capstyle() code into - RendererSVG._get_gc_props_svg() - SC - -2005-07-24 backend_gtk.py: call FigureCanvasBase.motion_notify_event() on - all motion-notify-events, not just ones where a modifier key or - button has been pressed (fixes bug report from Niklas Volbers) - SC - -2005-07-24 backend_gtk.py: modify print_figure() use own pixmap, fixing - problems where print_figure() overwrites the display pixmap. - return False from all button/key etc events - to allow the event - to propagate further - SC - -2005-07-23 backend_gtk.py: change expose_event from using set_back_pixmap(); - clear() to draw_drawable() - SC - -2005-07-23 backend_gtk.py: removed pygtk.require() - matplotlib/__init__.py: delete 'FROZEN' and 'McPLError' which are - no longer used - SC - -2005-07-22 backend_gdk.py: removed pygtk.require() - SC - -2005-07-21 backend_svg.py: Remove unused imports. Remove methods doc strings - which just duplicate the docs from backend_bases.py. Rename - draw_mathtext to _draw_mathtext. - SC - -2005-07-17 examples/embedding_in_gtk3.py: new example demonstrating placing - a FigureCanvas in a gtk.ScrolledWindow - SC - -2005-07-14 Fixed a Windows related bug (#1238412) in texmanager - DSD - -2005-07-11 Fixed color kwarg bug, setting color=1 or 0 caused an - exception - DSD - -2005-07-07 Added Eric's MA set_xdata Line2D fix - JDH - -2005-07-06 Made HOME/.matplotlib the new config dir where the - matplotlibrc file, the ttf.cache, and the tex.cache live. - The new default filenames in .matplotlib have no leading - dot and are not hidden. e.g., the new names are matplotlibrc - tex.cache ttffont.cache. This is how ipython does it so it - must be right. If old files are found, a warning is issued - and they are moved to the new location. Also fixed - texmanager to put all files, including temp files in - ~/.matplotlib/tex.cache, which allows you to usetex in - non-writable dirs. - -2005-07-05 Fixed bug #1231611 in subplots adjust layout. The problem - was that the text cacheing mechanism was not using the - transformation affine in the key. - JDH - -2005-07-05 Fixed default backend import problem when using API (SF bug - # 1209354 - see API_CHANGES for more info - JDH - -2005-07-04 backend_gtk.py: require PyGTK version 2.0.0 or higher - SC - -2005-06-30 setupext.py: added numarray_inc_dirs for building against - numarray when not installed in standard location - ADS - -2005-06-27 backend_svg.py: write figure width, height as int, not float. - Update to fix some of the pychecker warnings - SC - -2005-06-23 Updated examples/agg_test.py to demonstrate curved paths - and fills - JDH - -2005-06-21 Moved some texmanager and backend_agg tex caching to class - level rather than instance level - JDH - -2005-06-20 setupext.py: fix problem where _nc_backend_gdk is installed to the - wrong directory - SC - -2005-06-19 Added 10.4 support for CocoaAgg. - CM - -2005-06-18 Move Figure.get_width_height() to FigureCanvasBase and return - int instead of float. - SC - -2005-06-18 Applied Ted Drain's QtAgg patch: 1) Changed the toolbar to - be a horizontal bar of push buttons instead of a QToolbar - and updated the layout algorithms in the main window - accordingly. This eliminates the ability to drag and drop - the toolbar and detach it from the window. 2) Updated the - resize algorithm in the main window to show the correct - size for the plot widget as requested. This works almost - correctly right now. It looks to me like the final size of - the widget is off by the border of the main window but I - haven't figured out a way to get that information yet. We - could just add a small margin to the new size but that - seems a little hacky. 3) Changed the x/y location label to - be in the toolbar like the Tk backend instead of as a - status line at the bottom of the widget. 4) Changed the - toolbar pixmaps to use the ppm files instead of the png - files. I noticed that the Tk backend buttons looked much - nicer and it uses the ppm files so I switched them. - -2005-06-17 Modified the gtk backend to not queue mouse motion events. - This allows for live updates when dragging a slider. - CM - -2005-06-17 Added starter CocoaAgg backend. Only works on OS 10.3 for - now and requires PyObjC. (10.4 is high priority) - CM - -2005-06-17 Upgraded pyparsing and applied Paul McGuire's suggestions - for speeding things up. This more than doubles the speed - of mathtext in my simple tests. JDH - -2005-06-16 Applied David Cooke's subplot make_key patch - -======================================================== - -2005-06-15 0.82 released - -2005-06-15 Added subplot config tool to GTK* backends -- note you must - now import the NavigationToolbar2 from your backend of - choice rather than from backend_gtk because it needs to - know about the backend specific canvas -- see - examples/embedding_in_gtk2.py. Ditto for wx backend -- see - examples/embedding_in_wxagg.py - -2005-06-15 backend_cairo.py: updated to use pycairo 0.5.0 - SC - -2005-06-14 Wrote some GUI neutral widgets (Button, Slider, - RadioButtons, CheckButtons) in matplotlib.widgets. See - examples/widgets/*.py - JDH - -2005-06-14 Exposed subplot parameters as rc vars and as the fig - SubplotParams instance subplotpars. See - figure.SubplotParams, figure.Figure.subplots_adjust and the - pylab method subplots_adjust and - examples/subplots_adjust.py . Also added a GUI neutral - widget for adjusting subplots, see - examples/subplot_toolbar.py - JDH - -2005-06-13 Exposed cap and join style for lines with new rc params and - line properties - - lines.dash_joinstyle : miter # miter|round|bevel - lines.dash_capstyle : butt # butt|round|projecting - lines.solid_joinstyle : miter # miter|round|bevel - lines.solid_capstyle : projecting # butt|round|projecting - - -2005-06-13 Added kwargs to Axes init - -2005-06-13 Applied Baptiste's tick patch - JDH - -2005-06-13 Fixed rc alias 'l' bug reported by Fernando by removing - aliases for mainlevel rc options. - JDH - -2005-06-10 Fixed bug #1217637 in ticker.py - DSD - -2005-06-07 Fixed a bug in texmanager.py: .aux files not being removed - DSD - -2005-06-08 Added Sean Richard's hist binning fix -- see API_CHANGES - JDH - -2005-06-07 Fixed a bug in texmanager.py: .aux files not being removed - - DSD - - -===================================================================== - -2005-06-07 matplotlib-0.81 released - -2005-06-06 Added autoscale_on prop to axes - -2005-06-06 Added Nick's picker "among" patch - JDH - -2005-06-05 Fixed a TeX/LaTeX font discrepency in backend_ps. - DSD - -2005-06-05 Added a ps.distill option in rc settings. If True, postscript - output will be distilled using ghostscript, which should trim - the file size and allow it to load more quickly. Hopefully this - will address the issue of large ps files due to font - definitions. Tested with gnu-ghostscript-8.16. - DSD - -2005-06-03 Improved support for tex handling of text in backend_ps. - DSD - -2005-06-03 Added rc options to render text with tex or latex, and to select - the latex font package. - DSD - -2005-06-03 Fixed a bug in ticker.py causing a ZeroDivisionError - -2005-06-02 backend_gtk.py remove DBL_BUFFER, add line to expose_event to - try to fix pygtk 2.6 redraw problem - SC - -2005-06-01 The default behavior of ScalarFormatter now renders scientific - notation and large numerical offsets in a label at the end of - the axis. - DSD - -2005-06-01 Added Nicholas' frombyte image patch - JDH - -2005-05-31 Added vertical TeX support for agg - JDH - -2005-05-31 Applied Eric's cntr patch - JDH - -2005-05-27 Finally found the pesky agg bug (which Maxim was kind - enough to fix within hours) that was causing a segfault in - the win32 cached marker drawing. Now windows users can get - the enormouse performance benefits of caced markers w/o - those occasional pesy screenshots. - JDH - -2005-05-27 Got win32 build system working again, using a more recent - version of gtk and pygtk in the win32 build, gtk 2.6 from - http://www.gimp.org/~tml/gimp/win32/downloads.html (you - will also need libpng12.dll to use these). I haven't - tested whether this binary build of mpl for win32 will work - with older gtk runtimes, so you may need to upgrade. - -2005-05-27 Fixed bug where 2nd wxapp could be started if using wxagg - backend. - ADS - -2005-05-26 Added Daishi text with dash patch -- see examples/dashtick.py - -2005-05-26 Moved backend_latex functionality into backend_ps. If - text.usetex=True, the PostScript backend will use LaTeX to - generate the .ps or .eps file. Ghostscript is required for - eps output. - DSD - -2005-05-24 Fixed alignment and color issues in latex backend. - DSD - -2005-05-21 Fixed raster problem for small rasters with dvipng -- looks - like it was a premultipled alpha problem - JDH - -2005-05-20 Added linewidth and faceted kwarg to scatter to control - edgewidth and color. Also added autolegend patch to - inspect line segments. - -2005-05-18 Added Orsay and JPL qt fixes - JDH - -2005-05-17 Added a psfrag latex backend -- some alignment issues need - to be worked out. Run with -dLaTeX and a *.tex file and - *.eps file are generated. latex and dvips the generated - latex file to get ps output. Note xdvi *does* not work, - you must generate ps.- JDH - -2005-05-13 Added Florent Rougon's Axis set_label1 - patch - -2005-05-17 pcolor optimization, fixed bug in previous pcolor patch - JSWHIT - -2005-05-16 Added support for masked arrays in pcolor - JSWHIT - - -2005-05-12 Started work on TeX text for antigrain using pngdvi -- see - examples/tex_demo.py and the new module - matplotlib.texmanager. Rotated text not supported and - rendering small glyps is not working right yet. BUt large - fontsizes and/or high dpi saved figs work great. - -2005-05-10 New image resize options interpolation options. New values - for the interp kwarg are - - 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', - 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', - 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', - 'lanczos', 'blackman' - - See help(imshow) for details, particularly the - interpolation, filternorm and filterrad kwargs - - -2005-05-10 Applied Eric's contour mem leak fixes - JDH - -2005-05-10 Extended python agg wrapper and started implementing - backend_agg2, an agg renderer based on the python wrapper. - This will be more flexible and easier to extend than the - current backend_agg. See also examples/agg_test.py - JDH - -2005-05-09 Added Marcin's no legend patch to exclude lines from the - autolegend builder - - plot(x, y, label='nolegend') - -2005-05-05 Upgraded to agg23 - -2005-05-05 Added newscalarformatter_demo.py to examples. -DSD - -2005-05-04 Added NewScalarFormatter. Improved formatting of ticklabels, - scientific notation, and the ability to plot large large - numbers with small ranges, by determining a numerical offset. - See ticker.NewScalarFormatter for more details. -DSD - -2005-05-03 Added the option to specify a delimiter in pylab.load -DSD - -2005-04-28 Added Darren's line collection example - -2005-04-28 Fixed aa property in agg - JDH - -2005-04-27 Set postscript page size in .matplotlibrc - DSD - -2005-04-26 Added embedding in qt example. - JDH - -2005-04-14 Applied Michael Brady's qt backend patch: 1) fix a bug - where keyboard input was grabbed by the figure and not - released 2) turn on cursor changes 3) clean up a typo - and commented-out print statement. - JDH - - -2005-04-14 Applied Eric Firing's masked data lines patch and contour - patch. Support for masked arrays has been added to the - plot command and to the Line2D object. Only the valid - points are plotted. A "valid_only" kwarg was added to the - get_xdata() and get_ydata() methods of Line2D; by default - it is False, so that the original data arrays are - returned. Setting it to True returns the plottable points. - - see examples/masked_demo.py - JDH - -2005-04-13 Applied Tim Leslie's arrow key event handling patch - JDH - - -================================================================= -0.80 released - -2005-04-11 Applied a variant of rick's xlim/ylim/axis patch. These - functions now take kwargs to let you selectively alter only - the min or max if desired. e.g., xlim(xmin=2) or - axis(ymax=3). They always return the new lim. - JDH - - -2005-04-11 Incorporated Werner's wx patch -- wx backend should be - compatible with wxpython2.4 and recent versions of 2.5. - Some early versions of wxpython 2.5 will not work because - there was a temporary change in the dc API that was rolled - back to make it 2.4 compliant - -2005-04-11 modified tkagg show so that new figure window pops up on - call to figure - -2005-04-11 fixed wxapp init bug - -2005-04-02 updated backend_ps.draw_lines, draw_markers for use with the - new API - DSD - -2005-04-01 Added editable polygon example - -========================================================================== - -2005-03-31 0.74 released - -2005-03-30 Fixed and added checks for floating point inaccuracy in - ticker.Base - DSD - -2005-03-30 updated /ellipse definition in backend_ps.py to address bug - #1122041 - DSD - -2005-03-29 Added unicode support for Agg and PS - JDH - -2005-03-28 Added Jarrod's svg patch for text - JDH - -2005-03-28 Added Ludal's arrow and quiver patch - JDH - -2005-03-28 Added label kwarg to Axes to facilitate forcing the - creation of new Axes with otherwise identical attributes - -2005-03-28 Applied boxplot and OSX font search patches - -2005-03-27 Added ft2font NULL check to fix Japanase font bug - JDH - -2005-03-27 Added sprint legend patch plus John Gill's tests and fix -- - see examples/legend_auto.py - JDH - -========================================================================== - -2005-03-19 0.73.1 released - -2005-03-19 Reverted wxapp handling because it crashed win32 - JDH - -2005-03-18 Add .number attribute to figure objects returned by figure() - FP - -=========================================================================== -2005-03-18 0.73 released - -2005-03-16 Fixed labelsep bug - -2005-03-16 Applied Darren's ticker fix for small ranges - JDH - -2005-03-16 Fixed tick on horiz colorbar - JDH - -2005-03-16 Added Japanses winreg patch - JDH - -2005-03-15 backend_gtkagg.py: changed to use double buffering, this fixes - the problem reported Joachim Berdal Haga - "Parts of plot lagging - from previous frame in animation". Tested with anim.py and it makes - no noticable difference to performance (23.7 before, 23.6 after) - - SC - -2005-03-14 add src/_backend_gdk.c extension to provide a substitute function - for pixbuf.get_pixels_array(). Currently pixbuf.get_pixels_array() - only works with Numeric, and then only works if pygtk has been - compiled with Numeric support. The change provides a function - pixbuf_get_pixels_array() which works with Numeric and numarray and - is always available. It means that backend_gtk should be able to - display images and mathtext in all circumstances. - SC - -2005-03-11 Upgraded CXX to 5.3.1 - -2005-03-10 remove GraphicsContextPS.set_linestyle() - and GraphicsContextSVG.set_linestyle() since they do no more than - the base class GraphicsContext.set_linestyle() - SC - -2005-03-09 Refactored contour functionality into dedicated module - -2005-03-09 Added Eric's contourf updates and Nadia's clabel functionality - -2005-03-09 Moved colorbar to figure.Figure to expose it for API developers - - JDH - -2005-03-09 backend_cairo.py: implemented draw_markers() - SC - -2005-03-09 cbook.py: only use enumerate() (the python version) if the builtin - version is not available. - Add new function 'izip' which is set to itertools.izip if available - and the python equivalent if not available. - SC - -2005-03-07 backend_gdk.py: remove PIXELS_PER_INCH from points_to_pixels(), but - still use it to adjust font sizes. This allows the GTK version of - line_styles.py to more closely match GTKAgg, previously the markers - were being drawn too large. - SC - -2005-03-01 Added Eric's contourf routines - -2005-03-01 Added start of proper agg SWIG wrapper. I would like to - expose agg functionality directly a the user level and this - module will serve that purpose eventually, and will - hopefully take over most of the functionality of the - current _image and _backend_agg modules. - JDH - -2005-02-28 Fixed polyfit / polyval to convert input args to float - arrays - JDH - - -2005-02-25 Add experimental feature to backend_gtk.py to enable/disable - double buffering (DBL_BUFFER=True/False) - SC - -2005-02-24 colors.py change ColorConverter.to_rgb() so it always returns rgb - (and not rgba), allow cnames keys to be cached, change the exception - raised from RuntimeError to ValueError (like hex2color()) - hex2color() use a regular expression to check the color string is - valid - SC - - -2005-02-23 Added rc param ps.useafm so backend ps can use native afm - fonts or truetype. afme breaks mathtext but causes much - smaller font sizes and may result in images that display - better in some contexts (e.g., pdfs incorporated into latex - docs viewed in acrobat reader). I would like to extend - this approach to allow the user to use truetype only for - mathtext, which should be easy. - -2005-02-23 Used sequence protocol rather than tuple in agg collection - drawing routines for greater flexibility - JDH - - -=========================================================== -2005-02-22 0.72.1 released - -2005-02-21 fixed linestyles for collections -- contour now dashes for - levels <0 - -2005-02-21 fixed ps color bug - JDH - -2005-02-15 fixed missing qt file - -2005-02-15 banished error_msg and report_error. Internal backend - methods like error_msg_gtk are preserved. backend writers, - check your backends, and diff against 0.72 to make sure I - did the right thing! - JDH - - -2005-02-14 Added enthought traits to matplotlib tree - JDH - -=========================================================== - -2005-02-14 0.72 released - -2005-02-14 fix bug in cbook alltrue() and onetrue() - SC - -2005-02-11 updated qtagg backend from Ted - JDH - -2005-02-11 matshow fixes for figure numbering, return value and docs - FP - -2005-02-09 new zorder example for fine control in zorder_demo.py - FP - -2005-02-09 backend renderer draw_lines now has transform in backend, - as in draw_markers; use numerix in _backend_agg, aded small - line optimization to agg - -2005-02-09 subplot now deletes axes that it overlaps - -2005-02-08 Added transparent support for gzipped files in load/save - Fernando - Perez (FP from now on). - -2005-02-08 Small optimizations in PS backend. They may have a big impact for - large plots, otherwise they don't hurt - FP - -2005-02-08 Added transparent support for gzipped files in load/save - Fernando - Perez (FP from now on). - -2005-02-07 Added newstyle path drawing for markers - only implemented - in agg currently - JDH - -2005-02-05 Some superscript text optimizations for ticking log plots - -2005-02-05 Added some default key press events to pylab figures: 'g' - toggles grid - JDH - -2005-02-05 Added some support for handling log switching for lines - that have nonpos data - JDH - -2005-02-04 Added Nadia's contour patch - contour now has matlab - compatible syntax; this also fixed an unequal sized contour - array bug- JDH - -2005-02-04 Modified GTK backends to allow the FigureCanvas to be resized - smaller than its original size - SC - -2005-02-02 Fixed a bug in dates mx2num - JDH - -2005-02-02 Incorporated Fernando's matshow - JDH - -2005-02-01 Added Fernando's figure num patch, including experemental - support for pylab backend switching, LineCOllection.color - warns, savefig now a figure method, fixed a close(fig) bug - - JDH - -2005-01-31 updated datalim in contour - JDH - -2005-01-30 Added backend_qtagg.py provided by Sigve Tjora - SC - -2005-01-28 Added tk.inspect rc param to .matplotlibrc. IDLE users - should set tk.pythoninspect:True and interactive:True and - backend:TkAgg - -2005-01-28 Replaced examples/interactive.py with an updated script from - Fernando Perez - SC - -2005-01-27 Added support for shared x or y axes. See - examples/shared_axis_demo.py and examples/ganged_plots.py - -2005-01-27 Added Lee's patch for missing symbols \leq and \LEFTbracket - to _mathtext_data - JDH - -2005-01-26 Added Baptiste's two scales patch -- see help(twinx) in the - pylab interface for more info. See also - examples/two_scales.py - -2005-01-24 Fixed a mathtext parser bug that prevented font changes in - sub/superscripts - JDH - -2005-01-24 Fixed contour to work w/ interactive changes in colormaps, - clim, etc - JDH - -=============================================================== - -2005-01-21 matplotlib-0.71 released - -2005-01-21 Refactored numerix to solve vexing namespace issues - JDH - -2005-01-21 Applied Nadia's contour bug fix - JDH - -2005-01-20 Made some changes to the contour routine - particularly - region=1 seems t fix a lot of the zigzag strangeness. - Added colormaps as default for contour - JDH - -2005-01-19 Restored builtin names which were overridden (min, max, - abs, round, and sum) in pylab. This is a potentially - significant change for those who were relying on an array - version of those functions that previously overrode builtin - function names. - ADS - -2005-01-18 Added accents to mathtext: \hat, \breve, \grave, \bar, - \acute, \tilde, \vec, \dot, \ddot. All of them have the - same syntax, e.g., to make an overbar you do \bar{o} or to - make an o umlaut you do \ddot{o}. The shortcuts are also - provided, e.g., \"o \'e \`e \~n \.x \^y - JDH - -2005-01-18 Plugged image resize memory leaks - JDH - -2005-01-18 Fixed some mathtext parser problems relating to superscripts - -2005-01-17 Fixed a yticklabel problem for colorbars under change of - clim - JDH - -2005-01-17 Cleaned up Destroy handling in wx reducing memleak/fig from - approx 800k to approx 6k- JDH - -2005-01-17 Added kappa to latex_to_bakoma - JDH - -2005-01-15 Support arbitrary colorbar axes and horizontal colorbars - JDH - -2005-01-15 Fixed colormap number of colors bug so that the colorbar - has the same discretization as the image - JDH - -2005-01-15 Added Nadia's x,y contour fix - JDH - -2005-01-15 backend_cairo: added PDF support which requires pycairo 0.1.4. - Its not usable yet, but is ready for when the Cairo PDF backend - matures - SC - -2005-01-15 Added Nadia's x,y contour fix - -2005-01-12 Fixed set clip_on bug in artist - JDH - -2005-01-11 Reverted pythoninspect in tkagg - JDH - -2005-01-09 Fixed a backend_bases event bug caused when an event is - triggered when location is None - JDH - -2005-01-07 Add patch from Stephen Walton to fix bug in pylab.load() - when the % character is included in a comment. - ADS - -2005-01-07 Added markerscale attribute to Legend class. This allows - the marker size in the legend to be adjusted relative to - that in the plot. - ADS - -2005-01-06 Add patch from Ben Vanhaeren to make the FigureManagerGTK vbox a - public attribute - SC - -==================================================================== -2004-12-30 Release 0.70 - -2004-12-28 Added coord location to key press and added a - examples/picker_demo.py - -2004-12-28 Fixed coords notification in wx toolbar - JDH - -2004-12-28 Moved connection and disconnection event handling to the - FigureCanvasBase. Backends now only need to connect one - time for each of the button press, button release and key - press/release functions. The base class deals with - callbacks and multiple connections. This fixes flakiness - on some backends (tk, wx) in the presence of multiple - connections and/or disconnect - JDH - -2004-12-27 Fixed PS mathtext bug where color was not set - Jochen - please verify correct - JDH - -2004-12-27 Added Shadow class and added shadow kwarg to legend and pie - for shadow effect - JDH - -2004-12-27 Added pie charts and new example/pie_demo.py - -2004-12-23 Fixed an agg text rotation alignment bug, fixed some text - kwarg processing bugs, and added examples/text_rotation.py - to explain and demonstrate how text rotations and alignment - work in matplotlib. - JDH - -====================================================================== - -2004-12-22 0.65.1 released - JDH - -2004-12-22 Fixed colorbar bug which caused colorbar not to respond to - changes in colormap in some instances - JDH - -2004-12-22 Refactored NavigationToolbar in tkagg to support app - embedding , init now takes (canvas, window) rather than - (canvas, figman) - JDH - -2004-12-21 Refactored axes and subplot management - removed - add_subplot and add_axes from the FigureManager. classic - toolbar updates are done via an observer pattern on the - figure using add_axobserver. Figure now maintains the axes - stack (for gca) and supports axes deletion. Ported changes - to GTK, Tk, Wx, and FLTK. Please test! Added delaxes - JDH - -2004-12-21 Lots of image optimizations - 4x performance boost over - 0.65 JDH - -2004-12-20 Fixed a figimage bug where the axes is shown and modified - tkagg to move the destroy binding into the show method. - -2004-12-18 Minor refactoring of NavigationToolbar2 to support - embedding in an application - JDH - -2004-12-14 Added linestyle to collections (currently broken) - JDH - -2004-12-14 Applied Nadia's setupext patch to fix libstdc++ link - problem with contour and solaris -JDH - -2004-12-14 A number of pychecker inspired fixes, including removal of - True and False from cbook which I erroneously thought was - needed for python2.2 - JDH - -2004-12-14 Finished porting doc strings for set introspection. - Used silent_list for many get funcs that return - lists. JDH - -2004-12-13 dates.py: removed all timezone() calls, except for UTC - SC - -====================================================================== - -2004-12-13 0.65 released - JDH - -2004-12-13 colors.py: rgb2hex(), hex2color() made simpler (and faster), also - rgb2hex() - added round() instead of integer truncation - hex2color() - changed 256.0 divisor to 255.0, so now - '#ffffff' becomes (1.0,1.0,1.0) not (0.996,0.996,0.996) - SC - -2004-12-11 Added ion and ioff to pylab interface - JDH - -2004-12-11 backend_template.py: delete FigureCanvasTemplate.realize() - most - backends don't use it and its no longer needed - - backend_ps.py, backend_svg.py: delete show() and - draw_if_interactive() - they are not needed for image backends - - backend_svg.py: write direct to file instead of StringIO - - SC - -2004-12-10 Added zorder to artists to control drawing order of lines, - patches and text in axes. See examples/zoder_demo.py - JDH - -2004-12-10 Fixed colorbar bug with scatter - JDH - -2004-12-10 Added Nadia Dencheva contour code - JDH - -2004-12-10 backend_cairo.py: got mathtext working - SC - -2004-12-09 Added Norm Peterson's svg clipping patch - -2004-12-09 Added Matthew Newville's wx printing patch - -2004-12-09 Migrated matlab to pylab - JDH - -2004-12-09 backend_gtk.py: split into two parts - - backend_gdk.py - an image backend - - backend_gtk.py - A GUI backend that uses GDK - SC - -2004-12-08 backend_gtk.py: remove quit_after_print_xvfb(*args), show_xvfb(), - Dialog_MeasureTool(gtk.Dialog) one month after sending mail to - matplotlib-users asking if anyone still uses these functions - SC - -2004-12-02 backend_bases.py, backend_template.py: updated some of the method - documentation to make them consistent with each other - SC - -2004-12-04 Fixed multiple bindings per event for TkAgg mpl_connect and - mpl_disconnect. Added a "test_disconnect" command line - parameter to coords_demo.py JTM - -2004-12-04 Fixed some legend bugs JDH - -2004-11-30 Added over command for oneoff over plots. e.g., over(plot, x, - y, lw=2). Works with any plot function. - -2004-11-30 Added bbox property to text - JDH - -2004-11-29 Zoom to rect now respect reversed axes limits (for both - linear and log axes). - GL - -2004-11-29 Added the over command to the matlab interface. over - allows you to add an overlay plot regardless of hold - state. - JDH - -2004-11-25 Added Printf to mplutils for printf style format string - formatting in C++ (should help write better exceptions) - -2004-11-24 IMAGE_FORMAT: remove from agg and gtkagg backends as its no longer - used - SC - -2004-11-23 Added matplotlib compatible set and get introspection. See - set_and_get.py - -2004-11-23 applied Norbert's patched and exposed legend configuration - to kwargs - JDH - -2004-11-23 backend_gtk.py: added a default exception handler - SC - -2004-11-18 backend_gtk.py: change so that the backend knows about all image - formats and does not need to use IMAGE_FORMAT in other backends - SC - -2004-11-18 Fixed some report_error bugs in string interpolation as - reported on SF bug tracker- JDH - -2004-11-17 backend_gtkcairo.py: change so all print_figure() calls render using - Cairo and get saved using backend_gtk.print_figure() - SC - -2004-11-13 backend_cairo.py: Discovered the magic number (96) required for - Cairo PS plots to come out the right size. Restored Cairo PS output - and added support for landscape mode - SC - -2004-11-13 Added ishold - JDH - -2004-11-12 Added many new matlab colormaps - autumn bone cool copper - flag gray hot hsv jet pink prism spring summer winter - PG - -2004-11-11 greatly simplify the emitted postscript code - JV - -2004-11-12 Added new plotting functions spy, spy2 for sparse matrix - visualization - JDH - -2004-11-11 Added rgrids, thetragrids for customizing the grid - locations and labels for polar plots - JDH - -2004-11-11 make the Gtk backends build without an X-server connection - JV - -2004-11-10 matplotlib/__init__.py: Added FROZEN to signal we are running under - py2exe (or similar) - is used by backend_gtk.py - SC - -2004-11-09 backend_gtk.py: Made fix suggested by maffew@cat.org.au - to prevent problems when py2exe calls pygtk.require(). - SC - -2004-11-09 backend_cairo.py: Added support for printing to a fileobject. - Disabled cairo PS output which is not working correctly. - SC - -============================================================== -2004-11-08 matplotlib-0.64 released - -2004-11-04 Changed -dbackend processing to only use known backends, so - we don't clobber other non-matplotlib uses of -d, like -debug. - -2004-11-04 backend_agg.py: added IMAGE_FORMAT to list the formats that the - backend can save to. - backend_gtkagg.py: added support for saving JPG files by using the - GTK backend - SC - -2004-10-31 backend_cairo.py: now produces png and ps files (although the figure - sizing needs some work). pycairo did not wrap all the necessary - functions, so I wrapped them myself, they are included in the - backend_cairo.py doc string. - SC - -2004-10-31 backend_ps.py: clean up the generated PostScript code, use - the PostScript stack to hold itermediate values instead of - storing them in the dictionary. - JV - -2004-10-30 backend_ps.py, ft2font.cpp, ft2font.h: fix the position of - text in the PostScript output. The new FT2Font method - get_descent gives the distance between the lower edge of - the bounding box and the baseline of a string. In - backend_ps the text is shifted upwards by this amount. - JV - -2004-10-30 backend_ps.py: clean up the code a lot. Change the - PostScript output to be more DSC compliant. All - definitions for the generated PostScript are now in a - PostScript dictionary 'mpldict'. Moved the long comment - about drawing ellipses from the PostScript output into a - Python comment. - JV - -2004-10-30 backend_gtk.py: removed FigureCanvasGTK.realize() as its no longer - needed. Merged ColorManager into GraphicsContext - backend_bases.py: For set_capstyle/joinstyle() only set cap or - joinstyle if there is no error. - SC - -2004-10-30 backend_gtk.py: tidied up print_figure() and removed some of the - dependency on widget events - SC - -2004-10-28 backend_cairo.py: The renderer is complete except for mathtext, - draw_image() and clipping. gtkcairo works reasonably well. cairo - does not yet create any files since I can't figure how to set the - 'target surface', I don't think pycairo wraps the required functions - - SC - -2004-10-28 backend_gtk.py: Improved the save dialog (GTK 2.4 only) so it - presents the user with a menu of supported image formats - SC - -2004-10-28 backend_svg.py: change print_figure() to restore original face/edge - color - backend_ps.py : change print_figure() to ensure original face/edge - colors are restored even if there's an IOError - SC - -2004-10-27 Applied Norbert's errorbar patch to support barsabove kwarg - -2004-10-27 Applied Norbert's legend patch to support None handles - -2004-10-27 Added two more backends: backend_cairo.py, backend_gtkcairo.py - They are not complete yet, currently backend_gtkcairo just renders - polygons, rectangles and lines - SC - -2004-10-21 Added polar axes and plots - JDH - -2004-10-20 Fixed corrcoef bug exposed by corrcoef(X) where X is matrix - - JDH - -2004-10-19 Added kwarg support to xticks and yticks to set ticklabel - text properties -- thanks to T. Edward Whalen for the suggestion - -2004-10-19 Added support for PIL images in imshow(), image.py - ADS - -2004-10-19 Re-worked exception handling in _image.py and _transforms.py - to avoid masking problems with shared libraries. - JTM - -2004-10-16 Streamlined the matlab interface wrapper, removed the - noplot option to hist - just use mlab.hist instead. - -2004-09-30 Added Andrew Dalke's strftime code to extend the range of - dates supported by the DateFormatter - JDH - -2004-09-30 Added barh - JDH - -2004-09-30 Removed fallback to alternate array package from numerix - so that ImportErrors are easier to debug. JTM - -2004-09-30 Add GTK+ 2.4 support for the message in the toolbar. SC - -2004-09-30 Made some changes to support python22 - lots of doc - fixes. - JDH - -2004-09-29 Added a Verbose class for reporting - JDH - -============================================================== - -2004-09-28 Released 0.63.0 - -2004-09-28 Added save to file object for agg - see - examples/print_stdout.py - -2004-09-24 Reorganized all py code to lib subdir - -2004-09-24 Fixed axes resize image edge effects on interpolation - - required upgrade to agg22 which fixed an agg bug related to - this problem - -2004-09-20 Added toolbar2 message display for backend_tkagg. JTM - - -2004-09-17 Added coords formatter attributes. These must be callable, - and return a string for the x or y data. These will be used - to format the x and y data for the coords box. Default is - the axis major formatter. e.g.: - - # format the coords message box - def price(x): return '$%1.2f'%x - ax.format_xdata = DateFormatter('%Y-%m-%d') - ax.format_ydata = price - - -2004-09-17 Total rewrite of dates handling to use python datetime with - num2date, date2num and drange. pytz for timezone handling, - dateutils for spohisticated ticking. date ranges from - 0001-9999 are supported. rrules allow arbitrary date - ticking. examples/date_demo*.py converted to show new - usage. new example examples/date_demo_rrule.py shows how - to use rrules in date plots. The date locators are much - more general and almost all of them have different - constructors. See matplotlib.dates for more info. - -2004-09-15 Applied Fernando's backend __init__ patch to support easier - backend maintenance. Added his numutils to mlab. JDH - -2004-09-16 Re-designated all files in matplotlib/images as binary and - w/o keyword substitution using "cvs admin -kb *.svg ...". - See binary files in "info cvs" under Linux. This was messing - up builds from CVS on windows since CVS was doing lf -> cr/lf - and keyword substitution on the bitmaps. - JTM - -2004-09-15 Modified setup to build array-package-specific extensions - for those extensions which are array-aware. Setup builds - extensions automatically for either Numeric, numarray, or - both, depending on what you have installed. Python proxy - modules for the array-aware extensions import the version - optimized for numarray or Numeric determined by numerix. - - JTM - -2004-09-15 Moved definitions of infinity from mlab to numerix to avoid - divide by zero warnings for numarray - JTM - -2004-09-09 Added axhline, axvline, axhspan and axvspan - -============================================================== -2004-08-30 matplotlib 0.62.4 released - -2004-08-30 Fixed a multiple images with different extent bug, - Fixed markerfacecolor as RGB tuple - -2004-08-27 Mathtext now more than 5x faster. Thanks to Paul Mcguire - for fixes both to pyparsing and to the matplotlib grammar! - mathtext broken on python2.2 - -2004-08-25 Exposed Darren's and Greg's log ticking and formatting - options to semilogx and friends - -2004-08-23 Fixed grid w/o args to toggle grid state - JDH - -2004-08-11 Added Gregory's log patches for major and minor ticking - -2004-08-18 Some pixel edge effects fixes for images - -2004-08-18 Fixed TTF files reads in backend_ps on win32. - -2004-08-18 Added base and subs properties for logscale plots, user - modifiable using - set_[x,y]scale('log',base=b,subs=[mt1,mt2,...]) - GL - -2004-08-18 fixed a bug exposed by trying to find the HOME dir on win32 - thanks to Alan Issac for pointing to the light - JDH - -2004-08-18 fixed errorbar bug in setting ecolor - JDH - -2004-08-12 Added Darren Dale's exponential ticking patch - -2004-08-11 Added Gregory's fltkagg backend - -========================================================================== -2004-08-09 matplotlib-0.61.0 released - -2004-08-08 backend_gtk.py: get rid of the final PyGTK deprecation warning by - replacing gtkOptionMenu with gtkMenu in the 2.4 version of the - classic toolbar. - -2004-08-06 Added Tk zoom to rect rectangle, proper idle drawing, and - keybinding - JDH - -2004-08-05 Updated installing.html and INSTALL - JDH - -2004-08-01 backend_gtk.py: move all drawing code into the expose_event() - -2004-07-28 Added Greg's toolbar2 and backend_*agg patches - JDH - -2004-07-28 Added image.imread with support for loading png into - numerix arrays - -2004-07-28 Added key modifiers to events - implemented dynamic updates - and rubber banding for interactive pan/zoom - JDH - -2004-07-27 did a readthrough of SVG, replacing all the string - additions with string interps for efficiency, fixed some - layout problems, added font and image support (through - external pngs) - JDH - -2004-07-25 backend_gtk.py: modify toolbar2 to make it easier to support GTK+ - 2.4. Add GTK+ 2.4 toolbar support. - SC - -2004-07-24 backend_gtk.py: Simplified classic toolbar creation - SC - -2004-07-24 Added images/matplotlib.svg to be used when GTK+ windows are - minimised - SC - -2004-07-22 Added right mouse click zoom for NavigationToolbar2 panning - mode. - JTM - -2004-07-22 Added NavigationToolbar2 support to backend_tkagg. - Minor tweak to backend_bases. - JTM - -2004-07-22 Incorporated Gergory's renderer cache and buffer object - cache - JDH - -2004-07-22 Backend_gtk.py: Added support for GtkFileChooser, changed - FileSelection/FileChooser so that only one instance pops up, - and made them both modal. - SC - -2004-07-21 Applied backend_agg memory leak patch from hayden - - jocallo@online.no. Found and fixed a leak in binary - operations on transforms. Moral of the story: never incref - where you meant to decref! Fixed several leaks in ft2font: - moral of story: almost always return Py::asObject over - Py::Object - JDH - -2004-07-21 Fixed a to string memory allocation bug in agg and image - modules - JDH - -2004-07-21 Added mpl_connect and mpl_disconnect to matlab interface - - JDH - -2004-07-21 Added beginnings of users_guide to CVS - JDH - -2004-07-20 ported toolbar2 to wx - -2004-07-20 upgraded to agg21 - JDH - -2004-07-20 Added new icons for toolbar2 - JDH - -2004-07-19 Added vertical mathtext for *Agg and GTK - thanks Jim - Benson! - JDH - -2004-07-16 Added ps/eps/svg savefig options to wx and gtk JDH - -2004-07-15 Fixed python framework tk finder in setupext.py - JDH - -2004-07-14 Fixed layer images demo which was broken by the 07/12 image - extent fixes - JDH - -2004-07-13 Modified line collections to handle arbitrary length - segments for each line segment. - JDH - -2004-07-13 Fixed problems with image extent and origin - - set_image_extent deprecated. Use imshow(blah, blah, - extent=(xmin, xmax, ymin, ymax) instead - JDH - -2004-07-12 Added prototype for new nav bar with codifed event - handling. Use mpl_connect rather than connect for - matplotlib event handling. toolbar style determined by rc - toolbar param. backend status: gtk: prototype, wx: in - progress, tk: not started - JDH - -2004-07-11 backend_gtk.py: use builtin round() instead of redefining it. - - SC - -2004-07-10 Added embedding_in_wx3 example - ADS - -2004-07-09 Added dynamic_image_wxagg to examples - ADS - -2004-07-09 added support for embedding TrueType fonts in PS files - PEB - -2004-07-09 fixed a sfnt bug exposed if font cache is not built - -2004-07-09 added default arg None to matplotlib.matlab grid command to - toggle current grid state - -============================ - -2004-07-08 0.60.2 released - -2004-07-08 fixed a mathtext bug for '6' - -2004-07-08 added some numarray bug workarounds - -======= - -2004-07-07 0.60 released - -2004-07-07 Fixed a bug in dynamic_demo_wx - - -2004-07-07 backend_gtk.py: raise SystemExit immediately if - 'import pygtk' fails - SC - -2004-07-05 Added new mathtext commands \over{sym1}{sym2} and - \under{sym1}{sym2} - -2004-07-05 Unified image and patch collections colormapping and - scaling args. Updated docstrings for all - JDH - -2004-07-05 Fixed a figure legend bug and added - examples/figlegend_demo.py - JDH - -2004-07-01 Fixed a memory leak in image and agg to string methods - -2004-06-25 Fixed fonts_demo spacing problems and added a kwargs - version of the fonts_demo fonts_demo_kw.py - JDH - -2004-06-25 finance.py: handle case when urlopen() fails - SC - -2004-06-24 Support for multiple images on axes and figure, with - blending. Support for upper and lower image origins. - clim, jet and gray functions in matlab interface operate on - current image - JDH - -2004-06-23 ported code to Perry's new colormap and norm scheme. Added - new rc attributes image.aspect, image.interpolation, - image.cmap, image.lut, image.origin - -2004-06-20 backend_gtk.py: replace gtk.TRUE/FALSE with True/False. - simplified _make_axis_menu(). - SC - -2004-06-19 anim_tk.py: Updated to use TkAgg by default (not GTK) - backend_gtk_py: Added '_' in front of private widget - creation functions - SC - -2004-06-17 backend_gtk.py: Create a GC once in realise(), not every - time draw() is called. - SC - -2004-06-16 Added new py2exe FAQ entry and added frozen support in - get_data_path for py2exe - JDH - -2004-06-16 Removed GTKGD, which was always just a proof-of-concept - backend - JDH - -2004-06-16 backend_gtk.py updates to replace deprecated functions - gtk.mainquit(), gtk.mainloop(). - Update NavigationToolbar to use the new GtkToolbar API - SC - -2004-06-15 removed set_default_font from font_manager to unify font - customization using the new function rc. See API_CHANGES - for more info. The examples fonts_demo.py and - fonts_demo_kw.py are ported to the new API - JDH - -2004-06-15 Improved (yet again!) axis scaling to properly handle - singleton plots - JDH - -2004-06-15 Restored the old FigureCanvasGTK.draw() - SC - -2004-06-11 More memory leak fixes in transforms and ft2font - JDH - -2004-06-11 Eliminated numerix .numerix file and environment variable - NUMERIX. Fixed bug which prevented command line overrides: - --numarray or --numeric. - JTM - -2004-06-10 Added rc configuration function rc; deferred all rc param - setting until object creation time; added new rc attrs: - lines.markerfacecolor, lines.markeredgecolor, - lines.markeredgewidth, patch.linewidth, patch.facecolor, - patch.edgecolor, patch.antialiased; see - examples/customize_rc.py for usage - JDH - - ---------------------------------------------------------------- -2004-06-09 0.54.2 released - -2004-06-08 Rewrote ft2font using CXX as part of general memory leak - fixes; also fixed transform memory leaks - JDH - -2004-06-07 Fixed several problems with log ticks and scaling - JDH - -2004-06-07 Fixed width/height issues for images - JDH - -2004-06-03 Fixed draw_if_interactive bug for semilogx; - -2004-06-02 Fixed text clipping to clip to axes - JDH - -2004-06-02 Fixed leading newline text and multiple newline text - JDH - -2004-06-02 Fixed plot_date to return lines - JDH - -2004-06-01 Fixed plot to work with x or y having shape N,1 or 1,N - JDH - -2004-05-31 Added renderer markeredgewidth attribute of Line2D. - ADS - -2004-05-29 Fixed tick label clipping to work with navigation. - -2004-05-28 Added renderer grouping commands to support groups in - SVG/PS. - JDH - -2004-05-28 Fixed, this time I really mean it, the singleton plot - plot([0]) scaling bug; Fixed Flavio's shape = N,1 bug - JDH - -2004-05-28 added colorbar - JDH - -2004-05-28 Made some changes to the matplotlib.colors.Colormap to - propertly support clim - JDH - ------------------------------------------------------------------ -2004-05-27 0.54.1 released - -2004-05-27 Lots of small bug fixes: rotated text at negative angles, - errorbar capsize and autoscaling, right tick label - position, gtkagg on win98, alpha of figure background, - singleton plots - JDH - -2004-05-26 Added Gary's errorbar stuff and made some fixes for length - one plots and constant data plots - JDH - -2004-05-25 Tweaked TkAgg backend so that canvas.draw() works - more like the other backends. Fixed a bug resulting - in 2 draws per figure mangager show(). - JTM - ------------------------------------------------------------- -2004-05-19 0.54 released - -2004-05-18 Added newline seperated text with rotations to text.Text - layout - JDH - -2004-05-16 Added fast pcolor using PolyCollections. - JDH - -2004-05-14 Added fast polygon collections - changed scatter to use - them. Added multiple symbols to scatter. 10x speedup on - large scatters using *Agg and 5X speedup for ps. - JDH - -2004-05-14 On second thought... created an "nx" namespace in - in numerix which maps type names onto typecodes - the same way for both numarray and Numeric. This - undoes my previous change immediately below. To get a - typename for Int16 useable in a Numeric extension: - say nx.Int16. - JTM - -2004-05-15 Rewrote transformation class in extension code, simplified - all the artist constructors - JDH - -2004-05-14 Modified the type definitions in the numarray side of - numerix so that they are Numeric typecodes and can be - used with Numeric compilex extensions. The original - numarray types were renamed to type. - JTM - -2004-05-06 Gary Ruben sent me a bevy of new plot symbols and markers. - See matplotlib.matlab.plot - JDH - -2004-05-06 Total rewrite of mathtext - factored ft2font stuff out of - layout engine and defined abstract class for font handling - to lay groundwork for ps mathtext. Rewrote parser and made - layout engine much more precise. Fixed all the layout - hacks. Added spacing commands \/ and \hspace. Added - composite chars and defined angstrom. - JDH - -2004-05-05 Refactored text instances out of backend; aligned - text with arbitrary rotations is now supported - JDH - -2004-05-05 Added a Matrix capability for numarray to numerix. JTM - -2004-05-04 Updated whats_new.html.template to use dictionary and - template loop, added anchors for all versions and items; - updated goals.txt to use those for links. PG - -2004-05-04 Added fonts_demo.py to backend_driver, and AFM and TTF font - caches to font_manager.py - PEB - -2004-05-03 Redid goals.html.template to use a goals.txt file that - has a pseudo restructured text organization. PG - -2004-05-03 Removed the close buttons on all GUIs and added the python - #! bang line to the examples following Steve Chaplin's - advice on matplotlib dev - -2004-04-29 Added CXX and rewrote backend_agg using it; tracked down - and fixed agg memory leak - JDH - -2004-04-29 Added stem plot command - JDH - -2004-04-28 Fixed PS scaling and centering bug - JDH - -2004-04-26 Fixed errorbar autoscale problem - JDH - -2004-04-22 Fixed copy tick attribute bug, fixed singular datalim - ticker bug; fixed mathtext fontsize interactive bug. - JDH - -2004-04-21 Added calls to draw_if_interactive to axes(), legend(), - and pcolor(). Deleted duplicate pcolor(). - JTM - ------------------------------------------------------------- -2004-04-21 matplotlib 0.53 release - -2004-04-19 Fixed vertical alignment bug in PS backend - JDH - -2004-04-17 Added support for two scales on the "same axes" with tick - different ticking and labeling left right or top bottom. - See examples/two_scales.py - JDH - -2004-04-17 Added default dirs as list rather than single dir in - setupext.py - JDH - -2004-04-16 Fixed wx exception swallowing bug (and there was much - rejoicing!) - JDH - -2004-04-16 Added new ticker locator a formatter, fixed default font - return - JDH - -2004-04-16 Added get_name method to FontProperties class. Fixed font lookup - in GTK and WX backends. - PEB - -2004-04-16 Added get- and set_fontstyle msethods. - PEB - -2004-04-10 Mathtext fixes: scaling with dpi, - JDH - -2004-04-09 Improved font detection algorithm. - PEB - -2004-04-09 Move deprecation warnings from text.py to __init__.py - PEB - -2004-04-09 Added default font customization - JDH - -2004-04-08 Fixed viewlim set problem on axes and axis. - JDH - -2004-04-07 Added validate_comma_sep_str and font properties paramaters to - __init__. Removed font families and added rcParams to - FontProperties __init__ arguments in font_manager. Added - default font property parameters to .matplotlibrc file with - descriptions. Added deprecation warnings to the get_- and - set_fontXXX methods of the Text object. - PEB - -2004-04-06 Added load and save commands for ASCII data - JDH - -2004-04-05 Improved font caching by not reading AFM fonts until needed. - Added better documentation. Changed the behaviour of the - get_family, set_family, and set_name methods of FontProperties. - - PEB - -2004-04-05 Added WXAgg backend - JDH - -2004-04-04 Improved font caching in backend_agg with changes to - font_manager - JDH - -2004-03-29 Fixed fontdicts and kwargs to work with new font manager - - JDH - - - - - - - - --------------------------------------------- -This is the Old, stale, never used changelog - -2002-12-10 - Added a TODO file and CHANGELOG. Lots to do -- get - crackin'! - - - Fixed y zoom tool bug - - - Adopted a compromise fix for the y data clipping problem. - The problem was that for solid lines, the y data clipping - (as opposed to the gc clipping) caused artifactual - horizontal solid lines near the ylim boundaries. I did a - 5% offset hack in Axes set_ylim functions which helped, - but didn't cure the problem for very high gain y zooms. - So I disabled y data clipping for connected lines . If - you need extensive y clipping, either plot(y,x) because x - data clipping is always enabled, or change the _set_clip - code to 'if 1' as indicated in the lines.py src. See - _set_clip in lines.py and set_ylim in figure.py for more - information. - - -2002-12-11 - Added a measurement dialog to the figure window to - measure axes position and the delta x delta y with a left - mouse drag. These defaults can be overridden by deriving - from Figure and overrriding button_press_event, - button_release_event, and motion_notify_event, - and _dialog_measure_tool. - - - fixed the navigation dialog so you can check the axes the - navigation buttons apply to. - - - -2003-04-23 Released matplotlib v0.1 - -2003-04-24 Added a new line style PixelLine2D which is the plots the - markers as pixels (as small as possible) with format - symbol ',' - - Added a new class Patch with derived classes Rectangle, - RegularPolygon and Circle - -2003-04-25 Implemented new functions errorbar, scatter and hist - - Added a new line type '|' which is a vline. syntax is - plot(x, Y, '|') where y.shape = len(x),2 and each row gives - the ymin,ymax for the respective values of x. Previously I - had implemented vlines as a list of lines, but I needed the - efficientcy of the numeric clipping for large numbers of - vlines outside the viewport, so I wrote a dedicated class - Vline2D which derives from Line2D - - -2003-05-01 - - Fixed ytick bug where grid and tick show outside axis viewport with gc clip - -2003-05-14 - - Added new ways to specify colors 1) matlab format string 2) - html-style hex string, 3) rgb tuple. See examples/color_demo.py - -2003-05-28 - - Changed figure rendering to draw form a pixmap to reduce flicker. - See examples/system_monitor.py for an example where the plot is - continusouly updated w/o flicker. This example is meant to - simulate a system monitor that shows free CPU, RAM, etc... - -2003-08-04 - - Added Jon Anderson's GTK shell, which doesn't require pygtk to - have threading built-in and looks nice! - -2003-08-25 - - Fixed deprecation warnings for python2.3 and pygtk-1.99.18 - -2003-08-26 - - Added figure text with new example examples/figtext.py - - -2003-08-27 - - Fixed bugs i figure text with font override dictionairies and fig - text that was placed outside the window bounding box - -2003-09-1 thru 2003-09-15 - - Added a postscript and a GD module backend - -2003-09-16 - - Fixed font scaling and point scaling so circles, squares, etc on - lines will scale with DPI as will fonts. Font scaling is not fully - implemented on the gtk backend because I have not figured out how - to scale fonts to arbitrary sizes with GTK - -2003-09-17 - - Fixed figure text bug which crashed X windows on long figure text - extending beyond display area. This was, I believe, due to the - vestigial erase functionality that was no longer needed since I - began rendering to a pixmap - -2003-09-30 Added legend - -2003-10-01 Fixed bug when colors are specified with rgb tuple or hex - string. - - -2003-10-21 Andrew Straw provided some legend code which I modified - and incorporated. Thanks Andrew! - -2003-10-27 Fixed a bug in axis.get_view_distance that affected zoom in - versus out with interactive scrolling, and a bug in the axis text - reset system that prevented the text from being redrawn on a - interactive gtk view lim set with the widget - - Fixed a bug in that prevented the manual setting of ticklabel - strings from working properly - -2003-11-02 - Do a nearest neighbor color pick on GD when - allocate fails - -2003-11-02 - - Added pcolor plot - - Added MRI example - - Fixed bug that screwed up label position if xticks or yticks were - empty - - added nearest neighbor color picker when GD max colors exceeded - - fixed figure background color bug in GD backend - -2003-11-10 - 2003-11-11 - - major refactoring. - * Ticks (with labels, lines and grid) handled by dedicated class - * Artist now know bounding box and dpi - * Bounding boxes and transforms handled by dedicated classes - * legend in dedicated class. Does a better job of alignment and - bordering. Can be initialized with specific line instances. - See examples/legend_demo2.py - - -2003-11-14 Fixed legend positioning bug and added new position args - -2003-11-16 Finsihed porting GD to new axes API - - -2003-11-20 - add TM for matlab on website and in docs - - -2003-11-20 - make a nice errorbar and scatter screenshot - -2003-11-20 - auto line style cycling for multiple line types - broken - -2003-11-18 (using inkrect) :logical rect too big on gtk backend - -2003-11-18 ticks don't reach edge of axes in gtk mode -- - rounding error? - -2003-11-20 - port Gary's errorbar code to new API before 0.40 - -2003-11-20 - problem with stale _set_font. legend axes box - doesn't resize on save in GTK backend -- see htdocs legend_demo.py - -2003-11-21 - make a dash-dot dict for the GC - -2003-12-15 - fix install path bug diff --git a/CITATION.bib b/CITATION.bib new file mode 100644 index 000000000000..f9c78873bce3 --- /dev/null +++ b/CITATION.bib @@ -0,0 +1,14 @@ +@Article{Hunter:2007, + Author = {Hunter, J. D.}, + Title = {Matplotlib: A 2D graphics environment}, + Journal = {Computing in Science \& Engineering}, + Volume = {9}, + Number = {3}, + Pages = {90--95}, + abstract = {Matplotlib is a 2D graphics package used for Python for + application development, interactive scripting, and publication-quality + image generation across user interfaces and operating systems.}, + publisher = {IEEE COMPUTER SOC}, + doi = {10.1109/MCSE.2007.55}, + year = 2007 +} diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000000..ad7af5f76681 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,27 @@ +cff-version: 1.2.0 +message: 'If Matplotlib contributes to a project that leads to a scientific publication, please acknowledge this fact by citing J. D. Hunter, "Matplotlib: A 2D Graphics Environment", Computing in Science & Engineering, vol. 9, no. 3, pp. 90-95, 2007.' +title: 'Matplotlib: Visualization with Python' +authors: + - name: The Matplotlib Development Team + website: https://matplotlib.org/ +type: software +url: 'https://matplotlib.org/' +repository-code: 'https://github.com/matplotlib/matplotlib/' +preferred-citation: + type: article + authors: + - family-names: Hunter + given-names: John D. + title: "Matplotlib: A 2D graphics environment" + year: 2007 + date-published: 2007-06-18 + journal: Computing in Science & Engineering + volume: 9 + issue: 3 + start: 90 + end: 95 + doi: 10.1109/MCSE.2007.55 + publisher: + name: IEEE Computer Society + website: 'https://www.computer.org/' + abstract: Matplotlib is a 2D graphics package used for Python for application development, interactive scripting, and publication-quality image generation across user interfaces and operating systems. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..8fbbe8e7d6f3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,6 @@ + + +Our Code of Conduct is at +https://matplotlib.org/stable/project/code_of_conduct.html + +It is rendered from `doc/project/code_of_conduct.rst` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f5c602c0d7b8..000000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,2 +0,0 @@ -Please refer to the [Coding -Guidelines](http://matplotlib.org/devel/coding_guide.html). diff --git a/INSTALL b/INSTALL deleted file mode 100644 index aade12aab122..000000000000 --- a/INSTALL +++ /dev/null @@ -1,333 +0,0 @@ -.. The source of this document is INSTALL. During the doc build process, -.. this file is copied over to doc/users/installing.rst. -.. Therefore, you must edit INSTALL, *not* doc/users/installing.rst! -.. _pip: https://pypi.python.org/pypi/pip/ - -********** -Installing -********** - -There are many different ways to install matplotlib, and the best way -depends on what operating system you are using, what you already have -installed, and how you want to use it. To avoid wading through all -the details (and potential complications) on this page, there are several -convenient options. - -Installing pre-built packages -============================= - -Most platforms : scientific Python distributions ------------------------------------------------- - -The first option is to use one of the pre-packaged python -distributions that already provide matplotlib built-in. The -Continuum.io Python distribution (`Anaconda -`_ or `miniconda -`_) and the Enthought -distribution `(Canopy) `_ -are both excellent choices that "just work" out of the box for -Windows, OSX and common Linux platforms. Both of these distributions -include matplotlib and *lots* of other useful tools. - - -Linux : using your package manager ----------------------------------- - -If you are on Linux, you might prefer to use your package manager. matplotlib -is packaged for almost every major Linux distribution. - -* Debian / Ubuntu : ``sudo apt-get install python-matplotlib`` -* Fedora / Redhat : ``sudo yum install python-matplotlib`` - -Mac OSX : using pip -------------------- - -If you are on Mac OSX you can probably install matplotlib binaries using the -standard Python installation program pip_. -See :ref:`install_osx_binaries`. - -.. _installing_windows: - -Windows -------- - -If you don't already have Python installed, we recommend using -one of the `scipy-stack compatible Python distributions -`_ such as WinPython, Python(x,y), -Enthought Canopy, or Continuum Anaconda, which have matplotlib and many -of its dependencies, plus other useful packages, preinstalled. - -For `standard Python `_ installations, -install matplotlib using pip_:: - - python -m pip install -U pip setuptools - python -m pip install matplotlib - -In case Python 2.7 or 3.4 are not installed for all users, -the Microsoft Visual C++ 2008 ( -`64 bit `__ -or -`32 bit `__ -for Python 2.7) or Microsoft Visual C++ 2010 ( -`64 bit `__ -or -`32 bit `__ -for Python 3.4) redistributable packages need to be installed. - -Matplotlib depends on `Pillow `_ -for reading and saving JPEG, BMP, and TIFF image files. -Matplotlib requires `MiKTeX `_ and -`GhostScript `_ for rendering text -with LaTeX. -`FFmpeg `_, `avconv `_, -`mencoder `_, or -`ImageMagick `_ are required for the -animation module. - -The following backends should work out of the box: agg, tkagg, ps, -pdf and svg. -For other backends you may need to install -`pycairo `_, -`PyQt4 `_, -`PyQt5 `_, -`PySide `_, -`wxPython `_, -`PyGTK `_, -`Tornado `_, -or GhostScript. - -TkAgg is probably the best backend for interactive use from the -standard Python shell or IPython. It is enabled as the default backend -for the official binaries. GTK3 is not supported on Windows. - -The Windows wheels (:file:`*.whl`) on the `PyPI download page -`_ do not contain test data -or example code. -If you want to try the many demos that come in the matplotlib source -distribution, download the :file:`*.tar.gz` file and look in the -:file:`examples` subdirectory. -To run the test suite, copy the :file:`lib\\matplotlib\\tests` and -:file:`lib\\mpl_toolkits\\tests` directories from the source -distribution to :file:`sys.prefix\\Lib\\site-packages\\matplotlib` and -:file:`sys.prefix\\Lib\\site-packages\\mpl_toolkits` respectively, and -install `nose `_, `mock -`_, Pillow, MiKTeX, GhostScript, -ffmpeg, avconv, mencoder, ImageMagick, and `Inkscape -`_. - - - -.. _install_from_source: - -Installing from source -====================== - -If you are interested in contributing to matplotlib development, -running the latest source code, or just like to build everything -yourself, it is not difficult to build matplotlib from source. Grab -the latest *tar.gz* release file from `the PyPI files page -`_, or if you want to -develop matplotlib or just need the latest bugfixed version, grab the -latest git version :ref:`install-from-git`. - -The standard environment variables `CC`, `CXX`, `PKG_CONFIG` are respected. -This means you can set them if your toolchain is prefixed. This may be used for -cross compiling. - - export CC=x86_64-pc-linux-gnu-gcc - export CXX=x86_64-pc-linux-gnu-g++ - export PKG_CONFIG=x86_64-pc-linux-gnu-pkg-config - -Once you have satisfied the requirements detailed below (mainly -python, numpy, libpng and freetype), you can build matplotlib:: - - cd matplotlib - python setup.py build - python setup.py install - -We provide a `setup.cfg -`_ -file that goes with :file:`setup.py` which you can use to customize -the build process. For example, which default backend to use, whether -some of the optional libraries that matplotlib ships with are -installed, and so on. This file will be particularly useful to those -packaging matplotlib. - -If you have installed prerequisites to nonstandard places and need to -inform matplotlib where they are, edit ``setupext.py`` and add the base -dirs to the ``basedir`` dictionary entry for your ``sys.platform``. -e.g., if the header to some required library is in -``/some/path/include/someheader.h``, put ``/some/path`` in the -``basedir`` list for your platform. - -.. _install_requirements: - -Build requirements ------------------- - -These are external packages which you will need to install before -installing matplotlib. If you are building on OSX, see -:ref:`build_osx`. If you are building on Windows, see -:ref:`build_windows`. If you are installing dependencies with a -package manager on Linux, you may need to install the development -packages (look for a "-dev" postfix) in addition to the libraries -themselves. - - -Required Dependencies -^^^^^^^^^^^^^^^^^^^^^ - -:term:`python` 2.7, 3.4, or 3.5 - `Download python `_. - -:term:`numpy` |minimum_numpy_version| (or later) - array support for python (`download numpy `_) - -`setuptools `__ - Setuptools provides extensions for python package installation. - -:term:`dateutil` 1.1 or later - Provides extensions to python datetime handling. If using pip, - easy_install or installing from source, the installer will attempt - to download and install `python_dateutil` from PyPI. - -`pyparsing `__ - Required for matplotlib's mathtext math rendering support. If - using pip, easy_install or installing from source, the installer - will attempt to download and install `pyparsing` from PyPI. - -`libpng 1.2 (or later) `__ - library for loading and saving :term:`PNG` files (`download - `__). libpng requires - zlib. - -`pytz `__ - Used to manipulate time-zone aware datetimes. http://pypi.python.org/pypi/pytz - -:term:`FreeType` 2.3 or later - Library for reading true type font files. If using pip, easy_install or - installing from source, the installer will attempt to locate FreeType in - expected locations. If it cannot, try installing `pkg-config - `__, - a tool used to find required non-python libraries. - -`cycler `__ 0.10.0 or later - Composable cycle class used for constructing style-cycles - -`functools32` - Required for compatibility if running on versions of Python before - Python 3.2. - -Optional GUI framework -^^^^^^^^^^^^^^^^^^^^^^ - -These are optional packages which you may want to install to use -matplotlib with a user interface toolkit. See -:ref:`what-is-a-backend` for more details on the optional matplotlib -backends and the capabilities they provide. - -:term:`tk` 8.3 or later, not 8.6.0 or 8.6.1 - The TCL/Tk widgets library used by the TkAgg backend. - - Versions 8.6.0 and 8.6.1 are known to have issues that may result - in segfaults when closing multiple windows in the wrong order. - -:term:`pyqt` 4.4 or later - The Qt4 widgets library python wrappers for the Qt4Agg backend - -:term:`pygtk` 2.4 or later - The python wrappers for the GTK widgets library for use with the - GTK or GTKAgg backend - -:term:`wxpython` 2.8 or later - The python wrappers for the wx widgets library for use with the - WX or WXAgg backend - -Optional external programs -^^^^^^^^^^^^^^^^^^^^^^^^^^ -`ffmpeg `__/`avconv `__ or `mencoder `__ - Required for the animation module to be save out put to movie - formats. - -`ImageMagick `__ - Required for the animation module to be able to save to animated gif. - -Optional dependencies -^^^^^^^^^^^^^^^^^^^^^ - -`Pillow `__ - If Pillow is installed, matplotlib can read and write a larger - selection of image file formats. - -`pkg-config `__ - A tool used to find required non-python libraries. This is not strictly - required, but can make installation go more smoothly if the libraries and - headers are not in the expected locations. - - -Required libraries that ship with matplotlib -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:term:`agg` 2.4 - The antigrain C++ rendering engine. matplotlib links against the - agg template source statically, so it will not affect anything on - your system outside of matplotlib. - -`qhull` 2012.1 - A library for computing Delaunay triangulations. - -`ttconv` - truetype font utility - -six 1.9.0 - Python 2/3 compatibility library. Do not use this in third-party - code. - - -.. _build_linux: - -Building on Linux ------------------ - -It is easiest to use your system package manager to install the dependencies. - -If you are on Debian/Ubuntu, you can get all the dependencies -required to build matplotlib with:: - - sudo apt-get build-dep python-matplotlib - -If you are on Fedora/RedHat, you can get all the dependencies required -to build matplotlib by first installing ``yum-builddep`` and then -running:: - - su -c "yum-builddep python-matplotlib" - -This does not build matplotlib, but it does get and install the -build dependencies, which will make building from source easier. - - -.. _build_osx: - -Building on OSX ---------------- - -The build situation on OSX is complicated by the various places one -can get the libpng and freetype requirements (darwinports, fink, -/usr/X11R6) and the different architectures (e.g., x86, ppc, universal) and -the different OSX version (e.g., 10.4 and 10.5). We recommend that you build -the way we do for the OSX release: get the source from the tarball or the -git repository and follow the instruction in :file:`README.osx`. - - -.. _build_windows: - -Building on Windows -------------------- - -The Python shipped from http://www.python.org is compiled with Visual Studio -2008 for versions before 3.3, Visual Studio 2010 for 3.3 and 3.4, and -Visual Studio 2015 for 3.5. Python extensions are recommended to be compiled -with the same compiler. - -Since there is no canonical Windows package manager, the methods for building -freetype, zlib, and libpng from source code are documented as a build script -at `matplotlib-winbuild `_. diff --git a/INSTALL.rst b/INSTALL.rst new file mode 100644 index 000000000000..3fb01c58d259 --- /dev/null +++ b/INSTALL.rst @@ -0,0 +1 @@ +See doc/install/index.rst diff --git a/LICENSE/LICENSE.PIL b/LICENSE/LICENSE.PIL deleted file mode 100644 index 3f77350b923b..000000000000 --- a/LICENSE/LICENSE.PIL +++ /dev/null @@ -1,12 +0,0 @@ -Software License - -The Python Imaging Library (PIL) is - - Copyright © 1997-2011 by Secret Labs AB - Copyright © 1995-2011 by Fredrik Lundh - -By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: - -Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. - -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/LICENSE/LICENSE_BAKOMA b/LICENSE/LICENSE_BAKOMA index 801e20cd736f..6200f085b9d6 100644 --- a/LICENSE/LICENSE_BAKOMA +++ b/LICENSE/LICENSE_BAKOMA @@ -2,7 +2,7 @@ BaKoMa Fonts Licence -------------------- - This licence covers two font packs (known as BaKoMa Fonts Colelction, + This licence covers two font packs (known as BaKoMa Fonts Collection, which is available at `CTAN:fonts/cm/ps-type1/bakoma/'): 1) BaKoMa-CM (1.1/12-Nov-94) diff --git a/LICENSE/LICENSE_CARLOGO b/LICENSE/LICENSE_CARLOGO new file mode 100644 index 000000000000..8c99c656a0f5 --- /dev/null +++ b/LICENSE/LICENSE_CARLOGO @@ -0,0 +1,45 @@ +----> we renamed carlito -> carlogo to comply with the terms <---- + +Copyright (c) 2010-2013 by tyPoland Lukasz Dziedzic with Reserved Font Name "Carlito". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the copyright statement(s). + +"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. + +5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/LICENSE/LICENSE_COLORBREWER b/LICENSE/LICENSE_COLORBREWER index 568afe883ece..7557bb7e769b 100644 --- a/LICENSE/LICENSE_COLORBREWER +++ b/LICENSE/LICENSE_COLORBREWER @@ -1,38 +1,13 @@ -Apache-Style Software License for ColorBrewer Color Schemes +Apache-Style Software License for ColorBrewer software and ColorBrewer Color Schemes -Version 1.1 +Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University. -Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania -State University. All rights reserved. Redistribution and use in source -and binary forms, with or without modification, are permitted provided -that the following conditions are met: +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at -1. Redistributions as source code must retain the above copyright notice, -this list of conditions and the following disclaimer. +http://www.apache.org/licenses/LICENSE-2.0 -2. The end-user documentation included with the redistribution, if any, -must include the following acknowledgment: "This product includes color -specifications and designs developed by Cynthia Brewer -(http://colorbrewer.org/)." Alternately, this acknowledgment may appear in -the software itself, if and wherever such third-party acknowledgments -normally appear. - -3. The name "ColorBrewer" must not be used to endorse or promote products -derived from this software without prior written permission. For written -permission, please contact Cynthia Brewer at cbrewer@psu.edu. - -4. Products derived from this software may not be called "ColorBrewer", -nor may "ColorBrewer" appear in their name, without prior written -permission of Cynthia Brewer. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -CYNTHIA BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/LICENSE/LICENSE_CONDA b/LICENSE/LICENSE_CONDA deleted file mode 100644 index 8794a6d484af..000000000000 --- a/LICENSE/LICENSE_CONDA +++ /dev/null @@ -1,51 +0,0 @@ -Except where noted below, conda is released under the following terms: - -(c) 2012 Continuum Analytics, Inc. / http://continuum.io -All Rights Reserved - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Continuum Analytics, Inc. nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL CONTINUUM ANALYTICS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -Exceptions -========== - -versioneer.py is Public Domain - -The ProgressBar package is released under the following terms: - -# progressbar - Text progress bar library for Python. -# Copyright (c) 2005 Nilton Volpato -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/LICENSE/LICENSE_COURIERTEN b/LICENSE/LICENSE_COURIERTEN new file mode 100644 index 000000000000..c6d3fd7410a2 --- /dev/null +++ b/LICENSE/LICENSE_COURIERTEN @@ -0,0 +1,18 @@ +The Courier10PitchBT-Bold.pfb file is a Type-1 version of +Courier 10 Pitch BT Bold by Bitstream, obtained from +. It is included +here as test data only, but the following license applies. + + +(c) Copyright 1989-1992, Bitstream Inc., Cambridge, MA. + +You are hereby granted permission under all Bitstream propriety rights +to use, copy, modify, sublicense, sell, and redistribute the 4 Bitstream +Charter (r) Type 1 outline fonts and the 4 Courier Type 1 outline fonts +for any purpose and without restriction; provided, that this notice is +left intact on all copies of such fonts and that Bitstream's trademark +is acknowledged as shown below on all unmodified copies of the 4 Charter +Type 1 fonts. + +BITSTREAM CHARTER is a registered trademark of Bitstream Inc. + diff --git a/LICENSE/LICENSE_JQUERY b/LICENSE/LICENSE_JQUERY deleted file mode 100644 index f35387a3ab48..000000000000 --- a/LICENSE/LICENSE_JQUERY +++ /dev/null @@ -1,61 +0,0 @@ -Comment found in jQuery source code: - -/*! - * jQuery JavaScript Library v1.11.3 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2015-04-28T16:19Z - */ - -Comment found in jQuery UI source code: - -/*! jQuery UI - v1.11.4 - 2015-03-11 -* http://jqueryui.com -* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js -* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ - -Text found at http://jquery.org/license: - - jQuery Foundation projects are released under the terms of the license - specified in the project's repository or if not specified, under the - MIT license. - - The MIT License is simple and easy to understand and it places almost - no restrictions on what you can do with a jQuery Foundation project. - - You are free to use any jQuery Foundation project in any other project - (even commercial projects) as long as the copyright header is left - intact. - -The text links to https://tldrlegal.com/license/mit-license -which includes the following as the "Full License Text": - - The MIT License (MIT) - - Copyright (c) - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER b/LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER new file mode 100644 index 000000000000..0bc1fa7060b7 --- /dev/null +++ b/LICENSE/LICENSE_JSXTOOLS_RESIZE_OBSERVER @@ -0,0 +1,108 @@ +# CC0 1.0 Universal + +## Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an “owner”) of an original work of +authorship and/or a database (each, a “Work”). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific works +(“Commons”) that the public can reliably and without fear of later claims of +infringement build upon, modify, incorporate in other works, reuse and +redistribute as freely as possible in any form whatsoever and for any purposes, +including without limitation commercial purposes. These owners may contribute +to the Commons to promote the ideal of a free culture and the further +production of creative, cultural and scientific works, or to gain reputation or +greater distribution for their Work in part through the use and efforts of +others. + +For these and/or other purposes and motivations, and without any expectation of +additional consideration or compensation, the person associating CC0 with a +Work (the “Affirmer”), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and +publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be + protected by copyright and related or neighboring rights (“Copyright and + Related Rights”). Copyright and Related Rights include, but are not limited + to, the following: + 1. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + 2. moral rights retained by the original author(s) and/or performer(s); + 3. publicity and privacy rights pertaining to a person’s image or likeness + depicted in a Work; + 4. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(i), below; + 5. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + 6. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + 7. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations + thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, + applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and + unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright + and Related Rights and associated claims and causes of action, whether now + known or unknown (including existing as well as future claims and causes of + action), in the Work (i) in all territories worldwide, (ii) for the maximum + duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the “Waiver”). Affirmer + makes the Waiver for the benefit of each member of the public at large and + to the detriment of Affirmer’s heirs and successors, fully intending that + such Waiver shall not be subject to revocation, rescission, cancellation, + termination, or any other legal or equitable action to disrupt the quiet + enjoyment of the Work by the public as contemplated by Affirmer’s express + Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be + judged legally invalid or ineffective under applicable law, then the Waiver + shall be preserved to the maximum extent permitted taking into account + Affirmer’s express Statement of Purpose. In addition, to the extent the + Waiver is so judged Affirmer hereby grants to each affected person a + royalty-free, non transferable, non sublicensable, non exclusive, + irrevocable and unconditional license to exercise Affirmer’s Copyright and + Related Rights in the Work (i) in all territories worldwide, (ii) for the + maximum duration provided by applicable law or treaty (including future time + extensions), (iii) in any current or future medium and for any number of + copies, and (iv) for any purpose whatsoever, including without limitation + commercial, advertising or promotional purposes (the “License”). The License + shall be deemed effective as of the date CC0 was applied by Affirmer to the + Work. Should any part of the License for any reason be judged legally + invalid or ineffective under applicable law, such partial invalidity or + ineffectiveness shall not invalidate the remainder of the License, and in + such case Affirmer hereby affirms that he or she will not (i) exercise any + of his or her remaining Copyright and Related Rights in the Work or (ii) + assert any associated claims and causes of action with respect to the Work, + in either case contrary to Affirmer’s express Statement of Purpose. + +4. Limitations and Disclaimers. + 1. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + 2. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or + otherwise, including without limitation warranties of title, + merchantability, fitness for a particular purpose, non infringement, or + the absence of latent or other defects, accuracy, or the present or + absence of errors, whether or not discoverable, all to the greatest + extent permissible under applicable law. + 3. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person’s Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the Work. + 4. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see +http://creativecommons.org/publicdomain/zero/1.0/. diff --git a/LICENSE/LICENSE_LAST_RESORT_FONT b/LICENSE/LICENSE_LAST_RESORT_FONT new file mode 100644 index 000000000000..5fe3297bc1e1 --- /dev/null +++ b/LICENSE/LICENSE_LAST_RESORT_FONT @@ -0,0 +1,97 @@ +Last Resort High-Efficiency Font License +======================================== + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +SPDX-License-Identifier: OFL-1.1 diff --git a/LICENSE/LICENSE_SOLARIZED b/LICENSE/LICENSE_SOLARIZED new file mode 100644 index 000000000000..6e5a0475dd24 --- /dev/null +++ b/LICENSE/LICENSE_SOLARIZED @@ -0,0 +1,20 @@ +https://github.com/altercation/solarized/blob/master/LICENSE +Copyright (c) 2011 Ethan Schoonover + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/LICENSE/LICENSE_STIX b/LICENSE/LICENSE_STIX index 2f7aeea331ce..6034d9474814 100644 --- a/LICENSE/LICENSE_STIX +++ b/LICENSE/LICENSE_STIX @@ -1,71 +1,124 @@ -TERMS AND CONDITIONS - - 1. Permission is hereby granted, free of charge, to any person -obtaining a copy of the STIX Fonts-TM set accompanying this license -(collectively, the "Fonts") and the associated documentation files -(collectively with the Fonts, the "Font Software"), to reproduce and -distribute the Font Software, including the rights to use, copy, merge -and publish copies of the Font Software, and to permit persons to whom -the Font Software is furnished to do so same, subject to the following -terms and conditions (the "License"). - - 2. The following copyright and trademark notice and these Terms and -Conditions shall be included in all copies of one or more of the Font -typefaces and any derivative work created as permitted under this -License: - - Copyright (c) 2001-2005 by the STI Pub Companies, consisting of -the American Institute of Physics, the American Chemical Society, the -American Mathematical Society, the American Physical Society, Elsevier, -Inc., and The Institute of Electrical and Electronic Engineers, Inc. -Portions copyright (c) 1998-2003 by MicroPress, Inc. Portions copyright -(c) 1990 by Elsevier, Inc. All rights reserved. STIX Fonts-TM is a -trademark of The Institute of Electrical and Electronics Engineers, Inc. - - 3. You may (a) convert the Fonts from one format to another (e.g., -from TrueType to PostScript), in which case the normal and reasonable -distortion that occurs during such conversion shall be permitted and (b) -embed or include a subset of the Fonts in a document for the purposes of -allowing users to read text in the document that utilizes the Fonts. In -each case, you may use the STIX Fonts-TM mark to designate the resulting -Fonts or subset of the Fonts. - - 4. You may also (a) add glyphs or characters to the Fonts, or modify -the shape of existing glyphs, so long as the base set of glyphs is not -removed and (b) delete glyphs or characters from the Fonts, provided -that the resulting font set is distributed with the following -disclaimer: "This [name] font does not include all the Unicode points -covered in the STIX Fonts-TM set but may include others." In each case, -the name used to denote the resulting font set shall not include the -term "STIX" or any similar term. - - 5. You may charge a fee in connection with the distribution of the -Font Software, provided that no copy of one or more of the individual -Font typefaces that form the STIX Fonts-TM set may be sold by itself. - - 6. THE FONT SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK OR OTHER RIGHT. IN NO EVENT SHALL -MICROPRESS OR ANY OF THE STI PUB COMPANIES BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, INCLUDING, BUT NOT LIMITED TO, ANY GENERAL, -SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM OR OUT OF THE USE OR -INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT -SOFTWARE. - - 7. Except as contained in the notice set forth in Section 2, the -names MicroPress Inc. and STI Pub Companies, as well as the names of the -companies/organizations that compose the STI Pub Companies, shall not be -used in advertising or otherwise to promote the sale, use or other -dealings in the Font Software without the prior written consent of the -respective company or organization. - - 8. This License shall become null and void in the event of any -material breach of the Terms and Conditions herein by licensee. - - 9. A substantial portion of the STIX Fonts set was developed by -MicroPress Inc. for the STI Pub Companies. To obtain additional -mathematical fonts, please contact MicroPress, Inc., 68-30 Harrow -Street, Forest Hills, NY 11375, USA - Phone: (718) 575-1816. +The STIX fonts distributed with matplotlib have been modified from +their canonical form. They have been converted from OTF to TTF format +using Fontforge and this script: + #!/usr/bin/env fontforge + i=1 + while ( i<$argc ) + Open($argv[i]) + Generate($argv[i]:r + ".ttf") + i = i+1 + endloop + +The original STIX Font License begins below. + +----------------------------------------------------------- + +STIX Font License + +24 May 2010 + +Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the American +Institute of Physics, the American Chemical Society, the American Mathematical +Society, the American Physical Society, Elsevier, Inc., and The Institute of +Electrical and Electronic Engineers, Inc. (www.stixfonts.org), with Reserved +Font Name STIX Fonts, STIX Fonts (TM) is a trademark of The Institute of +Electrical and Electronics Engineers, Inc. + +Portions copyright (c) 1998-2003 by MicroPress, Inc. (www.micropress-inc.com), +with Reserved Font Name TM Math. To obtain additional mathematical fonts, please +contact MicroPress, Inc., 68-30 Harrow Street, Forest Hills, NY 11375, USA, +Phone: (718) 575-1816. + +Portions copyright (c) 1990 by Elsevier, Inc. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/LICENSE/LICENSE_enthought.txt b/LICENSE/LICENSE_enthought.txt deleted file mode 100644 index 27727c5eae9a..000000000000 --- a/LICENSE/LICENSE_enthought.txt +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2001, 2002 Enthought, Inc. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - a. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - b. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - c. Neither the name of the Enthought nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - diff --git a/LICENSE/pnpoly.license b/LICENSE/pnpoly.license deleted file mode 100644 index 0c838f9b011e..000000000000 --- a/LICENSE/pnpoly.license +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 1970-2003, Wm. Randolph Franklin - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimers. - 2. Redistributions in binary form must reproduce the above - copyright notice in the documentation and/or other materials - provided with the distribution. - 3. The name of W. Randolph Franklin may not be used to endorse or - promote products derived from this Software without specific - prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 85caada2a1bd..000000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,22 +0,0 @@ -include CHANGELOG INSTALL -include CONTRIBUTING.md -include Makefile MANIFEST.in -include matplotlibrc.template setup.cfg.template -include setupext.py setup.py distribute_setup.py -include lib/matplotlib/mpl-data/lineprops.glade -include lib/matplotlib/mpl-data/matplotlibrc -include lib/matplotlib/mpl-data/images/* -include lib/matplotlib/mpl-data/fonts/ttf/* -include lib/matplotlib/mpl-data/fonts/pdfcorefonts/* -include lib/matplotlib/mpl-data/fonts/afm/* -include lib/matplotlib/mpl-data/stylelib/* -recursive-include lib/matplotlib/mpl-data/sample_data * -recursive-include LICENSE * -recursive-include examples * -recursive-include doc * -recursive-include src *.cpp *.c *.h *.m -recursive-include lib * -recursive-include extern * -include versioneer.py -include lib/matplotlib/_version.py -include tests.py diff --git a/Makefile b/Makefile deleted file mode 100644 index 397e45c61513..000000000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# Makefile for matplotlib - -PYTHON = `which python` -VERSION = `${PYTHON} setup.py --version` - -DISTFILES = API_CHANGES KNOWN_BUGS INSTALL README license \ - CHANGELOG Makefile INTERACTIVE \ - MANIFEST.in lib lib/matplotlib lib/dateutil lib/pytz examples setup.py - -RELEASE = matplotlib-${VERSION} - - -clean: - ${PYTHON} setup.py clean;\ - rm -f *.png *.ps *.eps *.svg *.jpg *.pdf - find . -name "_tmp*.py" | xargs rm -f;\ - find . \( -name "*~" -o -name "*.pyc" \) | xargs rm -f;\ - find unit \( -name "*.png" -o -name "*.ps" -o -name "*.pdf" -o -name "*.eps" \) | xargs rm -f - find . \( -name "#*" -o -name ".#*" -o -name ".*~" -o -name "*~" \) | xargs rm -f - - -release: ${DISTFILES} - rm -f MANIFEST;\ - ${PYTHON} license.py ${VERSION} license/LICENSE;\ - ${PYTHON} setup.py sdist --formats=gztar,zip; - -pyback: - tar cvfz pyback.tar.gz *.py lib src examples/*.py unit/*.py - - -_build_osx105: - CFLAGS="-Os -arch i386 -arch ppc" LDFLAGS="-Os -arch i386 -arch ppc" python setup.py build - -build_osx105: - echo "Use 'make -f fetch deps mpl_install instead'" - - -jdh_doc_snapshot: - git pull;\ - python setup.py install --prefix=~/dev;\ - cd doc;\ - rm -rf build;\ - python make.py clean;\ - python make.py html latex sf sfpdf; - - -test: - ${PYTHON} setup.py test - - -test-coverage: - ${PYTHON} setup.py test --with-coverage --cover-package=matplotlib - - diff --git a/README.md b/README.md new file mode 100644 index 000000000000..8f9edaad2b5b --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +[![PyPi](https://img.shields.io/pypi/v/matplotlib)](https://pypi.org/project/matplotlib/) +[![Conda](https://img.shields.io/conda/vn/conda-forge/matplotlib)](https://anaconda.org/conda-forge/matplotlib) +[![Downloads](https://img.shields.io/pypi/dm/matplotlib)](https://pypi.org/project/matplotlib) +[![NUMFocus](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org) +[![LFX Health Score](https://insights.linuxfoundation.org/api/badge/health-score?project=matplotlib)](https://insights.linuxfoundation.org/project/matplotlib) + +[![Discourse help forum](https://img.shields.io/badge/help_forum-discourse-blue.svg)](https://discourse.matplotlib.org) +[![Gitter](https://badges.gitter.im/matplotlib/matplotlib.svg)](https://gitter.im/matplotlib/matplotlib) +[![GitHub issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/matplotlib/matplotlib/issues) +[![Contributing](https://img.shields.io/badge/PR-Welcome-%23FF8300.svg?)](https://matplotlib.org/stable/devel/index.html) + +[![GitHub actions status](https://github.com/matplotlib/matplotlib/workflows/Tests/badge.svg)](https://github.com/matplotlib/matplotlib/actions?query=workflow%3ATests) +[![Azure pipelines status](https://dev.azure.com/matplotlib/matplotlib/_apis/build/status/matplotlib.matplotlib?branchName=main)](https://dev.azure.com/matplotlib/matplotlib/_build/latest?definitionId=1&branchName=main) +[![AppVeyor status](https://ci.appveyor.com/api/projects/status/github/matplotlib/matplotlib?branch=main&svg=true)](https://ci.appveyor.com/project/matplotlib/matplotlib) +[![Codecov status](https://codecov.io/github/matplotlib/matplotlib/badge.svg?branch=main&service=github)](https://app.codecov.io/gh/matplotlib/matplotlib) +[![EffVer Versioning](https://img.shields.io/badge/version_scheme-EffVer-0097a7)](https://jacobtomlinson.dev/effver) + +![Matplotlib logotype](https://matplotlib.org/stable/_static/logo2.svg) + +Matplotlib is a comprehensive library for creating static, animated, and +interactive visualizations in Python. + +Check out our [home page](https://matplotlib.org/) for more information. + +![image](https://matplotlib.org/stable/_static/readme_preview.png) + +Matplotlib produces publication-quality figures in a variety of hardcopy +formats and interactive environments across platforms. Matplotlib can be +used in Python scripts, Python/IPython shells, web application servers, +and various graphical user interface toolkits. + +## Install + +See the [install +documentation](https://matplotlib.org/stable/users/installing/index.html), +which is generated from `/doc/install/index.rst` + +## Contribute + +You've discovered a bug or something else you want to change — excellent! + +You've worked out a way to fix it — even better! + +You want to tell us about it — best of all! + +Start at the [contributing +guide](https://matplotlib.org/devdocs/devel/contribute.html)! + +## Contact + +[Discourse](https://discourse.matplotlib.org/) is the discussion forum +for general questions and discussions and our recommended starting +point. + +Our active mailing lists (which are mirrored on Discourse) are: + +- [Users](https://mail.python.org/mailman/listinfo/matplotlib-users) + mailing list: +- [Announcement](https://mail.python.org/mailman/listinfo/matplotlib-announce) + mailing list: +- [Development](https://mail.python.org/mailman/listinfo/matplotlib-devel) + mailing list: + +[Gitter](https://gitter.im/matplotlib/matplotlib) is for coordinating +development and asking questions directly related to contributing to +matplotlib. + +## Citing Matplotlib + +If Matplotlib contributes to a project that leads to publication, please +acknowledge this by citing Matplotlib. + +[A ready-made citation +entry](https://matplotlib.org/stable/users/project/citing.html) is +available. diff --git a/README.osx b/README.osx deleted file mode 100644 index 0b30c3d98101..000000000000 --- a/README.osx +++ /dev/null @@ -1,31 +0,0 @@ -Building mpl on OSX is sometimes a nightmare because of all the -different types of zlib, png and freetype that may be on your system. - -For developers who want to build matplotlib from source, the recommended and -supported way to build is to use a third-party package manager to install the -required dependencies, and then install matplotlib from source using the -setup.py script. Two widely used package managers are homebrew, and -MacPorts. The following example illustrates how to install libpng and freetype -using brew: - -Example usage:: - - brew install libpng freetype pkg-config - -If you are using MacPorts, execute the following instead: - -Example usage:: - - port install libpng freetype pkgconfig - -To install matplotlib from source, execute: - -Example usage:: - - python setup.py install - - -Note that your environment is somewhat important. Some conda users have -found that, to run the tests, their PYTHONPATH must include -/path/to/anaconda/.../site-packages and their DYLD_FALLBACK_LIBRARY_PATH -must include /path/to/anaconda/lib. diff --git a/README.rst b/README.rst deleted file mode 100644 index b3654feac540..000000000000 --- a/README.rst +++ /dev/null @@ -1,61 +0,0 @@ -########## -matplotlib -########## - -matplotlib is a Python 2D plotting library which produces publication-quality -figures in a variety of hardcopy formats and interactive -environments across platforms. matplotlib can be used in Python -scripts, the Python and IPython shell (ala MATLAB or Mathematica), web -application servers, and various graphical user interface toolkits. - -`Home page `_ - -Installation -============= - -For installation instructions and requirements, see the INSTALL file. - -Testing -======= - -After installation, you can launch the test suite:: - - python tests.py - -Or from the Python interpreter:: - - import matplotlib - matplotlib.test() - -Consider reading http://matplotlib.org/devel/coding_guide.html#testing for -more information. Note that the test suite requires nose and on Python 2.7 mock -which are not installed by default. Please install with pip or your package -manager of choice. - -Contact -======= -matplotlib's communication channels include active mailing lists: - -* `Users `_ mailing list: matplotlib-users@python.org -* `Announcement `_ mailing list: matplotlib-announce@python.org -* `Development `_ mailing list: matplotlib-devel@python.org - - -The first is a good starting point for general questions and discussions. - - -.. image:: https://badges.gitter.im/Join%20Chat.svg - :alt: Join the chat at https://gitter.im/matplotlib/matplotlib - :target: https://gitter.im/matplotlib/matplotlib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge - -Contribute -========== -You've discovered a bug or something else you want to change - excellent! - -You've worked out a way to fix it – even better! - -You want to tell us about it – best of all! - -The easiest way to contribute is through GitHub; if interested in doing so, browse the `development workflow `_ guide. - -If you don't want to submit your changes via Github, take a look at the guide to `making patches `_. diff --git a/README.win.md b/README.win.md deleted file mode 100644 index 293ebdbcc998..000000000000 --- a/README.win.md +++ /dev/null @@ -1,67 +0,0 @@ -# Building on Windows - -There are a few possibilities to build matplotlib on Windows: - -* Wheels via [matplotlib-winbuild](https://github.com/jbmohler/matplotlib-winbuild) -* Wheels by using conda packages -* Conda packages - -## Wheel builds using conda packages - -This is a wheel build, but we use conda packages to get all the requirements. The binary -requirements (png, freetype,...) are statically linked and therefore not needed during the wheel -install. - -The commands below assume that you can compile a native python lib for the python version of your -choice. See [this howto](http://blog.ionelmc.ro/2014/12/21/compiling-python-extensions-on-windows/) -how to install and setup such environments. If in doubt: use python 3.5 as it mostly works -without fiddling with environment variables. - -``` sh -# create a new environment with the required packages -conda create -n "matplotlib_build" python=3.4 numpy python-dateutil pyparsing pytz tornado "cycler>=0.10" tk libpng zlib freetype -activate matplotlib_build -# if you want a qt backend, you also have to install pyqt (be aware that pyqt doesn't mix well if -# you have created the environment with conda-forge already activated...) -conda install pyqt -# this package is only available in the conda-forge channel -conda install -c conda-forge msinttypes -# for python 2.7 -conda install -c conda-forge functools32 - -# copy the libs which have "wrong" names -set LIBRARY_LIB=%CONDA_DEFAULT_ENV%\Library\lib -mkdir lib || cmd /c "exit /b 0" -copy %LIBRARY_LIB%\zlibstatic.lib lib\z.lib -copy %LIBRARY_LIB%\libpng_static.lib lib\png.lib - -# Make the header files and the rest of the static libs available during the build -# CONDA_DEFAULT_ENV is a env variable which is set to the currently active environment path -set MPLBASEDIRLIST=%CONDA_DEFAULT_ENV%\Library\;. - -# build the wheel -python setup.py bdist_wheel -``` - -The `build_alllocal.cmd` script automates these steps if you already created and activated the conda environment. - - -## Conda packages - -This needs a [working installed C compiler](http://blog.ionelmc.ro/2014/12/21/compiling-python-extensions-on-windows/) -for the version of python you are compiling the package for but you don't need to setup the -environment variables. - -```sh -# only the first time... -conda install conda-build - -# the python version you want a package for... -set CONDA_PY=3.5 - -# builds the package, using a clean build environment -conda build ci\conda_recipe - -# install the new package -conda install --use-local matplotlib -``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..4400a4501b51 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +## Supported Versions + +The following table lists versions and whether they are supported. Security +vulnerability reports will be accepted and acted upon for all supported +versions. + +| Version | Supported | +| ------- | ------------------ | +| 3.10.x | :white_check_mark: | +| 3.9.x | :white_check_mark: | +| 3.8.x | :x: | +| 3.7.x | :x: | +| 3.6.x | :x: | +| 3.5.x | :x: | +| < 3.5 | :x: | + + +## Reporting a Vulnerability + + +To report a security vulnerability, please use the [Tidelift security +contact](https://tidelift.com/security). Tidelift will coordinate the fix and +disclosure. + +If you have found a security vulnerability, in order to keep it confidential, +please do not report an issue on GitHub. + +We do not award bounties for security vulnerabilities. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index e8d0d6c426fa..000000000000 --- a/appveyor.yml +++ /dev/null @@ -1,189 +0,0 @@ -# With infos from -# http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ -# https://packaging.python.org/en/latest/appveyor/ -# https://github.com/rmcgibbo/python-appveyor-conda-example - -# Backslashes in quotes need to be escaped: \ -> "\\" - -environment: - - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: http://stackoverflow.com/a/13751649/163740 - CMD_IN_ENV: "cmd /E:ON /V:ON /C obvci_appveyor_python_build_env.cmd" - # Workaround for https://github.com/conda/conda-build/issues/636 - PYTHONIOENCODING: "UTF-8" - TEST_ARGS: --no-pep8 - PYTEST_ARGS: -ra --timeout=300 --durations=25 #--cov-report= --cov=lib #-n %NUMBER_OF_PROCESSORS% - USE_PYTEST: no - #PYTHONHASHSEED: 0 # Workaround for pytest-xdist flaky colletion order - # # https://github.com/pytest-dev/pytest/issues/920 - # # https://github.com/pytest-dev/pytest/issues/1075 - - matrix: - # for testing purpose: numpy 1.8 on py2.7, for the rest use 1.10/latest - # theoretically the CONDA_INSTALL_LOCN could be only two: one for 32bit, - # one for 64bit because we construct envs anyway. But using one for the - # right python version is hopefully making it fast due to package caching. - - TARGET_ARCH: "x64" - CONDA_PY: "27" - CONDA_NPY: "18" - PYTHON_VERSION: "2.7" - TEST_ALL: "no" - CONDA_INSTALL_LOCN: "C:\\Miniconda-x64" - - TARGET_ARCH: "x64" - CONDA_PY: "34" - CONDA_NPY: "110" - PYTHON_VERSION: "3.4" - TEST_ALL: "no" - CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" - - TARGET_ARCH: "x64" - CONDA_PY: "35" - CONDA_NPY: "110" - PYTHON_VERSION: "3.5" - TEST_ALL: "no" - CONDA_INSTALL_LOCN: "C:\\Miniconda35-x64" - - TARGET_ARCH: "x64" - CONDA_PY: "35" - CONDA_NPY: "110" - PYTHON_VERSION: "3.5" - TEST_ALL: "no" - CONDA_INSTALL_LOCN: "C:\\Miniconda35-x64" - USE_PYTEST: yes - - TARGET_ARCH: "x86" - CONDA_PY: "27" - CONDA_NPY: "18" - PYTHON_VERSION: "2.7" - # this variable influence pdf/svg and most importantly the latex related tests - # which triples the runtime of the tests (7-8min vs 30min). - # pick the one which seems to make the most problems and run it last, so that - # the rest of the tests can give feedback earlier - TEST_ALL: "yes" - CONDA_INSTALL_LOCN: "C:\\Miniconda" - -# We always use a 64-bit machine, but can build x86 distributions -# with the PYTHON_ARCH variable (which is used by CMD_IN_ENV). -platform: - - x64 - -# all our python builds have to happen in tests_script... -build: false - -init: - - cmd: "ECHO %PYTHON_VERSION% PYTEST=%USE_PYTEST% %CONDA_INSTALL_LOCN%" - -install: - - cmd: set PATH=%CONDA_INSTALL_LOCN%;%CONDA_INSTALL_LOCN%\scripts;%PATH%; - - cmd: set PYTHONUNBUFFERED=1 - # for obvci_appveyor_python_build_env.cmd - - cmd: conda install -c pelson/channel/development --yes --quiet obvious-ci - # for msinttypes and newer stuff - - cmd: conda config --add channels conda-forge - - cmd: conda config --set show_channel_urls yes - - cmd: conda config --set always_yes true - # For building conda packages - - cmd: conda install --yes conda-build jinja2 anaconda-client - # this is now the downloaded conda... - - conda info -a - - # Fix the appveyor build environment to work with conda build - # workaround for missing vcvars64.bat in py34 64bit - - cmd: copy ci\appveyor\vcvars64.bat "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64" - - # For building, use a new environment which only includes the requirements for mpl - # same things as the requirements in ci/conda_recipe/meta.yaml - # if conda-forge gets a new pyqt, it might be nice to install it as well to have more backends - # https://github.com/conda-forge/conda-forge.github.io/issues/157#issuecomment-223536381 - - conda create -q -n test-environment python=%PYTHON_VERSION% - pip setuptools numpy python-dateutil freetype=2.6 msinttypes "tk=8.5" - pyparsing pytz tornado "libpng>=1.6.21,<1.7" "zlib=1.2" "cycler>=0.10" - nose mock sphinx - - activate test-environment - - cmd: echo %PYTHON_VERSION% %TARGET_ARCH% - - cmd: IF %PYTHON_VERSION% == 2.7 conda install -q functools32 - # pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124 - - if x%USE_PYTEST% == xyes conda install -q pytest "pytest-cov>=2.3.1" pytest-timeout #pytest-xdist - - # Let the install prefer the static builds of the libs - - set LIBRARY_LIB=%CONDA_PREFIX%\Library\lib - - cmd: 'mkdir lib || cmd /c "exit /b 0"' - - copy /Y %LIBRARY_LIB%\zlibstatic.lib lib\z.lib - - copy /Y %LIBRARY_LIB%\libpng_static.lib lib\png.lib - # These z.lib / png.lib are not static versions but files which end up as - # dependencies to the dll file. This is fine for the conda build, but not here - # and for the wheels - - del %LIBRARY_LIB%\png.lib - - del %LIBRARY_LIB%\z.lib - - set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. - # enables the local freetype build - - copy ci\travis\setup.cfg . - # Show the installed packages + versions - - conda list - -test_script: - # Now build the thing.. - - '%CMD_IN_ENV% python setup.py develop' - # these should show no z, png, or freetype dll... - - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" - #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\_png*.pyd' - #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd' - - cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' - - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr z.*.dll && exit /b 1 || exit /b 0' - - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr png.*.dll && exit /b 1 || exit /b 0' - - # this are optional dependencies so that we don't skip so many tests... - - cmd: if x%TEST_ALL% == xyes; conda install -q pillow miktex inkscape - # missing packages on conda-forge for ffmpeg avconv mencoder imagemagick - - cmd: if x%TEST_ALL% == xyes; conda install -q -c menpo ffmpeg # a repackaged version - # This install sometimes failed randomly :-( - #- cmd: choco install imagemagick - - # Test import of tkagg backend - - python -c "import matplotlib as m; m.use('tkagg'); import matplotlib.pyplot as plt; print(plt.get_backend())" - # tests - - if x%USE_PYTEST% == xyes echo The following args are passed to pytest %PYTEST_ARGS% - - if x%USE_PYTEST% == xyes py.test %PYTEST_ARGS% %TEST_ARGS% - - if x%USE_PYTEST% == xno python tests.py %TEST_ARGS% - # Generate a html for visual tests - - python visual_tests.py - -after_test: - # After the tests were a success, build packages (wheels and conda) - - # Build the wheel with the static libs - # Hide the output, the copied files really clutter the build log... - - cmd: '%CMD_IN_ENV% python setup.py bdist_wheel > NUL:' - - # And now the conda build after a cleanup... - # cleanup build files so that they don't pollute the conda build but keep the wheel in dist... - - cmd: git clean -d -x -f -e dist/ - # cleanup the environment so that the test-environment does not leak into the conda build... - - cmd: set MPLBASEDIRLIST= - - cmd: set LIBRARY_LIB= - - cmd: deactivate - - cmd: path - - cmd: where python - - cmd: '%CMD_IN_ENV% conda config --get channels' - - cmd: '%CMD_IN_ENV% conda build -q .\ci\conda_recipe' - - # Move the conda package into the dist directory, to register it - # as an "artifact" for Appveyor. - - cmd: 'copy /Y %CONDA_INSTALL_LOCN%\conda-bld\win-32\*.bz2 dist || cmd /c "exit /b 0"' - - cmd: 'copy /Y %CONDA_INSTALL_LOCN%\conda-bld\win-64\*.bz2 dist || cmd /c "exit /b 0"' - - cmd: dir dist\ - - cmd: echo finished... - -artifacts: - - path: dist\* - name: packages - - - path: result_images\* - name: result_images - type: zip - -on_failure: - - python visual_tests.py - - echo zipping images after a failure... - - 7z a result_images.zip result_images\ |grep -v "Compressing" - - appveyor PushArtifact result_images.zip diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000000..d68a9d36f0d3 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,162 @@ +# Python package +# Create and test a Python package on multiple Python versions. +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and +# more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/python?view=azure-devops + +--- +trigger: + branches: + exclude: + - v*-doc +pr: + branches: + exclude: + - v*-doc + paths: + exclude: + - doc/**/* + - galleries/**/* + +stages: + + - stage: Check + jobs: + - job: Skip + pool: + vmImage: 'ubuntu-latest' + variables: + DECODE_PERCENTS: 'false' + RET: 'true' + steps: + - bash: | + git_log=`git log --max-count=1 --skip=1 --pretty=format:"%B" | tr "\n" " "` + echo "##vso[task.setvariable variable=log]$git_log" + - bash: echo "##vso[task.setvariable variable=RET]false" + condition: >- + or(contains(variables.log, '[skip azp]'), + contains(variables.log, '[azp skip]'), + contains(variables.log, '[skip ci]'), + contains(variables.log, '[ci skip]'), + contains(variables.log, '[ci doc]')) + - bash: echo "##vso[task.setvariable variable=start_main;isOutput=true]$RET" + name: result + + - stage: Main + condition: and(succeeded(), eq(dependencies.Check.outputs['Skip.result.start_main'], 'true')) + dependsOn: Check + jobs: + - job: Pytest + strategy: + matrix: + Windows_py311: + vmImage: 'windows-2022' # Keep one job pinned to the oldest image + python.version: '3.11' + Windows_py312: + vmImage: 'windows-latest' + python.version: '3.12' + Windows_py313: + vmImage: 'windows-latest' + python.version: '3.13' + maxParallel: 4 + pool: + vmImage: '$(vmImage)' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + architecture: 'x64' + displayName: 'Use Python $(python.version)' + + - bash: | + choco install ninja + displayName: 'Install dependencies' + + - bash: | + python -m pip install --upgrade pip + python -m pip install --upgrade -r requirements/dev/build-requirements.txt + python -m pip install -r requirements/testing/all.txt -r requirements/testing/extra.txt + displayName: 'Install dependencies with pip' + + - bash: | + CONFIG='--config-settings=setup-args=--vsenv' + CONFIG="$CONFIG --config-settings=setup-args=-Dcpp_link_args=-PROFILE" + CONFIG="$CONFIG --config-settings=setup-args=-Dbuildtype=debug" + + python -m pip install \ + --no-build-isolation $CONFIG \ + --verbose --editable .[dev] + displayName: "Install self" + + - script: env + displayName: 'print env' + + - script: pip list + displayName: 'print pip' + + - bash: | + set -e + SESSION_ID=$(python -c "import uuid; print(uuid.uuid4(), end='')") + echo "Coverage session ID: ${SESSION_ID}" + VS=$(ls -d /c/Program\ Files*/Microsoft\ Visual\ Studio/*/Enterprise) + echo "Visual Studio: ${VS}" + DIR="$VS/Common7/IDE/Extensions/Microsoft/CodeCoverage.Console" + # This is for MSVC 2022 (on windows-latest). + TOOL="$DIR/Microsoft.CodeCoverage.Console.exe" + for f in build/cp*/src/*.pyd; do + echo $f + echo "==============================" + "$TOOL" instrument $f --session-id $SESSION_ID \ + --log-level Verbose --log-file instrument.log + cat instrument.log + rm instrument.log + done + echo "Starting $TOOL in server mode" + "$TOOL" collect \ + --session-id $SESSION_ID --server-mode \ + --output-format cobertura --output extensions.xml \ + --log-level Verbose --log-file extensions.log & + VS_VER=2022 + + echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" + + PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ + --maxfail=50 --timeout=300 --durations=25 \ + --junitxml=junit/test-results.xml --cov-report=xml --cov=lib + + if [[ $VS_VER == 2022 ]]; then + "$TOOL" shutdown $SESSION_ID + echo "Coverage collection log" + echo "=======================" + cat extensions.log + else + "$TOOL" shutdown -session:$SESSION_ID + fi + displayName: 'pytest' + + - bash: | + if [[ -f extensions.coverage ]]; then + # For MSVC 2019. + "$VS_COVERAGE_TOOL" analyze -output:extensions.xml \ + -include_skipped_functions -include_skipped_modules \ + extensions.coverage + rm extensions.coverage + fi + displayName: 'Filter C coverage' + condition: succeededOrFailed() + - bash: | + bash <(curl -s https://codecov.io/bash) \ + -n "$PYTHON_VERSION $AGENT_OS" \ + -f 'coverage.xml' -f 'extensions.xml' + displayName: 'Upload to codecov.io' + condition: succeededOrFailed() + + - task: PublishTestResults@2 + inputs: + testResultsFiles: '**/test-results.xml' + testRunTitle: 'Python $(python.version)' + condition: succeededOrFailed() + + - publish: $(System.DefaultWorkingDirectory)/result_images + artifact: $(Agent.JobName)-result_images + condition: failed() diff --git a/boilerplate.py b/boilerplate.py deleted file mode 100644 index 601cf1bb3eca..000000000000 --- a/boilerplate.py +++ /dev/null @@ -1,361 +0,0 @@ -""" -Script to autogenerate pyplot wrappers. - -When this script is run, the current contents of pyplot are -split into generatable and non-generatable content (via the magic header -:attr:`PYPLOT_MAGIC_HEADER`) and the generatable content is overwritten. -Hence, the non-generatable content should be edited in the pyplot.py file -itself, whereas the generatable content must be edited via templates in -this file. - -This file is python 3 only due to the use of `inspect` -""" -# We did try to do the wrapping the smart way, -# with callable functions and new.function, but could never get the -# docstrings right for python2.2. See -# http://groups.google.com/group/comp.lang.python/browse_frm/thread/dcd63ec13096a0f6/1b14640f3a4ad3dc?#1b14640f3a4ad3dc -# For some later history, see -# http://thread.gmane.org/gmane.comp.python.matplotlib.devel/7068 - -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import six - -import os -import inspect -import random -import types - -import textwrap - -# this line imports the installed copy of matplotlib, and not the local copy -from matplotlib.axes import Axes - - -# this is the magic line that must exist in pyplot, after which the boilerplate content will be -# appended -PYPLOT_MAGIC_HEADER = '################# REMAINING CONTENT GENERATED BY boilerplate.py ##############\n' - -PYPLOT_PATH = os.path.join(os.path.dirname(__file__), 'lib', 'matplotlib', - 'pyplot.py') - - -AUTOGEN_MSG = """ -# This function was autogenerated by boilerplate.py. Do not edit as -# changes will be lost""" - - -PLOT_TEMPLATE = AUTOGEN_MSG + """ -@_autogen_docstring(Axes.%(func)s) -def %(func)s(%(argspec)s): - %(ax)s = gca() - # allow callers to override the hold state by passing hold=True|False - %(washold)s = %(ax)s.ishold() -%(sethold)s - if hold is not None: - %(ax)s.hold(hold) - try: - %(ret)s = %(ax)s.%(func)s(%(call)s) - finally: - %(ax)s.hold(%(washold)s) -%(mappable)s - return %(ret)s -""" - - -# Used for misc functions such as cla/legend etc. -MISC_FN_TEMPLATE = AUTOGEN_MSG + """ -@docstring.copy_dedent(Axes.%(func)s) -def %(func)s(%(argspec)s): - %(ret)s = gca().%(func)s(%(call)s) - return %(ret)s -""" - -# Used for colormap functions -CMAP_TEMPLATE = AUTOGEN_MSG + """ -def {name}(): - ''' - set the default colormap to {name} and apply to current image if any. - See help(colormaps) for more information - ''' - rc('image', cmap='{name}') - im = gci() - - if im is not None: - im.set_cmap(cm.{name}) - -""" - - -def boilerplate_gen(): - """Generator of lines for the automated part of pyplot.""" - - # these methods are all simple wrappers of Axes methods by the same - # name. - _plotcommands = ( - 'acorr', - 'angle_spectrum', - 'arrow', - 'axhline', - 'axhspan', - 'axvline', - 'axvspan', - 'bar', - 'barh', - 'broken_barh', - 'boxplot', - 'cohere', - 'clabel', - 'contour', - 'contourf', - 'csd', - 'errorbar', - 'eventplot', - 'fill', - 'fill_between', - 'fill_betweenx', - 'hexbin', - 'hist', - 'hist2d', - 'hlines', - 'imshow', - 'loglog', - 'magnitude_spectrum', - 'pcolor', - 'pcolormesh', - 'phase_spectrum', - 'pie', - 'plot', - 'plot_date', - 'psd', - 'quiver', - 'quiverkey', - 'scatter', - 'semilogx', - 'semilogy', - 'specgram', - #'spy', - 'stackplot', - 'stem', - 'step', - 'streamplot', - 'tricontour', - 'tricontourf', - 'tripcolor', - 'triplot', - 'violinplot', - 'vlines', - 'xcorr', - 'barbs', - ) - - _misccommands = ( - 'cla', - 'grid', - 'legend', - 'table', - 'text', - 'annotate', - 'ticklabel_format', - 'locator_params', - 'tick_params', - 'margins', - 'autoscale', - ) - - cmappable = { - 'contour': 'if %(ret)s._A is not None: sci(%(ret)s)', - 'contourf': 'if %(ret)s._A is not None: sci(%(ret)s)', - 'hexbin': 'sci(%(ret)s)', - 'scatter': 'sci(%(ret)s)', - 'pcolor': 'sci(%(ret)s)', - 'pcolormesh': 'sci(%(ret)s)', - 'hist2d': 'sci(%(ret)s[-1])', - 'imshow': 'sci(%(ret)s)', - #'spy' : 'sci(%(ret)s)', ### may return image or Line2D - 'quiver': 'sci(%(ret)s)', - 'specgram': 'sci(%(ret)s[-1])', - 'streamplot': 'sci(%(ret)s.lines)', - 'tricontour': 'if %(ret)s._A is not None: sci(%(ret)s)', - 'tricontourf': 'if %(ret)s._A is not None: sci(%(ret)s)', - 'tripcolor': 'sci(%(ret)s)', - } - - def format_value(value): - """ - Format function default values as needed for inspect.formatargspec. - The interesting part is a hard-coded list of functions used - as defaults in pyplot methods. - """ - if isinstance(value, types.FunctionType): - if value.__name__ in ('detrend_none', 'window_hanning'): - return '=mlab.' + value.__name__ - if value.__name__ == 'mean': - return '=np.' + value.__name__ - raise ValueError(('default value %s unknown to boilerplate.' + - 'formatvalue') % value) - return '=' + repr(value) - - text_wrapper = textwrap.TextWrapper(break_long_words=False) - - for fmt, cmdlist in [(PLOT_TEMPLATE, _plotcommands), - (MISC_FN_TEMPLATE, _misccommands)]: - for func in cmdlist: - # For some commands, an additional line is needed to set the - # color map - if func in cmappable: - mappable = ' ' + cmappable[func] % locals() - else: - mappable = '' - - # Get argspec of wrapped function - base_func = getattr(Axes, func) - has_data = 'data' in inspect.signature(base_func).parameters - work_func = inspect.unwrap(base_func) - - if six.PY2: - args, varargs, varkw, defaults = inspect.getargspec(work_func) - else: - (args, varargs, varkw, defaults, kwonlyargs, kwonlydefs, - annotations) = inspect.getfullargspec(work_func) - args.pop(0) # remove 'self' argument - if defaults is None: - defaults = () - else: - def_edited = [] - for val in defaults: - if six.PY2: - if isinstance(val, unicode): - val = val.encode('ascii', 'ignore') - def_edited.append(val) - defaults = tuple(def_edited) - - # Add a data keyword argument if needed (fmt is PLOT_TEMPLATE) and - # possible (if *args is used, we can't just add a data - # argument in front of it since it would gobble one of the - # arguments the user means to pass via *args) - # This needs to be done here so that it goes into call - if not varargs and fmt is PLOT_TEMPLATE and has_data: - args.append('data') - defaults = defaults + (None,) - - # How to call the wrapped function - call = [] - for i, arg in enumerate(args): - if len(defaults) < len(args) - i: - call.append('%s' % arg) - else: - call.append('%s=%s' % (arg, arg)) - - # remove the data keyword as it was needed above to go into the - # call but should go after `hold` in the signature. - # This is janky as all get out, but hopefully boilerplate will - # be retired soon. - if not varargs and fmt is PLOT_TEMPLATE and has_data: - args.pop() - defaults = defaults[:-1] - - if varargs is not None: - call.append('*' + varargs) - if varkw is not None: - call.append('**' + varkw) - call = ', '.join(call) - - text_wrapper.width = 80 - 19 - len(func) - join_with = '\n' + ' ' * (18 + len(func)) - call = join_with.join(text_wrapper.wrap(call)) - - # Add a hold keyword argument if needed (fmt is PLOT_TEMPLATE) and - # possible (if *args is used, we can't just add a hold - # argument in front of it since it would gobble one of the - # arguments the user means to pass via *args) - if varargs: - sethold = " hold = %(varkw)s.pop('hold', None)" % locals() - elif fmt is PLOT_TEMPLATE: - args.append('hold') - defaults = defaults + (None,) - if has_data: - args.append('data') - defaults = defaults + (None,) - sethold = '' - - # Now we can build the argspec for defining the wrapper - argspec = inspect.formatargspec(args, varargs, varkw, defaults, - formatvalue=format_value) - argspec = argspec[1:-1] # remove parens - - text_wrapper.width = 80 - 5 - len(func) - join_with = '\n' + ' ' * (5 + len(func)) - argspec = join_with.join(text_wrapper.wrap(argspec)) - - # A gensym-like facility in case some function takes an - # argument named washold, ax, or ret - washold, ret, ax = 'washold', 'ret', 'ax' - bad = set(args) | set((varargs, varkw)) - while washold in bad or ret in bad or ax in bad: - washold = 'washold' + str(random.randrange(10 ** 12)) - ret = 'ret' + str(random.randrange(10 ** 12)) - ax = 'ax' + str(random.randrange(10 ** 12)) - - # Since we can't avoid using some function names, - # bail out if they are used as argument names - for reserved in ('gca', 'gci'): - if reserved in bad: - msg = 'Axes method %s has kwarg named %s' % (func, reserved) - raise ValueError(msg) - - yield fmt % locals() - - cmaps = ( - 'autumn', - 'bone', - 'cool', - 'copper', - 'flag', - 'gray', - 'hot', - 'hsv', - 'jet', - 'pink', - 'prism', - 'spring', - 'summer', - 'winter', - 'spectral', - - 'magma', - 'inferno', - 'plasma', - 'viridis' - ) - # add all the colormaps (autumn, hsv, ....) - for name in cmaps: - yield CMAP_TEMPLATE.format(name=name) - - yield '' - yield '_setup_pyplot_info_docstrings()' - - -def build_pyplot(): - pyplot_path = os.path.join(os.path.dirname(__file__), 'lib', - 'matplotlib', 'pyplot.py') - - pyplot_orig = open(pyplot_path, 'r').readlines() - - try: - pyplot_orig = pyplot_orig[:pyplot_orig.index(PYPLOT_MAGIC_HEADER) + 1] - except IndexError: - raise ValueError('The pyplot.py file *must* have the exact line: %s' % PYPLOT_MAGIC_HEADER) - - pyplot = open(pyplot_path, 'w') - pyplot.writelines(pyplot_orig) - pyplot.write('\n') - - pyplot.writelines(boilerplate_gen()) - pyplot.write('\n') - - -if __name__ == '__main__': - # Write the matplotlib.pyplot file - build_pyplot() diff --git a/build_alllocal.cmd b/build_alllocal.cmd deleted file mode 100644 index 9eb9ceadbc68..000000000000 --- a/build_alllocal.cmd +++ /dev/null @@ -1,36 +0,0 @@ -:: This assumes you have installed all the dependencies via conda packages: -:: # create a new environment with the required packages -:: conda create -n "matplotlib_build" python=3.4 numpy python-dateutil pyparsing pytz tornado "cycler>=0.10" tk libpng zlib freetype -:: activate matplotlib_build -:: if you want qt backend, you also have to install pyqt -:: conda install pyqt -:: # this package is only available in the conda-forge channel -:: conda install -c conda-forge msinttypes -:: if you build on py2.7: -:: conda install -c conda-forge functools32 - -set TARGET=bdist_wheel -IF [%1]==[] ( - echo Using default target: %TARGET% -) else ( - set TARGET=%1 - echo Using user supplied target: %TARGET% -) - -IF NOT DEFINED CONDA_PREFIX ( - echo No Conda env activated: you need to create a conda env with the right packages and activate it! - GOTO:eof -) - -:: copy the libs which have "wrong" names -set LIBRARY_LIB=%CONDA_PREFIX%\Library\lib -mkdir lib || cmd /c "exit /b 0" -copy %LIBRARY_LIB%\zlibstatic.lib lib\z.lib -copy %LIBRARY_LIB%\libpng_static.lib lib\png.lib - -:: Make the header files and the rest of the static libs available during the build -:: CONDA_PREFIX is a env variable which is set to the currently active environment path -set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. - -:: build the target -python setup.py %TARGET% diff --git a/ci/appveyor/vcvars64.bat b/ci/appveyor/vcvars64.bat deleted file mode 100644 index c4659becc3ae..000000000000 --- a/ci/appveyor/vcvars64.bat +++ /dev/null @@ -1 +0,0 @@ -CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 \ No newline at end of file diff --git a/ci/check_version_number.py b/ci/check_version_number.py new file mode 100644 index 000000000000..8902fb0806c7 --- /dev/null +++ b/ci/check_version_number.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +""" +Check that the version number of the install Matplotlib does not start with 0 + +To run: + $ python3 -m build . + $ pip install dist/matplotlib*.tar.gz for sdist + $ pip install dist/matplotlib*.whl for wheel + $ ./ci/check_version_number.py +""" +import sys + +import matplotlib + + +print(f"Version {matplotlib.__version__} installed") +if matplotlib.__version__[0] == "0": + sys.exit("Version incorrectly starts with 0") diff --git a/ci/check_wheel_licenses.py b/ci/check_wheel_licenses.py new file mode 100644 index 000000000000..13470dc692bd --- /dev/null +++ b/ci/check_wheel_licenses.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +""" +Check that all specified .whl files have the correct LICENSE files included. + +To run: + $ python3 -m build --wheel + $ ./ci/check_wheel_licenses.py dist/*.whl +""" + +from pathlib import Path +import sys +import zipfile + + +if len(sys.argv) <= 1: + sys.exit('At least one wheel must be specified in command-line arguments.') + +project_dir = Path(__file__).parent.resolve().parent +license_dir = project_dir / 'LICENSE' + +license_file_names = {path.name for path in sorted(license_dir.glob('*'))} +for wheel in sys.argv[1:]: + print(f'Checking LICENSE files in: {wheel}') + with zipfile.ZipFile(wheel) as f: + wheel_license_file_names = {Path(path).name + for path in sorted(f.namelist()) + if '.dist-info/LICENSE' in path} + if not (len(wheel_license_file_names) and + wheel_license_file_names.issuperset(license_file_names)): + sys.exit(f'LICENSE file(s) missing:\n' + f'{wheel_license_file_names} !=\n' + f'{license_file_names}') diff --git a/ci/codespell-ignore-words.txt b/ci/codespell-ignore-words.txt new file mode 100644 index 000000000000..e138f26e216a --- /dev/null +++ b/ci/codespell-ignore-words.txt @@ -0,0 +1,23 @@ +aas +ABD +axises +coo +curvelinear +filll +flate +fpt +hax +inh +inout +ment +nd +oint +oly +te +thisy +ure +whis +wit +Copin +socio-economic +Ines diff --git a/ci/conda_recipe/README.md b/ci/conda_recipe/README.md deleted file mode 100644 index 7819c9f0c766..000000000000 --- a/ci/conda_recipe/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# conda package - -Up to now, this is mainly used to build a test conda package on windows on appveyor. \ No newline at end of file diff --git a/ci/conda_recipe/bld.bat b/ci/conda_recipe/bld.bat deleted file mode 100644 index a7810d418d2f..000000000000 --- a/ci/conda_recipe/bld.bat +++ /dev/null @@ -1,16 +0,0 @@ -set LIBPATH=%LIBRARY_LIB%; -set INCLUDE=%INCLUDE%;%PREFIX%\Library\include\freetype2 - -ECHO [directories] > setup.cfg -ECHO basedirlist = %LIBRARY_PREFIX% >> setup.cfg -ECHO [packages] >> setup.cfg -ECHO tests = False >> setup.cfg -ECHO sample_data = False >> setup.cfg -ECHO toolkits_tests = False >> setup.cfg - -@rem workaround for https://github.com/matplotlib/matplotlib/issues/6460 -@rem see also https://github.com/conda-forge/libpng-feedstock/pull/4 -copy /y %LIBRARY_LIB%\libpng16.lib %LIBRARY_LIB%\png.lib - -%PYTHON% setup.py install --single-version-externally-managed --record=record.txt -if errorlevel 1 exit 1 diff --git a/ci/conda_recipe/build.sh b/ci/conda_recipe/build.sh deleted file mode 100644 index c2967acb98cf..000000000000 --- a/ci/conda_recipe/build.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -if [ `uname` == Linux ]; then - pushd $PREFIX/lib - ln -s libtcl8.5.so libtcl.so - ln -s libtk8.5.so libtk.so - popd -fi - -cat < setup.cfg -[directories] -basedirlist = $PREFIX - -[packages] -tests = False -toolkit_tests = False -sample_data = False - -EOF - -# The macosx backend isn't building with conda at this stage. -if [ `uname` == Darwin ]; then -cat << EOF >> setup.cfg - -[gui_support] -tkagg = true -macosx = false - -EOF -fi - -cat setup.cfg -sed -i.bak "s|/usr/local|$PREFIX|" setupext.py - - -$PYTHON setup.py install --single-version-externally-managed --record=record.txt - diff --git a/ci/conda_recipe/cfg_qt4agg.patch b/ci/conda_recipe/cfg_qt4agg.patch deleted file mode 100644 index 16e6fc6c3934..000000000000 --- a/ci/conda_recipe/cfg_qt4agg.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git setup.cfg.template setup.cfg.template -index cae6f67..fd11c79 100644 ---- setup.cfg.template -+++ setup.cfg.template -@@ -88,7 +88,7 @@ - # if you have disabled the relevent extension modules. Agg will be used - # by default. - # --#backend = Agg -+backend = Qt4Agg - # - - [package_data] diff --git a/ci/conda_recipe/condaversion.patch b/ci/conda_recipe/condaversion.patch deleted file mode 100644 index 915fda3bcc23..000000000000 --- a/ci/conda_recipe/condaversion.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git setup.py setup.py -index 8af8b6d..4e4f9d2 100644 ---- setup.py -+++ setup.py -@@ -57,6 +57,9 @@ - import versioneer - __version__ = versioneer.get_version() - -+# For conda builds... -+with open("__conda_version__.txt", "w") as f: -+ f.write(__version__) - - # These are the packages in the order we want to display them. This - # list may contain strings to create section headers for the display. - \ No newline at end of file diff --git a/ci/conda_recipe/meta.yaml b/ci/conda_recipe/meta.yaml deleted file mode 100644 index 45db6b908159..000000000000 --- a/ci/conda_recipe/meta.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# Full credit goes to https://github.com/conda/conda-recipes for providing this recipe. -# It has been subsequently adapted for automated building with conda-forge and matplotlib. - -package: - name: matplotlib - version: 1.9.9 - -source: - path: ../../ - - patches: - # A patch to make Qt4Agg the default backend. - - cfg_qt4agg.patch # [linux] - # Patches the matplotlibrc template to default to Qt4. - - rctmp_pyside.patch # [not osx] - # dynamic version from git - # we can't use condas usual dynamic versions as setup.py uses - # multiprocessing during the configure stage and this seems to confuse conda-build. - # https://github.com/conda/conda-build/issues/1061 - - condaversion.patch - - -requirements: - build: - - python - - setuptools - - pkg-config # [not win] - - numpy x.x - - python-dateutil - - freetype 2.6* - - msinttypes # [win] - - cycler >=0.10 - - nose - - pyparsing - - pytz -# - py2cairo # [linux and py2k] - - tornado - - libpng >=1.6.21,<1.7 - - zlib 1.2* # [win] - - pyqt # [not osx] - - tk 8.5* # [linux] - - functools32 # [py2k] - - run: - - python - - numpy x.x - - cycler >=0.10 - - python-dateutil - - freetype 2.6* - - pytz - - pyparsing -# - py2cairo # [linux and py2k] - - qt 4* - - libpng >=1.6.21,<1.7 - - pyqt # [not osx] - - tk 8.5* # [linux and win] - - functools32 # [py2k] - -test: - imports: - - matplotlib - - matplotlib.pyplot - -about: - home: http://matplotlib.org/ - license: PSF-based (http://matplotlib.org/users/license.html) - summary: Publication quality figures in Python - -extra: - recipe-maintainers: - - janschulz # only in the mpl repository - - mdboom # rest form conda-forge - - ocefpaf - - pelson - - tacaswell diff --git a/ci/conda_recipe/osx-tk.patch b/ci/conda_recipe/osx-tk.patch deleted file mode 100644 index 1411225550e9..000000000000 --- a/ci/conda_recipe/osx-tk.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git setupext.py setupext.py -index ddf2ca1..b9e0729 100755 ---- setupext.py -+++ setupext.py -@@ -1659,52 +1659,11 @@ class BackendTkAgg(OptionalBackendPackage): - ext.library_dirs.extend([os.path.join(sys.prefix, 'dlls')]) - - elif sys.platform == 'darwin': -- # this config section lifted directly from Imaging - thanks to -- # the effbot! -- -- # First test for a MacOSX/darwin framework install - from os.path import join, exists -- framework_dirs = [ -- join(os.getenv('HOME'), '/Library/Frameworks'), -- '/Library/Frameworks', -- '/System/Library/Frameworks/', -- ] - -- # Find the directory that contains the Tcl.framework and -- # Tk.framework bundles. -- tk_framework_found = 0 -- for F in framework_dirs: -- # both Tcl.framework and Tk.framework should be present -- for fw in 'Tcl', 'Tk': -- if not exists(join(F, fw + '.framework')): -- break -- else: -- # ok, F is now directory with both frameworks. Continure -- # building -- tk_framework_found = 1 -- break -- if tk_framework_found: -- # For 8.4a2, we must add -I options that point inside -- # the Tcl and Tk frameworks. In later release we -- # should hopefully be able to pass the -F option to -- # gcc, which specifies a framework lookup path. -- -- tk_include_dirs = [ -- join(F, fw + '.framework', H) -- for fw in ('Tcl', 'Tk') -- for H in ('Headers', 'Versions/Current/PrivateHeaders') -- ] -- -- # For 8.4a2, the X11 headers are not included. Rather -- # than include a complicated search, this is a -- # hard-coded path. It could bail out if X11 libs are -- # not found... -- -- # tk_include_dirs.append('/usr/X11R6/include') -- frameworks = ['-framework', 'Tcl', '-framework', 'Tk'] -- ext.include_dirs.extend(tk_include_dirs) -- ext.extra_link_args.extend(frameworks) -- ext.extra_compile_args.extend(frameworks) -+ ext.include_dirs.append(join(sys.prefix, 'include')) -+ ext.libraries.extend(['tk8.5', 'tcl8.5']) -+ ext.library_dirs.append(join(sys.prefix, 'lib')) - - # you're still here? ok we'll try it this way... - else: diff --git a/ci/conda_recipe/rctmp_pyside.patch b/ci/conda_recipe/rctmp_pyside.patch deleted file mode 100644 index 906803575a90..000000000000 --- a/ci/conda_recipe/rctmp_pyside.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git matplotlibrc.template matplotlibrc.template -index fdbbf26..6902fe9 100644 ---- matplotlibrc.template -+++ matplotlibrc.template -@@ -35,12 +35,12 @@ - # You can also deploy your own backend outside of matplotlib by - # referring to the module name (which must be in the PYTHONPATH) as - # 'module://my_backend'. --backend : $TEMPLATE_BACKEND -+backend : Qt4Agg - - # If you are using the Qt4Agg backend, you can choose here - # to use the PyQt4 bindings or the newer PySide bindings to - # the underlying Qt4 toolkit. --#backend.qt4 : PyQt4 # PyQt4 | PySide -+backend.qt4 : PyQt4 # PyQt4 | PySide - - # Note that this can be overridden by the environment variable - # QT_API used by Enthought Tool Suite (ETS); valid values are diff --git a/ci/conda_recipe/run_test.py b/ci/conda_recipe/run_test.py deleted file mode 100644 index 5b3d95d1174f..000000000000 --- a/ci/conda_recipe/run_test.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import platform -import sys - -import matplotlib -import matplotlib.pyplot -import matplotlib._cntr -import matplotlib._image -import matplotlib._path -import matplotlib._png -import matplotlib._tri -import matplotlib.backends._backend_agg -import matplotlib.ft2font -import matplotlib.ttconv -if platform.system() not in ['Windows']: - import matplotlib.backends._tkagg - -import pylab -import mpl_toolkits - -if int(os.getenv('GUI_TEST', 0)): - assert matplotlib.rcParams['backend.qt4'] == 'PyQt4' - - import matplotlib.pyplot as plt - plt.ioff() - plt.title('If this window displays, success: CLOSE TO CONTINUE TESTS') - plt.plot([1,2,5,9]) - plt.show() diff --git a/ci/export_sdist_name.py b/ci/export_sdist_name.py new file mode 100644 index 000000000000..632c11a15f83 --- /dev/null +++ b/ci/export_sdist_name.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +""" +Determine the name of the sdist and export to GitHub output named SDIST_NAME. + +To run: + $ python3 -m build --sdist + $ ./ci/determine_sdist_name.py +""" +import os +from pathlib import Path +import sys + + +paths = [p.name for p in Path("dist").glob("*.tar.gz")] +if len(paths) != 1: + sys.exit(f"Only a single sdist is supported, but found: {paths}") + +print(paths[0]) +with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"SDIST_NAME={paths[0]}\n") diff --git a/ci/mypy-stubtest-allowlist.txt b/ci/mypy-stubtest-allowlist.txt new file mode 100644 index 000000000000..12b6feb9b2e0 --- /dev/null +++ b/ci/mypy-stubtest-allowlist.txt @@ -0,0 +1,54 @@ +# Non-typed (and private) modules/functions +matplotlib\.backends\..* +matplotlib\.tests(\..*)? +matplotlib\.pylab(\..*)? +matplotlib\._.* +matplotlib\.rcsetup\._listify_validator +matplotlib\.rcsetup\._validate_linestyle +matplotlib\.ft2font\.Glyph +matplotlib\.testing\.jpl_units\..* +matplotlib\.sphinxext(\..*)? + +# set methods have heavy dynamic usage of **kwargs, with differences for subclasses +# which results in technically inconsistent signatures, but not actually a problem +matplotlib\..*\.set$ + +# Typed inline, inconsistencies largely due to imports +matplotlib\.pyplot\..* +matplotlib\.typing\..* + +# Other decorator modifying signature +# Backcompat decorator which does not modify runtime reported signature +matplotlib\.offsetbox\..*Offset[Bb]ox\.get_offset + +# Inconsistent super/sub class parameter name (maybe rename for consistency) +matplotlib\.projections\.polar\.RadialLocator\.nonsingular +matplotlib\.ticker\.LogLocator\.nonsingular +matplotlib\.ticker\.LogitLocator\.nonsingular + +# Stdlib/Enum considered inconsistent (no fault of ours, I don't think) +matplotlib\.backend_bases\._Mode\.__new__ +matplotlib\.units\.Number\.__hash__ + +# 3.6 Pending deprecations +matplotlib\.figure\.Figure\.set_constrained_layout +matplotlib\.figure\.Figure\.set_constrained_layout_pads +matplotlib\.figure\.Figure\.set_tight_layout + +# Maybe should be abstractmethods, required for subclasses, stubs define once +matplotlib\.tri\..*TriInterpolator\.__call__ +matplotlib\.tri\..*TriInterpolator\.gradient + +# TypeVar used only in type hints +matplotlib\.backend_bases\.FigureCanvasBase\._T +matplotlib\.backend_managers\.ToolManager\._T +matplotlib\.spines\.Spine\._T + +# Parameter inconsistency due to 3.10 deprecation +matplotlib\.figure\.FigureBase\.get_figure + +# getitem method only exists for 3.10 deprecation backcompatability +matplotlib\.inset\.InsetIndicator\.__getitem__ + +# only defined in stubs; not present at runtime +matplotlib\.animation\.EventSourceProtocol diff --git a/ci/schemas/README.md b/ci/schemas/README.md new file mode 100644 index 000000000000..087fd31d2ab8 --- /dev/null +++ b/ci/schemas/README.md @@ -0,0 +1,5 @@ +YAML Schemas for linting and validation +======================================= + +Since pre-commit CI doesn't have Internet access, we need to bundle these files +in the repo. The schemas can be updated using `vendor_schemas.py`. diff --git a/ci/schemas/appveyor.json b/ci/schemas/appveyor.json new file mode 100644 index 000000000000..d19a10f23b75 --- /dev/null +++ b/ci/schemas/appveyor.json @@ -0,0 +1,781 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "allOf": [ + { + "$ref": "#/definitions/job" + } + ], + "definitions": { + "possiblySecretString": { + "anyOf": [ + { + "type": "string", + "description": "This value will be used directly (regular string)" + }, + { + "type": "number", + "description": "This value will be treated as a string even though it is a number" + }, + { + "title": "secret string", + "type": "object", + "additionalProperties": false, + "properties": { + "secure": { + "type": "string", + "description": "This should have been encrypted by the same user account to which the project belongs" + } + } + } + ] + }, + "commitFilter": { + "title": "commit filter", + "type": "object", + "additionalProperties": false, + "properties": { + "message": { + "type": "string", + "format": "regex", + "description": "Regex for matching commit message" + }, + "author": { + "description": "Commit author's username, name, email or regexp matching one of these.", + "anyOf": [ + { + "type": "string", + "format": "regex" + }, + { + "type": "string" + } + ] + }, + "files": { + "type": "array", + "description": "Only specific files (glob patterns)", + "items": { + "type": "string" + } + } + } + }, + "command": { + "title": "command", + "oneOf": [ + { + "type": "string", + "description": "Run a batch command" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "ps": { + "type": "string", + "description": "Run a PowerShell command" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "pwsh": { + "type": "string", + "description": "Run a PowerShell Core command" + } + } + }, + { + "type": "object", + "description": "Run a batch command", + "additionalProperties": false, + "properties": { + "cmd": { + "type": "string" + } + } + }, + { + "type": "object", + "description": "Run a Bash command", + "additionalProperties": false, + "properties": { + "sh": { + "type": "string" + } + } + } + ] + }, + "envVarHash": { + "title": "environment variable hash", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/possiblySecretString" + } + }, + "platform": { + "enum": ["x86", "x64", "ARM", "ARM64", "Win32", "Any CPU"] + }, + "configuration": { + "type": "string" + }, + "imageName": { + "enum": [ + "macOS", + "macOS-Mojave", + "macos-bigsur", + "macos-monterey", + "Previous macOS", + "Previous macOS-Mojave", + "Ubuntu", + "Ubuntu1604", + "Ubuntu1804", + "Ubuntu2004", + "Ubuntu2204", + "Previous Ubuntu", + "Previous Ubuntu1604", + "Previous Ubuntu1804", + "Previous Ubuntu2004", + "Visual Studio 2013", + "Visual Studio 2015", + "Visual Studio 2017", + "Visual Studio 2019", + "Visual Studio 2022", + "Visual Studio 2017 Preview", + "Visual Studio 2019 Preview", + "Previous Visual Studio 2013", + "Previous Visual Studio 2015", + "Previous Visual Studio 2017", + "Previous Visual Studio 2019", + "Previous Visual Studio 2022", + "zhaw18", + "WMF 5" + ] + }, + "image": { + "description": "Build worker image (VM template) -DEV_VERSION", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/imageName" + } + }, + { + "$ref": "#/definitions/imageName" + } + ] + }, + "jobScalars": { + "title": "job scalars", + "type": "object", + "properties": { + "image": { + "$ref": "#/definitions/image" + }, + "platform": { + "description": "Build platform, i.e. x86, x64, Any CPU. This setting is optional", + "oneOf": [ + { + "$ref": "#/definitions/platform" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/platform" + } + } + ] + }, + "configuration": { + "description": "Build Configuration, i.e. Debug, Release, etc.", + "oneOf": [ + { + "$ref": "#/definitions/configuration" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/configuration" + } + } + ] + } + }, + "allOf": [ + { + "not": { + "required": ["skip_tags"] + } + }, + { + "not": { + "required": ["skip_commits"] + } + }, + { + "not": { + "required": ["skip_branch_with_pr"] + } + }, + { + "not": { + "required": ["skip_non_tags"] + } + } + ] + }, + "job": { + "title": "job", + "type": "object", + "properties": { + "version": { + "description": "Version format", + "type": "string" + }, + "branches": { + "title": "branch options", + "type": "object", + "description": "Branches to build", + "additionalProperties": false, + "properties": { + "only": { + "description": "Whitelist", + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "description": "Blacklist", + "items": { + "type": "string" + } + } + } + }, + "skip_tags": { + "type": "boolean", + "description": "Do not build on tags (GitHub and BitBucket)" + }, + "skip_non_tags": { + "type": "boolean", + "description": "Start builds on tags only (GitHub and BitBucket)" + }, + "skip_commits": { + "$ref": "#/definitions/commitFilter", + "description": "Skipping commits with particular message or from specific user" + }, + "only_commits": { + "$ref": "#/definitions/commitFilter", + "description": "Including commits with particular message or from specific user" + }, + "skip_branch_with_pr": { + "type": "boolean", + "description": "Do not build feature branch with open Pull Requests" + }, + "max_jobs": { + "description": "Maximum number of concurrent jobs for the project", + "type": "integer" + }, + "notifications": { + "type": "array", + "items": { + "title": "notification", + "type": "object" + } + }, + "image": { + "$ref": "#/definitions/image" + }, + "init": { + "description": "Scripts that are called at very beginning, before repo cloning", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "clone_folder": { + "type": "string", + "description": "Clone directory" + }, + "shallow_clone": { + "type": "boolean", + "description": "Fetch repository as zip archive", + "default": false + }, + "clone_depth": { + "description": "Set git clone depth", + "type": "integer" + }, + "hosts": { + "title": "host options", + "type": "object", + "description": "Setting up etc\\hosts file", + "additionalProperties": { + "type": "string", + "anyOf": [ + { + "format": "ipv4" + }, + { + "format": "ipv6" + } + ] + } + }, + "environment": { + "description": "Environment variables", + "anyOf": [ + { + "title": "environment options", + "type": "object", + "properties": { + "global": { + "$ref": "#/definitions/envVarHash", + "description": "variables defined here are no different than those defined at top level of 'environment' node" + }, + "matrix": { + "type": "array", + "description": "an array of environment variables, each member of which is one dimension in the build matrix calculation", + "items": { + "$ref": "#/definitions/envVarHash" + } + } + } + }, + { + "$ref": "#/definitions/envVarHash" + } + ] + }, + "matrix": { + "title": "matrix options", + "type": "object", + "additionalProperties": false, + "properties": { + "fast_finish": { + "type": "boolean", + "description": "Set this flag to immediately finish build once one of the jobs fails" + }, + "allow_failures": { + "type": "array", + "description": "This is how to allow failing jobs in the matrix", + "items": { + "$ref": "#/definitions/jobScalars" + } + }, + "exclude": { + "type": "array", + "description": "Exclude configuration from the matrix. Works similarly to 'allow_failures' but build not even being started for excluded combination.", + "items": { + "$ref": "#/definitions/job" + } + } + } + }, + "cache": { + "type": "array", + "description": "Build cache to preserve files/folders between builds", + "items": { + "type": "string" + } + }, + "services": { + "type": "array", + "description": "Enable service required for build/tests", + "items": { + "enum": [ + "docker", + "iis", + "mongodb", + "msmq", + "mssql", + "mssql2008r2sp2", + "mssql2008r2sp2rs", + "mssql2012sp1", + "mssql2012sp1rs", + "mssql2014", + "mssql2014rs", + "mssql2016", + "mssql2017", + "mysql", + "postgresql", + "postgresql93", + "postgresql94", + "postgresql95", + "postgresql96", + "postgresql10", + "postgresql11", + "postgresql12", + "postgresql13" + ] + } + }, + "install": { + "description": "Scripts that run after cloning repository", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "assembly_info": { + "title": "assembly options", + "type": "object", + "description": "Enable patching of AssemblyInfo.* files", + "additionalProperties": false, + "properties": { + "patch": { + "type": "boolean" + }, + "file": { + "type": "string" + }, + "assembly_version": { + "type": "string" + }, + "assembly_file_version": { + "type": "string" + }, + "assembly_informational_version": { + "type": "string" + } + } + }, + "nuget": { + "title": "NuGet options", + "type": "object", + "description": "Automatically register private account and/or project AppVeyor NuGet feeds", + "properties": { + "account_feed": { + "type": "boolean" + }, + "project_feed": { + "type": "boolean" + }, + "disable_publish_on_pr": { + "type": "boolean", + "description": "Disable publishing of .nupkg artifacts to account/project feeds for pull request builds" + } + } + }, + "platform": { + "description": "Build platform, i.e. x86, x64, Any CPU. This setting is optional", + "oneOf": [ + { + "$ref": "#/definitions/platform" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/platform" + } + } + ] + }, + "configuration": { + "description": "Build Configuration, i.e. Debug, Release, etc.", + "oneOf": [ + { + "$ref": "#/definitions/configuration" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/configuration" + } + } + ] + }, + "build": { + "oneOf": [ + { + "type": "boolean", + "enum": [false] + }, + { + "title": "build options", + "type": "object", + "additionalProperties": false, + "properties": { + "parallel": { + "type": "boolean", + "description": "Enable MSBuild parallel builds" + }, + "project": { + "type": "string", + "description": "Path to Visual Studio solution or project" + }, + "publish_wap": { + "type": "boolean", + "description": "Package Web Application Projects (WAP) for Web Deploy" + }, + "publish_wap_xcopy": { + "type": "boolean", + "description": "Package Web Application Projects (WAP) for XCopy deployment" + }, + "publish_wap_beanstalk": { + "type": "boolean", + "description": "Package Web Applications for AWS Elastic Beanstalk deployment" + }, + "publish_wap_octopus": { + "type": "boolean", + "description": "Package Web Applications for Octopus deployment" + }, + "publish_azure_webjob": { + "type": "boolean", + "description": "Package Azure WebJobs for Zip Push deployment" + }, + "publish_azure": { + "type": "boolean", + "description": "Package Azure Cloud Service projects and push to artifacts" + }, + "publish_aspnet_core": { + "type": "boolean", + "description": "Package ASP.NET Core projects" + }, + "publish_core_console": { + "type": "boolean", + "description": "Package .NET Core console projects" + }, + "publish_nuget": { + "type": "boolean", + "description": "Package projects with .nuspec files and push to artifacts" + }, + "publish_nuget_symbols": { + "type": "boolean", + "description": "Generate and publish NuGet symbol packages" + }, + "include_nuget_references": { + "type": "boolean", + "description": "Add -IncludeReferencedProjects option while packaging NuGet artifacts" + }, + "verbosity": { + "enum": ["quiet", "minimal", "normal", "detailed"], + "description": "MSBuild verbosity level" + } + } + } + ] + }, + "before_build": { + "description": "Scripts to run before build", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "before_package": { + "description": "Scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_build": { + "description": "Scripts to run after build", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "build_script": { + "description": "To run your custom scripts instead of automatic MSBuild", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "before_test": { + "description": "Scripts to run before tests", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "test": { + "oneOf": [ + { + "type": "boolean", + "enum": [false], + "description": "To disable automatic tests" + }, + { + "title": "test options", + "description": "To run tests again only selected assemblies and/or categories", + "type": "object", + "additionalProperties": false, + "properties": { + "assemblies": { + "title": "assembly options", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "categories": { + "oneOf": [ + { + "title": "category options", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "type": "array", + "items": { + "type": "string" + } + }, + "except": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "description": "To run tests from different categories as separate jobs in parallel", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string", + "description": "A category common for all jobs" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + ] + } + } + } + ] + }, + "test_script": { + "description": "To run your custom scripts instead of automatic tests", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_test": { + "type": "array", + "description": "Scripts to run after tests", + "items": { + "$ref": "#/definitions/command" + } + }, + "artifacts": { + "type": "array", + "items": { + "title": "artifact options", + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "enum": [ + "Auto", + "WebDeployPackage", + "NuGetPackage", + "AzureCloudService", + "AzureCloudServiceConfig", + "SsdtPackage", + "Zip", + "File" + ] + } + }, + "required": ["path"] + } + }, + "before_deploy": { + "type": "array", + "description": "Scripts to run before deployment", + "items": { + "$ref": "#/definitions/command" + } + }, + "deploy": { + "oneOf": [ + { + "enum": ["off"] + }, + { + "type": "array", + "items": { + "title": "deployment options", + "type": "object" + } + } + ] + }, + "deploy_script": { + "description": "To run your custom scripts instead of provider deployments", + "type": "array", + "items": { + "$ref": "#/definitions/command" + } + }, + "after_deploy": { + "type": "array", + "description": "Scripts to run after deployment", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_success": { + "type": "array", + "description": "On successful build", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_failure": { + "type": "array", + "description": "On build failure", + "items": { + "$ref": "#/definitions/command" + } + }, + "on_finish": { + "type": "array", + "description": "After build failure or success", + "items": { + "$ref": "#/definitions/command" + } + } + } + } + }, + "id": "https://json.schemastore.org/appveyor.json", + "title": "JSON schema for AppVeyor CI configuration files" +} diff --git a/ci/schemas/circleciconfig.json b/ci/schemas/circleciconfig.json new file mode 100644 index 000000000000..076944098440 --- /dev/null +++ b/ci/schemas/circleciconfig.json @@ -0,0 +1,1411 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/circleciconfig.json", + "definitions": { + "logical": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nA logical statement to be used in dynamic configuration", + "oneOf": [ + { + "type": ["string", "boolean", "integer", "number"] + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "and": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical and: true when all statements in the list are true", + "type": "array", + "items": { + "$ref": "#/definitions/logical" + } + }, + "or": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical or: true when at least one statements in the list is true", + "type": "array", + "items": { + "$ref": "#/definitions/logical" + } + }, + "not": { + "$ref": "#/definitions/logical", + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nLogical not: true when statement is false" + }, + "equal": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nTrue when all elements in the list are equal", + "type": "array" + }, + "matches": { + "description": "https://circleci.com/docs/configuration-reference#logic-statements \n\nTrue when value matches the pattern", + "type": "object", + "additionalProperties": false, + "properties": { + "pattern": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + } + } + ] + }, + "filter": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "only": { + "description": "Either a single branch specifier, or a list of branch specifiers", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "ignore": { + "description": "Either a single branch specifier, or a list of branch specifiers", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "orbs": { + "description": "https://circleci.com/docs/configuration-reference#orbs-requires-version-21\n\nOrbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "description": "https://circleci.com/docs/creating-orbs#semantic-versioning-in-orbs\n\nAn orb to depend on and its semver range, or volatile for the most recent release.", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+@(dev:[\\.a-z0-9_-]+|\\d+|\\d+\\.\\d+|\\d+\\.\\d+\\.\\d+|volatile)$" + }, + { + "description": "https://circleci.com/docs/creating-orbs#creating-inline-orbs\n\nInline orbs can be handy during development of an orb or as a convenience for name-spacing jobs and commands in lengthy configurations, particularly if you later intend to share the orb with others.", + "type": "object", + "properties": { + "orbs": { + "$ref": "#/definitions/orbs" + }, + "commands": { + "$ref": "#/definitions/commands" + }, + "executors": { + "$ref": "#/definitions/executors" + }, + "jobs": { + "$ref": "#/definitions/jobs" + } + } + } + ] + } + }, + "commands": { + "description": "https://circleci.com/docs/configuration-reference#commands-requires-version-21\n\nA command definition defines a sequence of steps as a map to be executed in a job, enabling you to reuse a single command definition across multiple jobs.", + "type": "object", + "additionalProperties": { + "description": "https://circleci.com/docs/configuration-reference#commands-requires-version-21\n\nDefinition of a custom command.", + "type": "object", + "required": ["steps"], + "properties": { + "steps": { + "description": "A sequence of steps run inside the calling job of the command.", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + }, + "parameters": { + "description": "https://circleci.com/docs/reusing-config#using-the-parameters-declaration\n\nA map of parameter keys.", + "type": "object", + "patternProperties": { + "^[a-z][a-z0-9_-]+$": { + "oneOf": [ + { + "description": "https://circleci.com/docs/reusing-config#string\n\nA string parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["string"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#boolean\n\nA boolean parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["boolean"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "boolean" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#integer\n\nAn integer parameter.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["integer"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "integer" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#enum\n\nThe `enum` parameter may be a list of any values. Use the `enum` parameter type when you want to enforce that the value must be one from a specific set of string values.", + "type": "object", + "required": ["type", "enum"], + "properties": { + "type": { + "enum": ["enum"] + }, + "enum": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#executor\n\nUse an `executor` parameter type to allow the invoker of a job to decide what executor it will run on.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["executor"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string" + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#steps\n\nSteps are used when you have a job or command that needs to mix predefined and user-defined steps. When passed in to a command or job invocation, the steps passed as parameters are always defined as a sequence, even if only one step is provided.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["steps"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + } + }, + { + "description": "https://circleci.com/docs/reusing-config#environment-variable-name\n\nThe environment variable name parameter is a string that must match a POSIX_NAME regexp (e.g. no spaces or special characters) and is a more meaningful parameter type that enables additional checks to be performed. ", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "enum": ["env_var_name"] + }, + "description": { + "type": "string" + }, + "default": { + "type": "string", + "pattern": "^[a-zA-Z][a-zA-Z0-9_-]+$" + } + } + } + ] + } + } + }, + "description": { + "description": "A string that describes the purpose of the command.", + "type": "string" + } + } + } + }, + "dockerLayerCaching": { + "description": "Set to `true` to enable [Docker Layer Caching](https://circleci.com/docs/docker-layer-caching). Note: If you haven't already, you must open a support ticket to have a CircleCI Sales representative contact you about enabling this feature on your account for an additional fee.", + "type": "boolean", + "default": "true" + }, + "dockerExecutor": { + "description": "Options for the [docker executor](https://circleci.com/docs/configuration-reference#docker)", + "required": ["docker"], + "properties": { + "docker": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The name of a custom docker image to use", + "type": "string" + }, + "name": { + "description": "The name the container is reachable by. By default, container services are accessible through `localhost`", + "type": "string" + }, + "entrypoint": { + "description": "The command used as executable when launching the container", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "command": { + "description": "The command used as pid 1 (or args for entrypoint) when launching the container", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "user": { + "description": "Which user to run the command as", + "type": "string" + }, + "environment": { + "description": "A map of environment variable names and values", + "type": "object", + "additionalProperties": { + "type": ["string", "number", "boolean"] + } + }, + "auth": { + "description": "Authentication for registries using standard `docker login` credentials", + "type": "object", + "additionalProperties": false, + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "aws_auth": { + "description": "Authentication for AWS EC2 Container Registry (ECR). You can use the access/secret keys or OIDC.", + "type": "object", + "additionalProperties": false, + "properties": { + "aws_access_key_id": { + "type": "string" + }, + "aws_secret_access_key": { + "type": "string" + }, + "oidc_role_arn": { + "type": "string" + } + } + } + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. Note: A performance plan is required to access this feature.", + "type": "string", + "enum": [ + "small", + "medium", + "medium+", + "large", + "xlarge", + "2xlarge", + "2xlarge+", + "arm.medium", + "arm.large", + "arm.xlarge", + "arm.2xlarge" + ] + } + } + }, + "machineExecutor": { + "description": "Options for the [machine executor](https://circleci.com/docs/configuration-reference#machine)", + "type": "object", + "required": ["machine"], + "oneOf": [ + { + "properties": { + "machine": { + "oneOf": [ + { "const": "default" }, + { "const": true }, + { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-linux-machine-images-cloud). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "ubuntu-2004:2023.10.1", + "ubuntu-2004:2023.07.1", + "ubuntu-2004:2023.04.2", + "ubuntu-2004:2023.04.1", + "ubuntu-2004:2023.02.1", + "ubuntu-2004:2022.10.1", + "ubuntu-2004:2022.07.1", + "ubuntu-2004:2022.04.2", + "ubuntu-2004:2022.04.1", + "ubuntu-2004:202201-02", + "ubuntu-2004:202201-01", + "ubuntu-2004:202111-02", + "ubuntu-2004:202111-01", + "ubuntu-2004:202107-02", + "ubuntu-2004:202104-01", + "ubuntu-2004:202101-01", + "ubuntu-2004:202010-01", + "ubuntu-2004:current", + "ubuntu-2004:edge", + "ubuntu-2204:2023.10.1", + "ubuntu-2204:2023.07.2", + "ubuntu-2204:2023.04.2", + "ubuntu-2204:2023.04.1", + "ubuntu-2204:2023.02.1", + "ubuntu-2204:2022.10.2", + "ubuntu-2204:2022.10.1", + "ubuntu-2204:2022.07.2", + "ubuntu-2204:2022.07.1", + "ubuntu-2204:2022.04.2", + "ubuntu-2204:2022.04.1", + "ubuntu-2204:current", + "ubuntu-2204:edge", + "android:2023.11.1", + "android:2023.10.1", + "android:2023.09.1", + "android:2023.08.1", + "android:2023.07.1", + "android:2023.06.1", + "android:2023.05.1", + "android:2023.04.1", + "android:2023.03.1", + "android:2023.02.1", + "android:2022.12.1", + "android:2022.09.1", + "android:2022.08.1", + "android:2022.07.1", + "android:2022.06.2", + "android:2022.06.1", + "android:2022.04.1", + "android:2022.03.1", + "android:2022.01.1", + "android:2021.12.1", + "android:2021.10.1", + "android:202102-01" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + } + ] + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#linuxvm-execution-environment)", + "type": "string", + "enum": [ + "medium", + "large", + "xlarge", + "2xlarge", + "2xlarge+", + "arm.medium", + "arm.large", + "arm.xlarge", + "arm.2xlarge" + ] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-linux-gpu-images). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": ["linux-cuda-11:default", "linux-cuda-12:default"] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#gpu-execution-environment-linux)", + "type": "string", + "enum": ["gpu.nvidia.medium", "gpu.nvidia.large"] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-windows-machine-images-cloud). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "windows-server-2022-gui:2023.10.1", + "windows-server-2022-gui:2023.09.1", + "windows-server-2022-gui:2023.08.1", + "windows-server-2022-gui:2023.07.1", + "windows-server-2022-gui:2023.06.1", + "windows-server-2022-gui:2023.05.1", + "windows-server-2022-gui:2023.04.1", + "windows-server-2022-gui:2023.03.1", + "windows-server-2022-gui:2022.08.1", + "windows-server-2022-gui:2022.07.1", + "windows-server-2022-gui:2022.06.1", + "windows-server-2022-gui:2022.04.1", + "windows-server-2022-gui:current", + "windows-server-2022-gui:edge", + "windows-server-2019:2023.10.1", + "windows-server-2019:2023.08.1", + "windows-server-2019:2023.04.1", + "windows-server-2019:2022.08.1", + "windows-server-2019:current", + "windows-server-2019:edge" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#windows-execution-environment)", + "type": "string", + "enum": [ + "windows.medium", + "windows.large", + "windows.xlarge", + "windows.2xlarge" + ] + } + } + }, + { + "properties": { + "machine": { + "type": "object", + "additionalProperties": false, + "required": ["image"], + "properties": { + "image": { + "description": "The VM image to use. View [available images](https://circleci.com/docs/configuration-reference/#available-windows-gpu-image). **Note:** This key is **not** supported on the installable CircleCI. For information about customizing machine executor images on CircleCI installed on your servers, see our [VM Service documentation](https://circleci.com/docs/vm-service).", + "type": "string", + "enum": [ + "windows-server-2019-cuda:current", + "windows-server-2019-cuda:edge" + ] + }, + "docker_layer_caching": { + "$ref": "#/definitions/dockerLayerCaching" + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#gpu-execution-environment-windows)", + "type": "string", + "enum": ["windows.gpu.nvidia.medium"] + } + } + } + ] + }, + "macosExecutor": { + "description": "Options for the [macOS executor](https://circleci.com/docs/configuration-reference#macos)", + "type": "object", + "required": ["macos"], + "properties": { + "macos": { + "type": "object", + "additionalProperties": false, + "required": ["xcode"], + "properties": { + "xcode": { + "description": "The version of Xcode that is installed on the virtual machine, see the [Supported Xcode Versions section of the Testing iOS](https://circleci.com/docs/testing-ios#supported-xcode-versions) document for the complete list.", + "type": "string", + "enum": [ + "15.2.0", + "15.1.0", + "15.0.0", + "14.3.1", + "14.2.0", + "14.1.0", + "14.0.1", + "13.4.1", + "12.5.1" + ] + } + } + }, + "resource_class": { + "description": "Amount of CPU and RAM allocated for each job. View [available resource classes](https://circleci.com/docs/configuration-reference/#macos-execution-environment)", + "type": "string", + "enum": [ + "macos.x86.medium.gen2", + "macos.m1.medium.gen1", + "macos.m1.large.gen1" + ] + } + } + }, + "executorChoice": { + "type": "object", + "oneOf": [ + { + "$ref": "#/definitions/dockerExecutor" + }, + { + "$ref": "#/definitions/machineExecutor" + }, + { + "$ref": "#/definitions/macosExecutor" + } + ] + }, + "executors": { + "description": "Executors define the environment in which the steps of a job will be run, allowing you to reuse a single executor definition across multiple jobs.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/executorChoice", + "type": "object", + "properties": { + "shell": { + "description": "Shell to use for execution command in all steps. Can be overridden by shell in each step (default: See [Default Shell Options](https://circleci.com/docs/configuration-reference#default-shell-options)", + "type": "string" + }, + "working_directory": { + "description": "In which directory to run the steps.", + "type": "string" + }, + "environment": { + "description": "A map of environment variable names and values.", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + } + } + } + }, + "builtinSteps": { + "documentation": { + "run": { + "description": "https://circleci.com/docs/configuration-reference#run\n\nUsed for invoking all command-line programs, taking either a map of configuration values, or, when called in its short-form, a string that will be used as both the `command` and `name`. Run commands are executed using non-login shells by default, so you must explicitly source any dotfiles as part of the command." + }, + "checkout": { + "description": "https://circleci.com/docs/configuration-reference#checkout\n\nSpecial step used to check out source code to the configured `path` (defaults to the `working_directory`). The reason this is a special step is because it is more of a helper function designed to make checking out code easy for you. If you require doing git over HTTPS you should not use this step as it configures git to checkout over ssh." + }, + "setup_remote_docker": { + "description": "https://circleci.com/docs/configuration-reference#setup_remote_docker\n\nCreates a remote Docker environment configured to execute Docker commands." + }, + "save_cache": { + "description": "https://circleci.com/docs/configuration-reference#save_cache\n\nGenerates and stores a cache of a file or directory of files such as dependencies or source code in our object storage. Later jobs can restore this cache using the `restore_cache` step." + }, + "restore_cache": { + "description": "https://circleci.com/docs/configuration-reference#restore_cache\n\nRestores a previously saved cache based on a `key`. Cache needs to have been saved first for this key using the `save_cache` step." + }, + "deploy": { + "description": "https://circleci.com/docs/configuration-reference#deploy\n\nSpecial step for deploying artifacts. `deploy` uses the same configuration map and semantics as run step. Jobs may have more than one deploy step. In general deploy step behaves just like run with two exceptions:\n* In a job with parallelism, the deploy step will only be executed by node #0 and only if all nodes succeed. Nodes other than #0 will skip this step.\n* In a job that runs with SSH, the deploy step will not execute" + }, + "store_artifacts": { + "description": "https://circleci.com/docs/configuration-reference#store_artifacts\n\nStep to store artifacts (for example logs, binaries, etc) to be available in the web app or through the API." + }, + "store_test_results": { + "description": "https://circleci.com/docs/configuration-reference#storetestresults\n\nSpecial step used to upload test results so they display in builds' Test Summary section and can be used for timing analysis. To also see test result as build artifacts, please use the `store_artifacts` step." + }, + "persist_to_workspace": { + "description": "https://circleci.com/docs/configuration-reference#persist_to_workspace\n\nSpecial step used to persist a temporary file to be used by another job in the workflow" + }, + "attach_workspace": { + "description": "https://circleci.com/docs/configuration-reference#attach_workspace\n\nSpecial step used to attach the workflow's workspace to the current container. The full contents of the workspace are downloaded and copied into the directory the workspace is being attached at." + }, + "add_ssh_keys": { + "description": "https://circleci.com/docs/configuration-reference#add_ssh_keys\n\nSpecial step that adds SSH keys from a project's settings to a container. Also configures SSH to use these keys." + }, + "when": { + "description": "https://circleci.com/docs/configuration-reference#the-when-step-requires-version-21 \n\nConditional step to run on custom conditions (determined at config-compile time) that are checked before a workflow runs" + }, + "unless": { + "description": "https://circleci.com/docs/configuration-reference#the-when-step-requires-version-21 \n\nConditional step to run when custom conditions aren't met (determined at config-compile time) that are checked before a workflow runs" + } + }, + "configuration": { + "run": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/run" + } + ], + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": ["command"], + "properties": { + "command": { + "description": "Command to run via the shell", + "type": "string" + }, + "name": { + "description": "Title of the step to be shown in the CircleCI UI (default: full `command`)", + "type": "string" + }, + "shell": { + "description": "Shell to use for execution command", + "type": "string" + }, + "environment": { + "description": "Additional environmental variables, locally scoped to command", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + }, + "background": { + "description": "Whether or not this step should run in the background (default: false)", + "default": false, + "type": "boolean" + }, + "working_directory": { + "description": "In which directory to run this step (default: `working_directory` of the job", + "type": "string" + }, + "no_output_timeout": { + "description": "Elapsed time the command can run without output. The string is a decimal with unit suffix, such as \"20m\", \"1.25h\", \"5s\" (default: 10 minutes)", + "type": "string", + "pattern": "\\d+(\\.\\d+)?[mhs]", + "default": "10m" + }, + "when": { + "description": "Specify when to enable or disable the step. Takes the following values: `always`, `on_success`, `on_fail` (default: `on_success`)", + "enum": ["always", "on_success", "on_fail"] + } + } + } + ] + }, + "checkout": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/checkout" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Checkout directory (default: job's `working_directory`)", + "type": "string" + } + } + }, + "setup_remote_docker": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/setup_remote_docker" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "docker_layer_caching": { + "description": "When `docker_layer_caching` is set to `true`, CircleCI will try to reuse Docker Images (layers) built during a previous job or workflow (Paid feature)", + "type": "boolean", + "default": false + }, + "version": { + "description": "If your build requires a specific docker image, you can set it as an image attribute", + "anyOf": [ + { + "type": "string", + "enum": [ + "20.10.24", + "20.10.23", + "20.10.18", + "20.10.17", + "20.10.14", + "20.10.12", + "20.10.11", + "20.10.7", + "20.10.6", + "20.10.2", + "19.03.13" + ] + }, + { + "type": "string" + } + ] + } + } + }, + "save_cache": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/save_cache" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["paths", "key"], + "properties": { + "paths": { + "description": "List of directories which should be added to the cache", + "type": "array", + "items": { + "type": "string" + } + }, + "key": { + "description": "Unique identifier for this cache", + "type": "string" + }, + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Saving Cache')" + }, + "when": { + "description": "Specify when to enable or disable the step. Takes the following values: `always`, `on_success`, `on_fail` (default: `on_success`)", + "enum": ["always", "on_success", "on_fail"] + } + } + }, + "restore_cache": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/restore_cache" + } + ], + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["key"], + "properties": { + "key": { + "type": "string", + "description": "Single cache key to restore" + }, + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Restoring Cache')" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["keys"], + "properties": { + "name": { + "type": "string", + "description": "Title of the step to be shown in the CircleCI UI (default: 'Restoring Cache')" + }, + "keys": { + "description": "List of cache keys to lookup for a cache to restore. Only first existing key will be restored.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + }, + "deploy": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/deploy" + }, + { + "$ref": "#/definitions/builtinSteps/configuration/run" + } + ] + }, + "store_artifacts": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/store_artifacts" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["path"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Directory in the primary container to save as job artifacts", + "type": "string" + }, + "destination": { + "description": "Prefix added to the artifact paths in the artifacts API (default: the directory of the file specified in `path`)", + "type": "string" + } + } + }, + "store_test_results": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/store_test_results" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["path"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "path": { + "description": "Path (absolute, or relative to your `working_directory`) to directory containing subdirectories of JUnit XML or Cucumber JSON test metadata files", + "type": "string" + } + } + }, + "persist_to_workspace": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/persist_to_workspace" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["root", "paths"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "root": { + "description": "Either an absolute path or a path relative to `working_directory`", + "type": "string" + }, + "paths": { + "description": "Glob identifying file(s), or a non-glob path to a directory to add to the shared workspace. Interpreted as relative to the workspace root. Must not be the workspace root itself.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "attach_workspace": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/attach_workspace" + } + ], + "type": "object", + "additionalProperties": false, + "required": ["at"], + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "at": { + "description": "Directory to attach the workspace to", + "type": "string" + } + } + }, + "add_ssh_keys": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/add_ssh_keys" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "Title of the step to be shown in the CircleCI UI", + "type": "string" + }, + "fingerprints": { + "description": "Directory to attach the workspace to", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "when": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/when" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "condition": { + "$ref": "#/definitions/logical" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + }, + "required": ["condition", "steps"] + }, + "unless": { + "allOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/unless" + } + ], + "type": "object", + "additionalProperties": false, + "properties": { + "condition": { + "$ref": "#/definitions/logical" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + } + }, + "required": ["condition", "steps"] + } + } + }, + "step": { + "anyOf": [ + { + "$ref": "#/definitions/builtinSteps/documentation/checkout", + "enum": ["checkout"] + }, + { + "$ref": "#/definitions/builtinSteps/documentation/setup_remote_docker", + "enum": ["setup_remote_docker"] + }, + { + "$ref": "#/definitions/builtinSteps/documentation/add_ssh_keys", + "enum": ["add_ssh_keys"] + }, + { + "description": "https://circleci.com/docs/reusing-config#invoking-reusable-commands\n\nA custom command defined via the top level commands key", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+$" + }, + { + "description": "https://circleci.com/docs/using-orbs#commands\n\nA custom command defined via an orb.", + "type": "string", + "pattern": "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+$" + }, + { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "run": { + "$ref": "#/definitions/builtinSteps/configuration/run" + }, + "checkout": { + "$ref": "#/definitions/builtinSteps/configuration/checkout" + }, + "setup_remote_docker": { + "$ref": "#/definitions/builtinSteps/configuration/setup_remote_docker" + }, + "save_cache": { + "$ref": "#/definitions/builtinSteps/configuration/save_cache" + }, + "restore_cache": { + "$ref": "#/definitions/builtinSteps/configuration/restore_cache" + }, + "deploy": { + "$ref": "#/definitions/builtinSteps/configuration/deploy" + }, + "store_artifacts": { + "$ref": "#/definitions/builtinSteps/configuration/store_artifacts" + }, + "store_test_results": { + "$ref": "#/definitions/builtinSteps/configuration/store_test_results" + }, + "persist_to_workspace": { + "$ref": "#/definitions/builtinSteps/configuration/persist_to_workspace" + }, + "attach_workspace": { + "$ref": "#/definitions/builtinSteps/configuration/attach_workspace" + }, + "add_ssh_keys": { + "$ref": "#/definitions/builtinSteps/configuration/add_ssh_keys" + }, + "when": { + "$ref": "#/definitions/builtinSteps/configuration/when" + }, + "unless": { + "$ref": "#/definitions/builtinSteps/configuration/unless" + } + }, + "patternProperties": { + "^[a-z][a-z0-9_-]+$": { + "description": "https://circleci.com/docs/reusing-config#invoking-reusable-commands\n\nA custom command defined via the top level commands key" + }, + "^[a-z][a-z0-9_-]+/[a-z][a-z0-9_-]+$": { + "description": "https://circleci.com/docs/using-orbs#commands\n\nA custom command defined via an orb." + } + } + } + ] + }, + "jobRef": { + "description": "Run a job as part of this workflow", + "type": "object", + "additionalProperties": true, + "properties": { + "requires": { + "description": "Jobs are run in parallel by default, so you must explicitly require any dependencies by their job name.", + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "description": "The name key can be used to ensure build numbers are not appended when invoking the same job multiple times (e.g., sayhello-1, sayhello-2). The name assigned needs to be unique, otherwise numbers will still be appended to the job name", + "type": "string" + }, + "context": { + "description": "Either a single context name, or a list of contexts. The default name is `org-global`", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "default": "org-global" + }, + "type": { + "description": "A job may have a `type` of `approval` indicating it must be manually approved before downstream jobs may proceed.", + "enum": ["approval"] + }, + "filters": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "branches": { + "$ref": "#/definitions/filter" + }, + "tags": { + "$ref": "#/definitions/filter" + } + } + }, + "matrix": { + "description": "https://circleci.com/docs/configuration-reference#matrix-requires-version-21\n\nThe matrix stanza allows you to run a parameterized job multiple times with different arguments.", + "type": "object", + "additionalProperties": false, + "required": ["parameters"], + "properties": { + "parameters": { + "description": "A map of parameter names to every value the job should be called with", + "type": "object", + "additionalProperties": { + "type": "array" + } + }, + "exclude": { + "description": "A list of argument maps that should be excluded from the matrix", + "type": "array", + "items": { + "type": "object" + } + }, + "alias": { + "description": "An alias for the matrix, usable from another job's requires stanza. Defaults to the name of the job being executed", + "type": "string" + } + } + } + } + }, + "jobs": { + "description": "Jobs are collections of steps. All of the steps in the job are executed in a single unit, either within a fresh container or VM.", + "type": "object", + "additionalProperties": { + "type": "object", + "oneOf": [ + { + "$ref": "#/definitions/executorChoice" + }, + { + "type": "object", + "required": ["executor"], + "properties": { + "executor": { + "description": "The name of the executor to use (defined via the top level executors map).", + "type": "string" + } + } + }, + { + "type": "object", + "required": ["executor"], + "properties": { + "executor": { + "description": "Executor stanza to use for the job", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "The name of the executor to use (defined via the top level executors map).", + "type": "string" + } + } + } + } + } + ], + "required": ["steps"], + "properties": { + "shell": { + "description": "Shell to use for execution command in all steps. Can be overridden by shell in each step", + "type": "string" + }, + "steps": { + "description": "A list of steps to be performed", + "type": "array", + "items": { + "$ref": "#/definitions/step" + } + }, + "working_directory": { + "description": "In which directory to run the steps. (default: `~/project`. `project` is a literal string, not the name of the project.) You can also refer the directory with `$CIRCLE_WORKING_DIRECTORY` environment variable.", + "type": "string", + "default": "~/project" + }, + "parallelism": { + "description": "Number of parallel instances of this job to run (default: 1)", + "default": 1, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string", + "pattern": "^<<.+\\..+>>$" + } + ] + }, + "environment": { + "description": "A map of environment variable names and variables (NOTE: these will override any environment variables you set in the CircleCI web interface).", + "type": "object", + "additionalProperties": { + "type": ["string", "number"] + } + }, + "branches": { + "description": "A map defining rules for whitelisting/blacklisting execution of specific branches for a single job that is **not** in a workflow (default: all whitelisted). See Workflows for configuring branch execution for jobs in a workflow.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "properties": { + "version": { + "description": "The version field is intended to be used in order to issue warnings for deprecation or breaking changes.", + "default": 2.1, + "enum": [2, 2.1] + }, + "orbs": { + "$ref": "#/definitions/orbs" + }, + "commands": { + "$ref": "#/definitions/commands" + }, + "executors": { + "$ref": "#/definitions/executors" + }, + "jobs": { + "$ref": "#/definitions/jobs" + }, + "workflows": { + "description": "Used for orchestrating all jobs. Each workflow consists of the workflow name as a key and a map as a value", + "type": "object", + "properties": { + "version": { + "description": "The Workflows `version` field is used to issue warnings for deprecation or breaking changes during v2 Beta. It is deprecated as of CircleCI v2.1", + "enum": [2] + } + }, + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "triggers": { + "description": "Specifies which triggers will cause this workflow to be executed. Default behavior is to trigger the workflow when pushing to a branch.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "schedule": { + "description": "A workflow may have a schedule indicating it runs at a certain time, for example a nightly build that runs every day at 12am UTC:", + "type": "object", + "properties": { + "cron": { + "description": "See the [crontab man page](http://pubs.opengroup.org/onlinepubs/7908799/xcu/crontab.html)", + "type": "string" + }, + "filters": { + "description": "A map defining rules for execution on specific branches", + "type": "object", + "additionalProperties": false, + "properties": { + "branches": { + "$ref": "#/definitions/filter" + } + } + } + } + } + } + } + }, + "jobs": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/jobRef", + "type": "object" + } + } + ] + } + }, + "when": { + "$ref": "#/definitions/logical", + "description": "Specify when to run the workflow." + }, + "unless": { + "$ref": "#/definitions/logical", + "description": "Specify when *not* to run the workflow." + } + } + } + } + }, + "required": ["version"], + "title": "JSON schema for CircleCI configuration files", + "type": "object" +} diff --git a/ci/schemas/codecov.json b/ci/schemas/codecov.json new file mode 100644 index 000000000000..98decea44415 --- /dev/null +++ b/ci/schemas/codecov.json @@ -0,0 +1,620 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/codecov", + "definitions": { + "default": { + "$comment": "See https://docs.codecov.com/docs/commit-status#basic-configuration", + "properties": { + "target": { + "type": ["string", "number"], + "pattern": "^(([0-9]+\\.?[0-9]*|\\.[0-9]+)%?|auto)$", + "default": "auto" + }, + "threshold": { + "type": "string", + "default": "0%", + "pattern": "^([0-9]+\\.?[0-9]*|\\.[0-9]+)%?$" + }, + "base": { + "type": "string", + "default": "auto", + "deprecated": true + }, + "flags": { + "type": "array", + "default": [] + }, + "paths": { + "type": ["array", "string"], + "default": [] + }, + "branches": { + "type": "array", + "default": [] + }, + "if_not_found": { + "type": "string", + "enum": ["failure", "success"], + "default": "success" + }, + "informational": { + "type": "boolean", + "default": false + }, + "only_pulls": { + "type": "boolean", + "default": false + }, + "if_ci_failed": { + "type": "string", + "enum": ["error", "success"] + }, + "flag_coverage_not_uploaded_behavior": { + "type": "string", + "enum": ["include", "exclude", "pass"] + } + } + }, + "flag": { + "type": "object", + "properties": { + "joined": { + "type": "boolean" + }, + "required": { + "type": "boolean" + }, + "ignore": { + "type": "array", + "items": { + "type": "string" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "assume": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + } + } + }, + "layout": { + "anyOf": [ + {}, + { + "enum": [ + "header", + "footer", + "diff", + "file", + "files", + "flag", + "flags", + "reach", + "sunburst", + "uncovered" + ] + } + ] + }, + "notification": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "description": "Schema for codecov.yml files.", + "properties": { + "codecov": { + "description": "See https://docs.codecov.io/docs/codecov-yaml for details", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "bot": { + "description": "Team bot. See https://docs.codecov.io/docs/team-bot for details", + "type": "string" + }, + "branch": { + "type": "string" + }, + "ci": { + "description": "Detecting CI services. See https://docs.codecov.io/docs/detecting-ci-services for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "assume_all_flags": { + "type": "boolean" + }, + "strict_yaml_branch": { + "type": "string" + }, + "max_report_age": { + "type": ["string", "integer", "boolean"] + }, + "disable_default_path_fixes": { + "type": "boolean" + }, + "require_ci_to_pass": { + "type": "boolean" + }, + "allow_pseudo_compare": { + "type": "boolean" + }, + "archive": { + "type": "object", + "properties": { + "uploads": { + "type": "boolean" + } + } + }, + "notify": { + "type": "object", + "properties": { + "after_n_builds": { + "type": "integer" + }, + "countdown": { + "type": "integer" + }, + "delay": { + "type": "integer" + }, + "wait_for_ci": { + "type": "boolean" + } + } + }, + "ui": { + "type": "object", + "properties": { + "hide_density": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_complexity": { + "type": ["boolean", "array"], + "items": { + "type": "string" + } + }, + "hide_contextual": { + "type": "boolean" + }, + "hide_sunburst": { + "type": "boolean" + }, + "hide_search": { + "type": "boolean" + } + } + } + } + }, + "coverage": { + "description": "Coverage configuration. See https://docs.codecov.io/docs/coverage-configuration for details.", + "type": "object", + "properties": { + "precision": { + "type": "integer", + "minimum": 0, + "maximum": 5 + }, + "round": { + "enum": ["down", "up", "nearest"] + }, + "range": { + "type": "string" + }, + "notify": { + "description": "Notifications. See https://docs.codecov.io/docs/notifications for details.", + "type": "object", + "properties": { + "irc": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "channel": { + "type": "string" + }, + "password": { + "type": "string" + }, + "nickserv_password": { + "type": "string" + }, + "notice": { + "type": "boolean" + } + } + }, + "slack": { + "description": "Slack. See https://docs.codecov.io/docs/notifications#section-slack for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "attachments": { + "$ref": "#/definitions/layout" + } + } + }, + "gitter": { + "description": "Gitter. See https://docs.codecov.io/docs/notifications#section-gitter for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "hipchat": { + "description": "Hipchat. See https://docs.codecov.io/docs/notifications#section-hipchat for details.", + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "card": { + "type": "boolean" + }, + "notify": { + "type": "boolean" + } + } + }, + "webhook": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "email": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "branches": { + "type": "string" + }, + "threshold": { + "type": "string" + }, + "message": { + "type": "string" + }, + "flags": { + "type": "string" + }, + "base": { + "enum": ["parent", "pr", "auto"] + }, + "only_pulls": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "layout": { + "$ref": "#/definitions/layout" + }, + "+to": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "status": { + "description": "Commit status. See https://docs.codecov.io/docs/commit-status for details.", + "type": ["boolean", "object"], + "additionalProperties": false, + "properties": { + "default_rules": { + "type": "object" + }, + "project": { + "properties": { + "default": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "additionalProperties": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + }, + "patch": { + "anyOf": [ + { + "$ref": "#/definitions/default", + "type": "object" + }, + { + "type": "string", + "enum": ["off"] + }, + { + "type": "boolean" + } + ] + }, + "changes": { + "$ref": "#/definitions/default", + "type": ["object", "boolean"] + } + } + } + } + }, + "ignore": { + "description": "Ignoring paths. see https://docs.codecov.io/docs/ignoring-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "fixes": { + "description": "Fixing paths. See https://docs.codecov.io/docs/fixing-paths for details.", + "type": "array", + "items": { + "type": "string" + } + }, + "flags": { + "description": "Flags. See https://docs.codecov.io/docs/flags for details.", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/flag" + } + } + ] + }, + "comment": { + "description": "Pull request comments. See https://docs.codecov.io/docs/pull-request-comments for details.", + "oneOf": [ + { + "type": "object", + "properties": { + "layout": { + "$ref": "#/definitions/layout" + }, + "require_changes": { + "type": "boolean" + }, + "require_base": { + "type": "boolean" + }, + "require_head": { + "type": "boolean" + }, + "branches": { + "type": "array", + "items": { + "type": "string" + } + }, + "behavior": { + "enum": ["default", "once", "new", "spammy"] + }, + "flags": { + "type": "array", + "items": { + "$ref": "#/definitions/flag" + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + { + "const": false + } + ] + }, + "github_checks": { + "description": "GitHub Checks. See https://docs.codecov.com/docs/github-checks for details.", + "anyOf": [ + { + "type": "object", + "properties": { + "annotations": { + "type": "boolean" + } + } + }, + { "type": "boolean" }, + { "type": "string", "enum": ["off"] } + ] + } + }, + "title": "JSON schema for Codecov configuration files", + "type": "object" +} diff --git a/ci/schemas/conda-environment.json b/ci/schemas/conda-environment.json new file mode 100644 index 000000000000..fb1e821778c3 --- /dev/null +++ b/ci/schemas/conda-environment.json @@ -0,0 +1,53 @@ +{ + "title": "conda environment file", + "description": "Support for conda's environment.yml files (e.g. `conda env export > environment.yml`)", + "id": "https://raw.githubusercontent.com/Microsoft/vscode-python/main/schemas/conda-environment.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": { + "channel": { + "type": "string" + }, + "package": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "properties": { + "name": { + "type": "string" + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/channel" + } + }, + "dependencies": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/package" + }, + { + "type": "object", + "properties": { + "pip": { + "type": "array", + "items": { + "$ref": "#/definitions/package" + } + } + }, + "required": ["pip"] + } + ] + } + }, + "prefix": { + "$ref": "#/definitions/path" + } + } +} diff --git a/ci/schemas/github-funding.json b/ci/schemas/github-funding.json new file mode 100644 index 000000000000..d146d692c483 --- /dev/null +++ b/ci/schemas/github-funding.json @@ -0,0 +1,113 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-funding.json", + "$comment": "https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository", + "additionalProperties": false, + "definitions": { + "github_username": { + "type": "string", + "maxLength": 39, + "pattern": "^[a-zA-Z0-9](-?[a-zA-Z0-9])*$", + "examples": ["SampleUserName"] + }, + "nullable_string": { + "type": ["string", "null"] + } + }, + "description": "You can add a sponsor button in your repository to increase the visibility of funding options for your open source project.", + "properties": { + "community_bridge": { + "$ref": "#/definitions/nullable_string", + "title": "CommunityBridge", + "description": "Project name on CommunityBridge.", + "minLength": 1 + }, + "github": { + "title": "GitHub Sponsors", + "description": "Username or usernames on GitHub.", + "oneOf": [ + { + "$ref": "#/definitions/github_username" + }, + { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/github_username" + } + } + ] + }, + "issuehunt": { + "$ref": "#/definitions/nullable_string", + "title": "IssueHunt", + "description": "Username on IssueHunt.", + "minLength": 1 + }, + "ko_fi": { + "$ref": "#/definitions/nullable_string", + "title": "Ko-fi", + "description": "Username on Ko-fi.", + "minLength": 1 + }, + "liberapay": { + "$ref": "#/definitions/nullable_string", + "title": "Liberapay", + "description": "Username on Liberapay.", + "minLength": 1 + }, + "open_collective": { + "$ref": "#/definitions/nullable_string", + "title": "Open Collective", + "description": "Username on Open Collective.", + "minLength": 1 + }, + "otechie": { + "$ref": "#/definitions/nullable_string", + "title": "Otechie", + "description": "Username on Otechie.", + "minLength": 1 + }, + "patreon": { + "$ref": "#/definitions/nullable_string", + "title": "Patreon", + "description": "Username on Pateron.", + "minLength": 1, + "maxLength": 100 + }, + "tidelift": { + "$ref": "#/definitions/nullable_string", + "title": "Tidelift", + "description": "Platform and package on Tidelift.", + "pattern": "^(npm|pypi|rubygems|maven|packagist|nuget)/.+$" + }, + "lfx_crowdfunding": { + "$ref": "#/definitions/nullable_string", + "title": "LFX Crowdfunding", + "description": "Project name on LFX Crowdfunding.", + "minLength": 1 + }, + "polar": { + "$ref": "#/definitions/github_username", + "title": "Polar", + "description": "Username on Polar.", + "minLength": 1 + }, + "custom": { + "title": "Custom URL", + "description": "Link or links where funding is accepted on external locations.", + "type": ["string", "array", "null"], + "format": "uri-reference", + "items": { + "title": "Link", + "description": "Link to an external location.", + "type": "string", + "format": "uri-reference" + }, + "uniqueItems": true + } + }, + "title": "GitHub Funding", + "type": "object" +} diff --git a/ci/schemas/github-issue-config.json b/ci/schemas/github-issue-config.json new file mode 100644 index 000000000000..b46556bb04a5 --- /dev/null +++ b/ci/schemas/github-issue-config.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-issue-config.json", + "$comment": "https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "properties": { + "blank_issues_enabled": { + "description": "Specify whether allow blank issue creation\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "boolean" + }, + "contact_links": { + "title": "contact links", + "description": "Contact links\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["name", "url", "about"], + "properties": { + "name": { + "description": "A link title\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "minLength": 1, + "examples": ["Sample name"] + }, + "url": { + "description": "A link URL\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "pattern": "^https?://", + "examples": ["https://sample/url"] + }, + "about": { + "description": "A link description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser", + "type": "string", + "minLength": 1, + "examples": ["Sample description"] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false, + "title": "GitHub issue template chooser config file schema", + "type": "object" +} diff --git a/ci/schemas/github-issue-forms.json b/ci/schemas/github-issue-forms.json new file mode 100644 index 000000000000..c928818dfdd1 --- /dev/null +++ b/ci/schemas/github-issue-forms.json @@ -0,0 +1,1295 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/github-issue-forms.json", + "$comment": "https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms", + "additionalProperties": false, + "definitions": { + "type": { + "description": "A form item type\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys", + "type": "string", + "enum": ["checkboxes", "dropdown", "input", "markdown", "textarea"] + }, + "id": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$", + "examples": ["SampleId"] + }, + "validations": { + "title": "validation options", + "type": "object", + "properties": { + "required": { + "description": "Specify whether require a form item", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "assignee": { + "type": "string", + "maxLength": 39, + "pattern": "^[a-zA-Z0-9](-?[a-zA-Z0-9])*$", + "examples": ["SampleAssignee"] + }, + "label": { + "type": "string", + "minLength": 1, + "examples": ["Sample label"] + }, + "description": { + "type": "string", + "default": "", + "examples": ["Sample description"] + }, + "placeholder": { + "type": "string", + "default": "", + "examples": ["Sample placeholder"] + }, + "value": { + "type": "string", + "minLength": 1, + "examples": ["Sample value"] + }, + "form_item": { + "title": "form item", + "description": "A form item\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#about-githubs-form-schema", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "$ref": "#/definitions/type" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "markdown" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "markdown", + "description": "Markdown\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#markdown", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "attributes": { + "title": "markdown attributes", + "description": "Markdown attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes", + "type": "object", + "required": ["value"], + "properties": { + "value": { + "description": "A markdown code\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes", + "type": "string", + "minLength": 1, + "examples": ["Sample code"] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "textarea" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "textarea", + "description": "Textarea\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#textarea", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "A textarea id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "textarea attributes", + "description": "Textarea attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short textarea description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long textarea description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "placeholder": { + "$ref": "#/definitions/placeholder", + "description": "A textarea placeholder\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "value": { + "$ref": "#/definitions/value", + "description": "A textarea value\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1" + }, + "render": { + "description": "A textarea syntax highlighting mode\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-1", + "type": "string", + "enum": [ + "1C Enterprise", + "4D", + "ABAP CDS", + "ABAP", + "ABNF", + "AFDKO", + "AGS Script", + "AIDL", + "AL", + "AMPL", + "ANTLR", + "API Blueprint", + "APL", + "ASL", + "ASN.1", + "ASP.NET", + "ATS", + "ActionScript", + "Ada", + "Alloy", + "Alpine Abuild", + "Altium Designer", + "AngelScript", + "Ant Build System", + "ApacheConf", + "Apex", + "Apollo Guidance Computer", + "AppleScript", + "Arc", + "AsciiDoc", + "AspectJ", + "Assembly", + "Astro", + "Asymptote", + "Augeas", + "AutoHotkey", + "AutoIt", + "AutoIt3", + "AutoItScript", + "Avro IDL", + "Awk", + "BASIC", + "Ballerina", + "Batchfile", + "Beef", + "Befunge", + "BibTeX", + "Bicep", + "Bison", + "BitBake", + "Blade", + "BlitzBasic", + "BlitzMax", + "Boo", + "Boogie", + "Brainfuck", + "Brightscript", + "Browserslist", + "C", + "C#", + "C++", + "C-ObjDump", + "C2hs Haskell", + "CIL", + "CLIPS", + "CMake", + "COBOL", + "CODEOWNERS", + "COLLADA", + "CSON", + "CSS", + "CSV", + "CUE", + "CWeb", + "Cabal Config", + "Cabal", + "Cap'n Proto", + "Carto", + "CartoCSS", + "Ceylon", + "Chapel", + "Charity", + "ChucK", + "Cirru", + "Clarion", + "Classic ASP", + "Clean", + "Click", + "Clojure", + "Closure Templates", + "Cloud Firestore Security Rules", + "CoNLL", + "CoNLL-U", + "CoNLL-X", + "ColdFusion CFC", + "ColdFusion", + "Common Lisp", + "Common Workflow Language", + "Component Pascal", + "Containerfile", + "Cool", + "Coq", + "Cpp-ObjDump", + "Crystal", + "Csound Document", + "Csound Score", + "Csound", + "Cuda", + "Cue Sheet", + "Cycript", + "Cython", + "D-ObjDump", + "DIGITAL Command Language", + "DM", + "DTrace", + "Dafny", + "Darcs Patch", + "Dart", + "DataWeave", + "Dhall", + "Diff", + "Dlang", + "Dockerfile", + "Dogescript", + "Dylan", + "E", + "E-mail", + "EBNF", + "ECL", + "ECLiPSe", + "EJS", + "EQ", + "Eagle", + "Earthly", + "Easybuild", + "Ecere Projects", + "EditorConfig", + "Eiffel", + "Elixir", + "Elm", + "Emacs Lisp", + "EmberScript", + "Erlang", + "F#", + "F*", + "FIGfont", + "FIGlet Font", + "FLUX", + "Factor", + "Fancy", + "Fantom", + "Faust", + "Fennel", + "Filebench WML", + "Filterscript", + "Fluent", + "Formatted", + "Forth", + "Fortran Free Form", + "Fortran", + "FreeBasic", + "Frege", + "Futhark", + "G-code", + "GAML", + "GAMS", + "GAP", + "GCC Machine Description", + "GDB", + "GDScript", + "GEDCOM", + "GLSL", + "GN", + "Game Maker Language", + "Gemfile.lock", + "Genie", + "Genshi", + "Gentoo Eclass", + "Gerber Image", + "Gettext Catalog", + "Gherkin", + "Git Config", + "Glyph Bitmap Distribution Format", + "Glyph", + "Gnuplot", + "Go Checksums", + "Go Module", + "Go", + "Golo", + "Gosu", + "Grace", + "Gradle", + "Grammatical Framework", + "Graph Modeling Language", + "GraphQL", + "Graphviz (DOT)", + "Groovy Server Pages", + "Groovy", + "HAProxy", + "HCL", + "HTML", + "HTML+ECR", + "HTML+EEX", + "HTML+ERB", + "HTML+PHP", + "HTML+Razor", + "HTTP", + "HXML", + "Hack", + "Haml", + "Handlebars", + "Harbour", + "HashiCorp Configuration Language", + "Haskell", + "Haxe", + "HiveQL", + "HolyC", + "Hy", + "IDL", + "IGOR Pro", + "IPython Notebook", + "Idris", + "Ignore List", + "ImageJ Macro", + "Inform 7", + "Io", + "Ioke", + "Isabelle ROOT", + "Isabelle", + "J", + "JAR Manifest", + "JFlex", + "JSON with Comments", + "JSON", + "JSON5", + "JSONLD", + "JSONiq", + "Jasmin", + "Java Properties", + "Java Server Pages", + "Java", + "JavaScript", + "JavaScript+ERB", + "Jest Snapshot", + "Jinja", + "Jison Lex", + "Jison", + "Jolie", + "Jsonnet", + "Julia", + "Jupyter Notebook", + "Kaitai Struct", + "KakouneScript", + "KiCad Layout", + "KiCad Legacy Layout", + "KiCad Schematic", + "Kit", + "Kotlin", + "Kusto", + "LFE", + "LLVM", + "LOLCODE", + "LSL", + "LTspice Symbol", + "LabVIEW", + "Lark", + "Lasso", + "Lean", + "Less", + "Lex", + "LilyPond", + "Limbo", + "Linker Script", + "Linux Kernel Module", + "Liquid", + "Literate Agda", + "Literate CoffeeScript", + "Literate Haskell", + "LiveScript", + "Logos", + "Logtalk", + "LookML", + "LoomScript", + "Lua", + "M", + "M4", + "M4Sugar", + "MATLAB", + "MAXScript", + "MLIR", + "MQL4", + "MQL5", + "MTML", + "MUF", + "Macaulay2", + "Makefile", + "Mako", + "Markdown", + "Marko", + "Mathematica", + "Max", + "Mercury", + "Meson", + "Metal", + "Microsoft Developer Studio Project", + "Microsoft Visual Studio Solution", + "MiniD", + "Mirah", + "Modelica", + "Modula-2", + "Modula-3", + "Module Management System", + "Monkey", + "Moocode", + "MoonScript", + "Motoko", + "Motorola 68K Assembly", + "Muse", + "Myghty", + "NASL", + "NCL", + "NEON", + "NPM Config", + "NSIS", + "NWScript", + "Nearley", + "Nemerle", + "NeoSnippet", + "NetLinx", + "NetLinx+ERB", + "NetLogo", + "NewLisp", + "Nextflow", + "Nginx", + "Ninja", + "Nit", + "Nix", + "NumPy", + "Nunjucks", + "ObjDump", + "Object Data Instance Notation", + "ObjectScript", + "Objective-C", + "Objective-C++", + "Objective-J", + "Odin", + "Omgrofl", + "Opa", + "Opal", + "Open Policy Agent", + "OpenCL", + "OpenEdge ABL", + "OpenQASM", + "OpenRC runscript", + "OpenSCAD", + "OpenStep Property List", + "OpenType Feature File", + "Org", + "Ox", + "Oxygene", + "Oz", + "P4", + "PEG.js", + "PHP", + "PLpgSQL", + "POV-Ray SDL", + "Pan", + "Papyrus", + "Parrot Assembly", + "Parrot Internal Representation", + "Parrot", + "Pascal", + "Pawn", + "Pep8", + "Perl", + "Pickle", + "PicoLisp", + "PigLatin", + "Pike", + "PlantUML", + "Pod 6", + "Pod", + "PogoScript", + "Pony", + "PostCSS", + "PostScript", + "PowerShell", + "Prisma", + "Processing", + "Proguard", + "Prolog", + "Promela", + "Propeller Spin", + "Protocol Buffer", + "Protocol Buffers", + "Public Key", + "Pug", + "Puppet", + "Pure Data", + "PureBasic", + "PureScript", + "Python", + "Q#", + "QMake", + "Qt Script", + "Quake", + "R", + "RAML", + "RDoc", + "REALbasic", + "REXX", + "RMarkdown", + "RPC", + "RPM Spec", + "Racket", + "Ragel", + "Raw token data", + "ReScript", + "Readline Config", + "Reason", + "Rebol", + "Record Jar", + "Red", + "Redirect Rules", + "Regular Expression", + "RenderScript", + "Rich Text Format", + "Ring", + "Riot", + "RobotFramework", + "Roff", + "Rouge", + "Rscript", + "Ruby", + "Rust", + "SAS", + "SCSS", + "SELinux Kernel Policy Language", + "SELinux Policy", + "SMT", + "SPARQL", + "SQF", + "SQL", + "SQLPL", + "SRecode Template", + "SSH Config", + "STON", + "SVG", + "SWIG", + "Sage", + "SaltStack", + "Sass", + "Scala", + "Scaml", + "Scheme", + "Scilab", + "Self", + "ShaderLab", + "Shell", + "ShellCheck Config", + "Sieve", + "Singularity", + "Slash", + "Slice", + "Slim", + "SmPL", + "Smalltalk", + "SnipMate", + "Solidity", + "Soong", + "SourcePawn", + "Spline Font Database", + "Squirrel", + "Stan", + "Standard ML", + "Starlark", + "StringTemplate", + "Stylus", + "SubRip Text", + "SugarSS", + "SuperCollider", + "Svelte", + "Swift", + "SystemVerilog", + "TI Program", + "TLA", + "TOML", + "TSQL", + "TSV", + "TSX", + "TXL", + "Tcl", + "Tcsh", + "TeX", + "Tea", + "Terra", + "Texinfo", + "Text", + "TextMate Properties", + "Textile", + "Thrift", + "Turing", + "Turtle", + "Twig", + "Type Language", + "TypeScript", + "UltiSnip", + "UltiSnips", + "Unified Parallel C", + "Unity3D Asset", + "Unix Assembly", + "Uno", + "UnrealScript", + "Ur", + "Ur/Web", + "UrWeb", + "V", + "VBA", + "VCL", + "VHDL", + "Vala", + "Valve Data Format", + "Verilog", + "Vim Help File", + "Vim Script", + "Vim Snippet", + "Visual Basic .NET", + "Vue", + "Wavefront Material", + "Wavefront Object", + "Web Ontology Language", + "WebAssembly", + "WebVTT", + "Wget Config", + "Wikitext", + "Windows Registry Entries", + "Wollok", + "World of Warcraft Addon Data", + "X BitMap", + "X Font Directory Index", + "X PixMap", + "X10", + "XC", + "XCompose", + "XML Property List", + "XML", + "XPages", + "XProc", + "XQuery", + "XS", + "XSLT", + "Xojo", + "Xonsh", + "Xtend", + "YAML", + "YANG", + "YARA", + "YASnippet", + "Yacc", + "ZAP", + "ZIL", + "Zeek", + "ZenScript", + "Zephir", + "Zig", + "Zimpl", + "abl", + "abuild", + "acfm", + "aconf", + "actionscript 3", + "actionscript3", + "ada2005", + "ada95", + "adobe composite font metrics", + "adobe multiple font metrics", + "advpl", + "ags", + "ahk", + "altium", + "amfm", + "amusewiki", + "apache", + "apkbuild", + "arexx", + "as3", + "asm", + "asp", + "aspx", + "aspx-vb", + "ats2", + "au3", + "autoconf", + "b3d", + "bash session", + "bash", + "bat", + "batch", + "bazel", + "blitz3d", + "blitzplus", + "bmax", + "bplus", + "bro", + "bsdmake", + "byond", + "bzl", + "c++-objdump", + "c2hs", + "cURL Config", + "cake", + "cakescript", + "cfc", + "cfm", + "cfml", + "chpl", + "clipper", + "coccinelle", + "coffee", + "coffee-script", + "coldfusion html", + "console", + "cperl", + "cpp", + "csharp", + "csound-csd", + "csound-orc", + "csound-sco", + "cucumber", + "curlrc", + "cwl", + "dcl", + "delphi", + "desktop", + "dircolors", + "django", + "dosbatch", + "dosini", + "dpatch", + "dtrace-script", + "eC", + "ecr", + "editor-config", + "edn", + "eeschema schematic", + "eex", + "elisp", + "emacs muse", + "emacs", + "email", + "eml", + "erb", + "fb", + "fish", + "flex", + "foxpro", + "fsharp", + "fstar", + "ftl", + "fundamental", + "gf", + "git-ignore", + "gitattributes", + "gitconfig", + "gitignore", + "gitmodules", + "go mod", + "go sum", + "go.mod", + "go.sum", + "golang", + "groff", + "gsp", + "hbs", + "heex", + "help", + "html+django", + "html+jinja", + "html+ruby", + "htmlbars", + "htmldjango", + "hylang", + "i7", + "ignore", + "igor", + "igorpro", + "ijm", + "inc", + "inform7", + "inputrc", + "irc logs", + "irc", + "java server page", + "jq", + "jruby", + "js", + "jsonc", + "jsp", + "kak", + "kakscript", + "keyvalues", + "ksy", + "lassoscript", + "latex", + "leex", + "lhaskell", + "lhs", + "lisp", + "litcoffee", + "live-script", + "ls", + "m2", + "m68k", + "mIRC Script", + "macruby", + "mail", + "make", + "man page", + "man", + "man-page", + "manpage", + "markojs", + "max/msp", + "maxmsp", + "mbox", + "mcfunction", + "mdoc", + "mediawiki", + "mf", + "mma", + "mumps", + "mupad", + "nanorc", + "nasm", + "ne-on", + "nesC", + "nette object notation", + "nginx configuration file", + "nixos", + "njk", + "node", + "npmrc", + "nroff", + "nush", + "nvim", + "obj-c", + "obj-c++", + "obj-j", + "objc", + "objc++", + "objectivec", + "objectivec++", + "objectivej", + "objectpascal", + "objj", + "octave", + "odin-lang", + "odinlang", + "oncrpc", + "ooc", + "openedge", + "openrc", + "osascript", + "pandoc", + "pasm", + "pcbnew", + "perl-6", + "perl6", + "pir", + "plain text", + "posh", + "postscr", + "pot", + "pov-ray", + "povray", + "progress", + "protobuf", + "pwsh", + "pycon", + "pyrex", + "python3", + "q", + "ql", + "qsharp", + "ragel-rb", + "ragel-ruby", + "rake", + "raw", + "razor", + "rb", + "rbx", + "reStructuredText", + "readline", + "red/system", + "redirects", + "regex", + "regexp", + "renpy", + "rhtml", + "robots txt", + "robots", + "robots.txt", + "rpcgen", + "rs", + "rs-274x", + "rss", + "rst", + "rusthon", + "salt", + "saltstate", + "sed", + "sepolicy", + "sh", + "shell-script", + "shellcheckrc", + "sml", + "snippet", + "sourcemod", + "soy", + "specfile", + "splus", + "squeak", + "terraform", + "tl", + "tm-properties", + "troff", + "ts", + "udiff", + "vb .net", + "vb.net", + "vb6", + "vbnet", + "vdf", + "vim", + "vimhelp", + "viml", + "visual basic 6", + "visual basic for applications", + "visual basic", + "vlang", + "wasm", + "wast", + "wdl", + "wgetrc", + "wiki", + "winbatch", + "wisp", + "wl", + "wolfram lang", + "wolfram language", + "wolfram", + "wsdl", + "xBase", + "xbm", + "xdr", + "xhtml", + "xml+genshi", + "xml+kid", + "xpm", + "xsd", + "xsl", + "xten", + "yas", + "yml", + "zsh" + ] + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "textarea validations", + "description": "Textarea validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "input" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "input", + "description": "Input\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#input", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "An input id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "input attributes", + "description": "Input attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short input description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long input description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "placeholder": { + "$ref": "#/definitions/placeholder", + "description": "An input placeholder\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + }, + "value": { + "$ref": "#/definitions/value", + "description": "An input value\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-2" + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "input validations", + "description": "Input validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations-1" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "dropdown" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "dropdown", + "description": "dropdown\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#dropdown", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "A dropdown id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "dropdown attributes", + "description": "Dropdown attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "object", + "required": ["label", "options"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short dropdown description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long dropdown description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3" + }, + "multiple": { + "description": "Specify whether allow a multiple choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "boolean", + "default": false + }, + "options": { + "description": "Dropdown choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "examples": ["Sample choice"] + } + }, + "default": { + "description": "Index of the default option\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-3", + "type": "integer", + "examples": [0] + } + }, + "additionalProperties": false + }, + "validations": { + "$ref": "#/definitions/validations", + "title": "dropdown validations", + "description": "Dropdown validations\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#validations-2" + } + }, + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "const": "checkboxes" + } + } + }, + "then": { + "$comment": "For `additionalProperties` to work `type` must also be present here.", + "title": "checkboxes", + "description": "Checkboxes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#checkboxes", + "type": "object", + "required": ["type", "attributes"], + "properties": { + "type": { + "$ref": "#/definitions/type" + }, + "id": { + "$ref": "#/definitions/id", + "description": "Checkbox list id\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#keys" + }, + "attributes": { + "title": "checkbox list attributes", + "description": "Checkbox list attributes\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "object", + "required": ["label", "options"], + "properties": { + "label": { + "$ref": "#/definitions/label", + "description": "A short checkbox list description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4" + }, + "description": { + "$ref": "#/definitions/description", + "description": "A long checkbox list description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4" + }, + "options": { + "description": "Checkbox list choices\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "array", + "minItems": 1, + "items": { + "title": "checkbox list choice", + "description": "Checkbox list choice\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "object", + "required": ["label"], + "properties": { + "label": { + "description": "A short checkbox list choice description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "string", + "minLength": 1, + "examples": ["Sample label"] + }, + "required": { + "description": "Specify whether a choice is required\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-4", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + ] + } + }, + "properties": { + "name": { + "description": "An issue template name\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample name"] + }, + "description": { + "description": "An issue template description\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample description"] + }, + "body": { + "description": "An issue template body\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_item" + } + }, + "assignees": { + "description": "An issue template assignees\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "oneOf": [ + { + "$ref": "#/definitions/assignee" + }, + { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/assignee" + } + } + ] + }, + "labels": { + "description": "An issue template labels\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "examples": [ + "Sample label", + "bug", + "documentation", + "duplicate", + "enhancement", + "good first issue", + "help wanted", + "invalid", + "question", + "wontfix" + ] + } + }, + "title": { + "description": "An issue template title\nhttps://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax", + "type": "string", + "minLength": 1, + "examples": ["Sample title", "Bug: ", "Feature: "] + } + }, + "required": ["name", "description", "body"], + "title": "GitHub issue forms config file schema", + "type": "object" +} diff --git a/ci/schemas/pull-request-labeler-5.json b/ci/schemas/pull-request-labeler-5.json new file mode 100644 index 000000000000..22ad7955814f --- /dev/null +++ b/ci/schemas/pull-request-labeler-5.json @@ -0,0 +1,95 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/pull-request-labeler-5.json", + "$comment": "https://github.com/actions/labeler", + "$defs": { + "stringOrStringArray": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "match": { + "title": "Match", + "type": "object", + "properties": { + "changed-files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "any-glob-to-any-file": { "$ref": "#/$defs/stringOrStringArray" }, + "any-glob-to-all-files": { + "$ref": "#/$defs/stringOrStringArray" + }, + "all-globs-to-any-file": { + "$ref": "#/$defs/stringOrStringArray" + }, + "all-globs-to-all-files": { + "$ref": "#/$defs/stringOrStringArray" + } + }, + "oneOf": [ + { "required": ["any-glob-to-any-file"] }, + { "required": ["any-glob-to-all-files"] }, + { "required": ["all-globs-to-any-file"] }, + { "required": ["all-globs-to-all-files"] } + ], + "additionalProperties": false + } + }, + "base-branch": { "$ref": "#/$defs/stringOrStringArray" }, + "head-branch": { "$ref": "#/$defs/stringOrStringArray" } + }, + "oneOf": [ + { "required": ["changed-files"] }, + { "required": ["base-branch"] }, + { "required": ["head-branch"] } + ], + "additionalProperties": false + } + }, + "additionalProperties": { + "title": "Label", + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "all": { + "title": "All", + "type": "array", + "items": { "$ref": "#/$defs/match" } + } + }, + "additionalProperties": false, + "required": ["all"] + }, + { + "type": "object", + "properties": { + "any": { + "title": "Any", + "type": "array", + "items": { "$ref": "#/$defs/match" } + } + }, + "additionalProperties": false, + "required": ["any"] + }, + { "$ref": "#/$defs/match" } + ] + } + }, + "description": "A GitHub Action for automatically labelling pull requests.", + "title": "Pull Request Labeler", + "type": "object" +} diff --git a/ci/schemas/vendor_schemas.py b/ci/schemas/vendor_schemas.py new file mode 100644 index 000000000000..a40e262e69f7 --- /dev/null +++ b/ci/schemas/vendor_schemas.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +Download YAML Schemas for linting and validation. + +Since pre-commit CI doesn't have Internet access, we need to bundle these files +in the repo. +""" + +import os +import pathlib +import urllib.request + + +HERE = pathlib.Path(__file__).parent +SCHEMAS = [ + 'https://json.schemastore.org/appveyor.json', + 'https://json.schemastore.org/circleciconfig.json', + 'https://json.schemastore.org/github-funding.json', + 'https://json.schemastore.org/github-issue-config.json', + 'https://json.schemastore.org/github-issue-forms.json', + 'https://json.schemastore.org/codecov.json', + 'https://json.schemastore.org/pull-request-labeler-5.json', + 'https://github.com/microsoft/vscode-python/raw/' + 'main/schemas/conda-environment.json', +] + + +def print_progress(block_count, block_size, total_size): + size = block_count * block_size + if total_size != -1: + size = min(size, total_size) + width = 50 + percent = size / total_size * 100 + filled = int(percent // (100 // width)) + percent_str = '\N{Full Block}' * filled + '\N{Light Shade}' * (width - filled) + print(f'{percent_str} {size:6d} / {total_size:6d}', end='\r') + + +# First clean up existing files. +for json in HERE.glob('*.json'): + os.remove(json) + +for schema in SCHEMAS: + path = HERE / schema.rsplit('/', 1)[-1] + print(f'Downloading {schema} to {path}') + urllib.request.urlretrieve(schema, filename=path, reporthook=print_progress) + print() + # This seems weird, but it normalizes line endings to the current platform, + # so that Git doesn't complain about it. + path.write_text(path.read_text()) diff --git a/ci/travis/build_children.sh b/ci/travis/build_children.sh deleted file mode 100755 index 5f26c4890cc1..000000000000 --- a/ci/travis/build_children.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Modified from: -# https://github.com/ajdm/travis-build-children/blob/master/build_children.sh - -# Get last child project build number from branch named "latest" -BUILD_NUM=$(curl -s 'https://api.travis-ci.org/repos/MacPython/matplotlib-wheels/branches/latest' | grep -o '^{"branch":{"id":[0-9]*,' | grep -o '[0-9]' | tr -d '\n') - -# Restart last child project build -curl -X POST https://api.travis-ci.org/builds/$BUILD_NUM/restart --header "Authorization: token "$AUTH_TOKEN diff --git a/ci/travis/matplotlibDeployKey.enc b/ci/travis/matplotlibDeployKey.enc deleted file mode 100644 index f73fb807cdf5..000000000000 Binary files a/ci/travis/matplotlibDeployKey.enc and /dev/null differ diff --git a/ci/travis/setup.cfg b/ci/travis/setup.cfg deleted file mode 100644 index 61cdc102a0f8..000000000000 --- a/ci/travis/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[test] -local_freetype=True \ No newline at end of file diff --git a/ci/travis/travis_after_all.py b/ci/travis/travis_after_all.py deleted file mode 100644 index 4e35b39ab9a5..000000000000 --- a/ci/travis/travis_after_all.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copied from: -# https://github.com/dmakhno/travis_after_all -# commit b7172bca9 -import os -import json -import time -import logging - -try: - import urllib.request as urllib2 -except ImportError: - import urllib2 - -log = logging.getLogger("travis.leader") -log.addHandler(logging.StreamHandler()) -log.setLevel(logging.INFO) - -TRAVIS_JOB_NUMBER = 'TRAVIS_JOB_NUMBER' -TRAVIS_BUILD_ID = 'TRAVIS_BUILD_ID' -POLLING_INTERVAL = 'LEADER_POLLING_INTERVAL' - -build_id = os.getenv(TRAVIS_BUILD_ID) -polling_interval = int(os.getenv(POLLING_INTERVAL, '5')) - -#assume, first job is the leader -is_leader = lambda job_number: job_number.endswith('.1') - -if not os.getenv(TRAVIS_JOB_NUMBER): - # seems even for builds with only one job, this won't get here - log.fatal("Don't use defining leader for build without matrix") - exit(1) -elif is_leader(os.getenv(TRAVIS_JOB_NUMBER)): - log.info("This is a leader") -else: - #since python is subprocess, env variables are exported back via file - with open(".to_export_back", "w") as export_var: - export_var.write("BUILD_MINION=YES") - log.info("This is a minion") - exit(0) - - -class MatrixElement(object): - def __init__(self, json_raw): - self.is_finished = json_raw['finished_at'] is not None - self.is_succeeded = json_raw['result'] == 0 - self.number = json_raw['number'] - self.is_leader = is_leader(self.number) - - -def matrix_snapshot(): - """ - :return: Matrix List - """ - response = urllib2.build_opener().open("https://api.travis-ci.org/builds/{0}".format(build_id)).read() - raw_json = json.loads(response) - matrix_without_leader = [MatrixElement(element) for element in raw_json["matrix"]] - return matrix_without_leader - - -def wait_others_to_finish(): - def others_finished(): - """ - Dumps others to finish - Leader cannot finish, it is working now - :return: tuple(True or False, List of not finished jobs) - """ - snapshot = matrix_snapshot() - finished = [el.is_finished for el in snapshot if not el.is_leader] - return reduce(lambda a, b: a and b, finished), [el.number for el in snapshot if - not el.is_leader and not el.is_finished] - - while True: - finished, waiting_list = others_finished() - if finished: break - log.info("Leader waits for minions {0}...".format(waiting_list)) # just in case do not get "silence timeout" - time.sleep(polling_interval) - - -try: - wait_others_to_finish() - - final_snapshot = matrix_snapshot() - log.info("Final Results: {0}".format([(e.number, e.is_succeeded) for e in final_snapshot])) - - BUILD_AGGREGATE_STATUS = 'BUILD_AGGREGATE_STATUS' - others_snapshot = [el for el in final_snapshot if not el.is_leader] - if reduce(lambda a, b: a and b, [e.is_succeeded for e in others_snapshot]): - os.environ[BUILD_AGGREGATE_STATUS] = "others_succeeded" - elif reduce(lambda a, b: a and b, [not e.is_succeeded for e in others_snapshot]): - log.error("Others Failed") - os.environ[BUILD_AGGREGATE_STATUS] = "others_failed" - else: - log.warn("Others Unknown") - os.environ[BUILD_AGGREGATE_STATUS] = "unknown" - #since python is subprocess, env variables are exported back via file - with open(".to_export_back", "w") as export_var: - export_var.write("BUILD_LEADER=YES {0}={1}".format(BUILD_AGGREGATE_STATUS, os.environ[BUILD_AGGREGATE_STATUS])) - -except Exception as e: - log.fatal(e) diff --git a/ci/travis/travis_tools.sh b/ci/travis/travis_tools.sh deleted file mode 100644 index 0710891430d1..000000000000 --- a/ci/travis/travis_tools.sh +++ /dev/null @@ -1,26 +0,0 @@ -# Tools for working with travis-ci -export WHEELHOST="travis-wheels.scikit-image.org" -export WHEELHOUSE="http://${WHEELHOST}/" - -retry () { - # https://gist.github.com/fungusakafungus/1026804 - local retry_max=5 - local count=$retry_max - while [ $count -gt 0 ]; do - "$@" && break - count=$(($count - 1)) - sleep 1 - done - - [ $count -eq 0 ] && { - echo "Retry failed [$retry_max]: $@" >&2 - return 1 - } - return 0 -} - - -wheelhouse_pip_install() { - # Install pip requirements via travis wheelhouse - retry pip install --timeout=60 --no-index --trusted-host $WHEELHOST --find-links $WHEELHOUSE $@ -} diff --git a/conftest.py b/conftest.py deleted file mode 100644 index 2a1dbe0a4ece..000000000000 --- a/conftest.py +++ /dev/null @@ -1,103 +0,0 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import inspect -import os -import pytest -import unittest - -import matplotlib -matplotlib.use('agg') - -from matplotlib import default_test_modules - - -IGNORED_TESTS = { - 'matplotlib': [], -} - - -def blacklist_check(path): - """Check if test is blacklisted and should be ignored""" - head, tests_dir = os.path.split(path.dirname) - if tests_dir != 'tests': - return True - head, top_module = os.path.split(head) - return path.purebasename in IGNORED_TESTS.get(top_module, []) - - -def whitelist_check(path): - """Check if test is not whitelisted and should be ignored""" - left = path.dirname - last_left = None - module_path = path.purebasename - while len(left) and left != last_left: - last_left = left - left, tail = os.path.split(left) - module_path = '.'.join([tail, module_path]) - if module_path in default_test_modules: - return False - return True - - -COLLECT_FILTERS = { - 'none': lambda _: False, - 'blacklist': blacklist_check, - 'whitelist': whitelist_check, -} - - -def is_nose_class(cls): - """Check if supplied class looks like Nose testcase""" - return any(name in ['setUp', 'tearDown'] - for name, _ in inspect.getmembers(cls)) - - -def pytest_addoption(parser): - group = parser.getgroup("matplotlib", "matplotlib custom options") - - group.addoption('--collect-filter', action='store', - choices=COLLECT_FILTERS, default='blacklist', - help='filter tests during collection phase') - - group.addoption('--no-pep8', action='store_true', - help='skip PEP8 compliance tests') - - -def pytest_configure(config): - matplotlib._called_from_pytest = True - matplotlib._init_tests() - - if config.getoption('--no-pep8'): - default_test_modules.remove('matplotlib.tests.test_coding_standards') - IGNORED_TESTS['matplotlib'] += 'test_coding_standards' - - -def pytest_unconfigure(config): - matplotlib._called_from_pytest = False - - -def pytest_ignore_collect(path, config): - if path.ext == '.py': - collect_filter = config.getoption('--collect-filter') - return COLLECT_FILTERS[collect_filter](path) - - -def pytest_pycollect_makeitem(collector, name, obj): - if inspect.isclass(obj): - if is_nose_class(obj) and not issubclass(obj, unittest.TestCase): - # Workaround unittest-like setup/teardown names in pure classes - setup = getattr(obj, 'setUp', None) - if setup is not None: - obj.setup_method = lambda self, _: obj.setUp(self) - tearDown = getattr(obj, 'tearDown', None) - if tearDown is not None: - obj.teardown_method = lambda self, _: obj.tearDown(self) - setUpClass = getattr(obj, 'setUpClass', None) - if setUpClass is not None: - obj.setup_class = obj.setUpClass - tearDownClass = getattr(obj, 'tearDownClass', None) - if tearDownClass is not None: - obj.teardown_class = obj.tearDownClass - - return pytest.Class(name, parent=collector) diff --git a/distribute_setup.py b/distribute_setup.py deleted file mode 100755 index a4ba3a698df6..000000000000 --- a/distribute_setup.py +++ /dev/null @@ -1,559 +0,0 @@ -#!python -"""Bootstrap distribute installation - -If you want to use setuptools in your package's setup.py, just include this -file in the same directory with it, and add this to the top of your setup.py:: - - from distribute_setup import use_setuptools - use_setuptools() - -If you want to require a specific version of setuptools, set a download -mirror, or use an alternate download directory, you can do so by supplying -the appropriate options to ``use_setuptools()``. - -This file can also be run as a script to install or upgrade setuptools. -""" -import os -import shutil -import sys -import time -import fnmatch -import tempfile -import tarfile -import optparse - -from distutils import log - -try: - from site import USER_SITE -except ImportError: - USER_SITE = None - -try: - import subprocess - - def _python_cmd(*args): - args = (sys.executable,) + args - return subprocess.call(args) == 0 - -except ImportError: - # will be used for python 2.3 - def _python_cmd(*args): - args = (sys.executable,) + args - # quoting arguments if windows - if sys.platform == 'win32': - def quote(arg): - if ' ' in arg: - return '"%s"' % arg - return arg - args = [quote(arg) for arg in args] - return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 - -MINIMUM_VERSION = "0.6.28" -DEFAULT_VERSION = "0.6.45" -DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" -SETUPTOOLS_FAKED_VERSION = "0.6c11" - -SETUPTOOLS_PKG_INFO = """\ -Metadata-Version: 1.0 -Name: setuptools -Version: %s -Summary: xxxx -Home-page: xxx -Author: xxx -Author-email: xxx -License: xxx -Description: xxx -""" % SETUPTOOLS_FAKED_VERSION - - -def _install(tarball, install_args=()): - # extracting the tarball - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) - - # installing - log.warn('Installing Distribute') - if not _python_cmd('setup.py', 'install', *install_args): - log.warn('Something went wrong during the installation.') - log.warn('See the error message above.') - # exitcode will be 2 - return 2 - finally: - os.chdir(old_wd) - shutil.rmtree(tmpdir) - - -def _build_egg(egg, tarball, to_dir): - # extracting the tarball - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) - - # building an egg - log.warn('Building a Distribute egg in %s', to_dir) - _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) - - finally: - os.chdir(old_wd) - shutil.rmtree(tmpdir) - # returning the result - log.warn(egg) - if not os.path.exists(egg): - raise IOError('Could not build the egg.') - - -def _do_download(version, download_base, to_dir, download_delay): - egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' - % (version, sys.version_info[0], sys.version_info[1])) - if not os.path.exists(egg): - tarball = download_setuptools(version, download_base, - to_dir, download_delay) - _build_egg(egg, tarball, to_dir) - sys.path.insert(0, egg) - import setuptools - setuptools.bootstrap_install_from = egg - - -def use_setuptools(version=MINIMUM_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, download_delay=15, no_fake=True): - # making sure we use the absolute path - to_dir = os.path.abspath(to_dir) - was_imported = 'pkg_resources' in sys.modules or \ - 'setuptools' in sys.modules - try: - try: - import pkg_resources - - # Setuptools 0.7b and later is a suitable (and preferable) - # substitute for any Distribute version. - try: - pkg_resources.require("setuptools>=0.7b") - return - except (pkg_resources.DistributionNotFound, - pkg_resources.VersionConflict): - pass - - if not hasattr(pkg_resources, '_distribute'): - if not no_fake: - _fake_setuptools() - raise ImportError - except ImportError: - return _do_download(version, download_base, to_dir, download_delay) - try: - pkg_resources.require("distribute>=" + version) - return - except pkg_resources.VersionConflict: - e = sys.exc_info()[1] - if was_imported: - sys.stderr.write( - "The required version of distribute (>=%s) is not available,\n" - "and can't be installed while this script is running. Please\n" - "install a more recent version first, using\n" - "'easy_install -U distribute'." - "\n\n(Currently using %r)\n" % (version, e.args[0])) - sys.exit(2) - else: - del pkg_resources, sys.modules['pkg_resources'] # reload ok - return _do_download(version, download_base, to_dir, - download_delay) - except pkg_resources.DistributionNotFound: - return _do_download(version, download_base, to_dir, - download_delay) - finally: - if not no_fake: - _create_fake_setuptools_pkg_info(to_dir) - - -def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, delay=15): - """Download distribute from a specified location and return its filename - - `version` should be a valid distribute version number that is available - as an egg for download under the `download_base` URL (which should end - with a '/'). `to_dir` is the directory where the egg will be downloaded. - `delay` is the number of seconds to pause before an actual download - attempt. - """ - # making sure we use the absolute path - to_dir = os.path.abspath(to_dir) - try: - from urllib.request import urlopen - except ImportError: - from urllib2 import urlopen - tgz_name = "distribute-%s.tar.gz" % version - url = download_base + tgz_name - saveto = os.path.join(to_dir, tgz_name) - src = dst = None - if not os.path.exists(saveto): # Avoid repeated downloads - try: - log.warn("Downloading %s", url) - src = urlopen(url) - # Read/write all in one block, so we don't create a corrupt file - # if the download is interrupted. - data = src.read() - dst = open(saveto, "wb") - dst.write(data) - finally: - if src: - src.close() - if dst: - dst.close() - return os.path.realpath(saveto) - - -def _no_sandbox(function): - def __no_sandbox(*args, **kw): - try: - from setuptools.sandbox import DirectorySandbox - if not hasattr(DirectorySandbox, '_old'): - def violation(*args): - pass - DirectorySandbox._old = DirectorySandbox._violation - DirectorySandbox._violation = violation - patched = True - else: - patched = False - except ImportError: - patched = False - - try: - return function(*args, **kw) - finally: - if patched: - DirectorySandbox._violation = DirectorySandbox._old - del DirectorySandbox._old - - return __no_sandbox - - -def _patch_file(path, content): - """Will backup the file then patch it""" - f = open(path) - existing_content = f.read() - f.close() - if existing_content == content: - # already patched - log.warn('Already patched.') - return False - log.warn('Patching...') - _rename_path(path) - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() - return True - -_patch_file = _no_sandbox(_patch_file) - - -def _same_content(path, content): - f = open(path) - existing_content = f.read() - f.close() - return existing_content == content - - -def _rename_path(path): - new_name = path + '.OLD.%s' % time.time() - log.warn('Renaming %s to %s', path, new_name) - os.rename(path, new_name) - return new_name - - -def _remove_flat_installation(placeholder): - if not os.path.isdir(placeholder): - log.warn('Unkown installation at %s', placeholder) - return False - found = False - for file in os.listdir(placeholder): - if fnmatch.fnmatch(file, 'setuptools*.egg-info'): - found = True - break - if not found: - log.warn('Could not locate setuptools*.egg-info') - return - - log.warn('Moving elements out of the way...') - pkg_info = os.path.join(placeholder, file) - if os.path.isdir(pkg_info): - patched = _patch_egg_dir(pkg_info) - else: - patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) - - if not patched: - log.warn('%s already patched.', pkg_info) - return False - # now let's move the files out of the way - for element in ('setuptools', 'pkg_resources.py', 'site.py'): - element = os.path.join(placeholder, element) - if os.path.exists(element): - _rename_path(element) - else: - log.warn('Could not find the %s element of the ' - 'Setuptools distribution', element) - return True - -_remove_flat_installation = _no_sandbox(_remove_flat_installation) - - -def _after_install(dist): - log.warn('After install bootstrap.') - placeholder = dist.get_command_obj('install').install_purelib - _create_fake_setuptools_pkg_info(placeholder) - - -def _create_fake_setuptools_pkg_info(placeholder): - if not placeholder or not os.path.exists(placeholder): - log.warn('Could not find the install location') - return - pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) - setuptools_file = 'setuptools-%s-py%s.egg-info' % \ - (SETUPTOOLS_FAKED_VERSION, pyver) - pkg_info = os.path.join(placeholder, setuptools_file) - if os.path.exists(pkg_info): - log.warn('%s already exists', pkg_info) - return - - log.warn('Creating %s', pkg_info) - try: - f = open(pkg_info, 'w') - except EnvironmentError: - log.warn("Don't have permissions to write %s, skipping", pkg_info) - return - try: - f.write(SETUPTOOLS_PKG_INFO) - finally: - f.close() - - pth_file = os.path.join(placeholder, 'setuptools.pth') - log.warn('Creating %s', pth_file) - f = open(pth_file, 'w') - try: - f.write(os.path.join(os.curdir, setuptools_file)) - finally: - f.close() - -_create_fake_setuptools_pkg_info = _no_sandbox( - _create_fake_setuptools_pkg_info -) - - -def _patch_egg_dir(path): - # let's check if it's already patched - pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') - if os.path.exists(pkg_info): - if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): - log.warn('%s already patched.', pkg_info) - return False - _rename_path(path) - os.mkdir(path) - os.mkdir(os.path.join(path, 'EGG-INFO')) - pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') - f = open(pkg_info, 'w') - try: - f.write(SETUPTOOLS_PKG_INFO) - finally: - f.close() - return True - -_patch_egg_dir = _no_sandbox(_patch_egg_dir) - - -def _before_install(): - log.warn('Before install bootstrap.') - _fake_setuptools() - - -def _under_prefix(location): - if 'install' not in sys.argv: - return True - args = sys.argv[sys.argv.index('install') + 1:] - for index, arg in enumerate(args): - for option in ('--root', '--prefix'): - if arg.startswith('%s=' % option): - top_dir = arg.split('root=')[-1] - return location.startswith(top_dir) - elif arg == option: - if len(args) > index: - top_dir = args[index + 1] - return location.startswith(top_dir) - if arg == '--user' and USER_SITE is not None: - return location.startswith(USER_SITE) - return True - - -def _fake_setuptools(): - log.warn('Scanning installed packages') - try: - import pkg_resources - except ImportError: - # we're cool - log.warn('Setuptools or Distribute does not seem to be installed.') - return - ws = pkg_resources.working_set - try: - setuptools_dist = ws.find( - pkg_resources.Requirement.parse('setuptools', replacement=False) - ) - except TypeError: - # old distribute API - setuptools_dist = ws.find( - pkg_resources.Requirement.parse('setuptools') - ) - - if setuptools_dist is None: - log.warn('No setuptools distribution found') - return - # detecting if it was already faked - setuptools_location = setuptools_dist.location - log.warn('Setuptools installation detected at %s', setuptools_location) - - # if --root or --preix was provided, and if - # setuptools is not located in them, we don't patch it - if not _under_prefix(setuptools_location): - log.warn('Not patching, --root or --prefix is installing Distribute' - ' in another location') - return - - # let's see if its an egg - if not setuptools_location.endswith('.egg'): - log.warn('Non-egg installation') - res = _remove_flat_installation(setuptools_location) - if not res: - return - else: - log.warn('Egg installation') - pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') - if (os.path.exists(pkg_info) and - _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): - log.warn('Already patched.') - return - log.warn('Patching...') - # let's create a fake egg replacing setuptools one - res = _patch_egg_dir(setuptools_location) - if not res: - return - log.warn('Patching complete.') - _relaunch() - - -def _relaunch(): - log.warn('Relaunching...') - # we have to relaunch the process - # pip marker to avoid a relaunch bug - _cmd1 = ['-c', 'install', '--single-version-externally-managed'] - _cmd2 = ['-c', 'install', '--record'] - if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2: - sys.argv[0] = 'setup.py' - args = [sys.executable] + sys.argv - sys.exit(subprocess.call(args)) - - -def _extractall(self, path=".", members=None): - """Extract all members from the archive to the current working - directory and set owner, modification time and permissions on - directories afterwards. `path' specifies a different directory - to extract to. `members' is optional and must be a subset of the - list returned by getmembers(). - """ - import copy - import operator - from tarfile import ExtractError - directories = [] - - if members is None: - members = self - - for tarinfo in members: - if tarinfo.isdir(): - # Extract directories with a safe mode. - directories.append(tarinfo) - tarinfo = copy.copy(tarinfo) - tarinfo.mode = 448 # decimal for oct 0700 - self.extract(tarinfo, path) - - # Reverse sort directories. - if sys.version_info < (2, 4): - def sorter(dir1, dir2): - return cmp(dir1.name, dir2.name) - directories.sort(sorter) - directories.reverse() - else: - directories.sort(key=operator.attrgetter('name'), reverse=True) - - # Set correct owner, mtime and filemode on directories. - for tarinfo in directories: - dirpath = os.path.join(path, tarinfo.name) - try: - self.chown(tarinfo, dirpath) - self.utime(tarinfo, dirpath) - self.chmod(tarinfo, dirpath) - except ExtractError: - e = sys.exc_info()[1] - if self.errorlevel > 1: - raise - else: - self._dbg(1, "tarfile: %s" % e) - - -def _build_install_args(options): - """ - Build the arguments to 'python setup.py install' on the distribute package - """ - install_args = [] - if options.user_install: - if sys.version_info < (2, 6): - log.warn("--user requires Python 2.6 or later") - raise SystemExit(1) - install_args.append('--user') - return install_args - - -def _parse_args(): - """ - Parse the command line for options - """ - parser = optparse.OptionParser() - parser.add_option( - '--user', dest='user_install', action='store_true', default=False, - help='install in user site package (requires Python 2.6 or later)') - parser.add_option( - '--download-base', dest='download_base', metavar="URL", - default=DEFAULT_URL, - help='alternative URL from where to download the distribute package') - options, args = parser.parse_args() - # positional arguments are ignored - return options - - -def main(version=DEFAULT_VERSION): - """Install or upgrade setuptools and EasyInstall""" - options = _parse_args() - tarball = download_setuptools(download_base=options.download_base) - return _install(tarball, _build_install_args(options)) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/doc-requirements.txt b/doc-requirements.txt deleted file mode 100644 index 9c2c189e9151..000000000000 --- a/doc-requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Requirements for building docs -# You will first need a matching matplotlib installed -# e.g (from the matplotlib root directory) -# pip install -e . -# -# Install the documentation requirements with: -# pip install -r doc-requirements.txt -# -sphinx>1.0 -numpydoc -ipython -mock -colorspacious -pillow diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000000..baed196a3ee2 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,50 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -W --keep-going +SPHINXBUILD = python -msphinx +SPHINXPROJ = matplotlib +SOURCEDIR = . +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# workaround because sphinx does not completely clean up (#11139) +clean: + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + rm -rf "$(SOURCEDIR)/build" + rm -rf "$(SOURCEDIR)/_tags" + rm -rf "$(SOURCEDIR)/api/_as_gen" + rm -rf "$(SOURCEDIR)/gallery" + rm -rf "$(SOURCEDIR)/plot_types" + rm -rf "$(SOURCEDIR)/tutorials" + rm -rf "$(SOURCEDIR)/users/explain" + rm -rf "$(SOURCEDIR)/savefig" + rm -rf "$(SOURCEDIR)/sphinxext/__pycache__" + rm -f $(SOURCEDIR)/_static/constrained_layout*.png + rm -f $(SOURCEDIR)/sg_execution_times.rst + +show: + @python -c "import webbrowser; webbrowser.open_new_tab('file://$(shell pwd)/build/html/index.html')" + +html-noplot: + $(SPHINXBUILD) -D plot_gallery=0 -b html $(SOURCEDIR) $(BUILDDIR)/html $(SPHINXOPTS) $(O) + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +# This will skip the subdirectories listed in .mpl_skip_subdirs.yaml If +# this file does not exist, one will be created for you. This option useful +# to quickly build parts of the docs, but the resulting build will not +# have all the crosslinks etc. +html-skip-subdirs: + $(SPHINXBUILD) -D skip_sub_dirs=1 -b html $(SOURCEDIR) $(BUILDDIR)/html $(SPHINXOPTS) $(O) + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/README.txt b/doc/README.txt index 5a9f8187dcf1..c34dbd769712 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -1,64 +1,66 @@ -maptlotlib documentation +Matplotlib documentation ======================== - Building the documentation -------------------------- -A list of dependencies can be found in ../doc-requirements.txt. +See :file:`doc/devel/documenting_mpl.rst` for instructions to build the docs. -All of these dependencies can be installed through pip:: +Organization +------------ - pip install -r ../doc-requirements.txt +This is the top level directory for the Matplotlib +documentation. All of the documentation is written using Sphinx, a +python documentation system based on reStructuredText. This directory contains the +following -or conda:: +Files +^^^^^ - conda install sphinx numpydoc ipython mock colorspacious pillow +* index.rst - the top level include document (and landing page) for the Matplotlib docs -To build the HTML documentation, type ``python make.py html`` in this -directory. The top file of the results will be ./build/html/index.html +* conf.py - the sphinx configuration -**Note that Sphinx uses the installed version of the package to build the -documentation**: matplotlib must be installed *before* the docs can be -generated. +* docutils.conf - htmnl output configuration -You can build the documentation with several options: +* Makefile and make.bat - entry points for building the docs -* `--small` saves figures in low resolution. -* `--allowsphinxwarnings`: Don't turn Sphinx warnings into errors. -* `-n N` enables parallel build of the documentation using N process. +* matplotlibrc - rcParam configuration for docs -Organization -------------- +* missing-references.json - list of known missing/broken references -This is the top level build directory for the matplotlib -documentation. All of the documentation is written using sphinx, a -python documentation system built on top of ReST. This directory contains -* users - the user documentation, e.g., plotting tutorials, configuration - tips, etc. +Content folders +^^^^^^^^^^^^^^^ -* devel - documentation for matplotlib developers +* api - templates for generating the api documentation -* faq - frequently asked questions +* devel - documentation for contributing to Matplotlib -* api - placeholders to automatically generate the api documentation +* project - about Matplotlib, e.g. mission, code of conduct, licenses, history, etc. -* mpl_toolkits - documentation of individual toolkits that ship with - matplotlib +* users - usage documentation, e.g., installation, tutorials, faq, explanations, etc. -* make.py - the build script to build the html or PDF docs +* thirdpartypackages - redirect to -* index.rst - the top level include document for matplotlib docs +Build folders +^^^^^^^^^^^^^ -* conf.py - the sphinx configuration +* _static - supplementary files; e.g. images, CSS, etc. -* _static - used by the sphinx build system +* _templates - Sphinx page templates -* _templates - used by the sphinx build system +* sphinxext - Sphinx extensions for the Matplotlib docs -* sphinxext - Sphinx extensions for the mpl docs +Symlinks +-------- -* mpl_examples - a link to the matplotlib examples in case any - documentation wants to literal include them +During the documentation build, sphinx-gallery creates symlinks from the source folders +in `/galleries` to target_folders in '/doc'; therefore ensure that you are editing the +real files rather than the symbolic links. +Source files -> symlink: +* galleries/tutorials -> doc/tutorials +* galleries/plot_types -> doc/plot_types +* galleries/examples -> doc/gallery +* galleries/users_explain -> doc/users/explain diff --git a/doc/_embedded_plots/axes_margins.py b/doc/_embedded_plots/axes_margins.py new file mode 100644 index 000000000000..d026840c3c15 --- /dev/null +++ b/doc/_embedded_plots/axes_margins.py @@ -0,0 +1,42 @@ +import numpy as np +import matplotlib.pyplot as plt + +fig, ax = plt.subplots(figsize=(6.5, 4)) +x = np.linspace(0, 1, 33) +y = -np.sin(x * 2*np.pi) +ax.plot(x, y, 'o') +ax.margins(0.5, 0.2) +ax.set_title("margins(x=0.5, y=0.2)") + +# fix the Axes limits so that the following helper drawings +# cannot change them further. +ax.set(xlim=ax.get_xlim(), ylim=ax.get_ylim()) + + +def arrow(p1, p2, **props): + ax.annotate("", p1, p2, + arrowprops=dict(arrowstyle="<->", shrinkA=0, shrinkB=0, **props)) + + +axmin, axmax = ax.get_xlim() +aymin, aymax = ax.get_ylim() +xmin, xmax = x.min(), x.max() +ymin, ymax = y.min(), y.max() + +y0 = -0.8 +ax.axvspan(axmin, xmin, color=("orange", 0.1)) +ax.axvspan(xmax, axmax, color=("orange", 0.1)) +arrow((xmin, y0), (xmax, y0), color="sienna") +arrow((xmax, y0), (axmax, y0), color="orange") +ax.text((xmax + axmax)/2, y0+0.05, "x margin\n* x data range", + ha="center", va="bottom", color="orange") +ax.text(0.55, y0+0.1, "x data range", va="bottom", color="sienna") + +x0 = 0.1 +ax.axhspan(aymin, ymin, color=("tab:green", 0.1)) +ax.axhspan(ymax, aymax, color=("tab:green", 0.1)) +arrow((x0, ymin), (x0, ymax), color="darkgreen") +arrow((x0, ymax), (x0, aymax), color="tab:green") +ax.text(x0, (ymax + aymax) / 2, " y margin * y data range", + va="center", color="tab:green") +ax.text(x0, 0.5, " y data range", color="darkgreen") diff --git a/doc/_embedded_plots/figure_subplots_adjust.py b/doc/_embedded_plots/figure_subplots_adjust.py new file mode 100644 index 000000000000..d32a029fe05d --- /dev/null +++ b/doc/_embedded_plots/figure_subplots_adjust.py @@ -0,0 +1,34 @@ +import matplotlib.pyplot as plt + + +fig, axs = plt.subplots(2, 2, figsize=(6.5, 4)) +fig.set_facecolor('lightblue') +fig.subplots_adjust(0.1, 0.1, 0.9, 0.9, 0.4, 0.4) + +overlay = fig.add_axes([0, 0, 1, 1], zorder=100) +overlay.axis("off") +xycoords = 'figure fraction' +arrowprops = dict(arrowstyle="<->", shrinkA=0, shrinkB=0) + +for ax in axs.flat: + ax.set(xticks=[], yticks=[]) + +overlay.annotate("", (0, 0.75), (0.1, 0.75), + xycoords=xycoords, arrowprops=arrowprops) # left +overlay.annotate("", (0.435, 0.25), (0.565, 0.25), + xycoords=xycoords, arrowprops=arrowprops) # wspace +overlay.annotate("", (0, 0.8), (0.9, 0.8), + xycoords=xycoords, arrowprops=arrowprops) # right +fig.text(0.05, 0.7, "left", ha="center") +fig.text(0.5, 0.3, "wspace", ha="center") +fig.text(0.05, 0.83, "right", ha="center") + +overlay.annotate("", (0.75, 0), (0.75, 0.1), + xycoords=xycoords, arrowprops=arrowprops) # bottom +overlay.annotate("", (0.25, 0.435), (0.25, 0.565), + xycoords=xycoords, arrowprops=arrowprops) # hspace +overlay.annotate("", (0.8, 0), (0.8, 0.9), + xycoords=xycoords, arrowprops=arrowprops) # top +fig.text(0.65, 0.05, "bottom", va="center") +fig.text(0.28, 0.5, "hspace", va="center") +fig.text(0.82, 0.05, "top", va="center") diff --git a/doc/_embedded_plots/grouped_bar.py b/doc/_embedded_plots/grouped_bar.py new file mode 100644 index 000000000000..f02e269328d2 --- /dev/null +++ b/doc/_embedded_plots/grouped_bar.py @@ -0,0 +1,15 @@ +import matplotlib.pyplot as plt + +categories = ['A', 'B'] +data0 = [1.0, 3.0] +data1 = [1.4, 3.4] +data2 = [1.8, 3.8] + +fig, ax = plt.subplots(figsize=(4, 2.2)) +ax.grouped_bar( + [data0, data1, data2], + tick_labels=categories, + labels=['dataset 0', 'dataset 1', 'dataset 2'], + colors=['#1f77b4', '#58a1cf', '#abd0e6'], +) +ax.legend() diff --git a/doc/_embedded_plots/hatch_classes.py b/doc/_embedded_plots/hatch_classes.py new file mode 100644 index 000000000000..cb9cd7d4b356 --- /dev/null +++ b/doc/_embedded_plots/hatch_classes.py @@ -0,0 +1,28 @@ +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle + +fig, ax = plt.subplots() + +pattern_to_class = { + '/': 'NorthEastHatch', + '\\': 'SouthEastHatch', + '|': 'VerticalHatch', + '-': 'HorizontalHatch', + '+': 'VerticalHatch + HorizontalHatch', + 'x': 'NorthEastHatch + SouthEastHatch', + 'o': 'SmallCircles', + 'O': 'LargeCircles', + '.': 'SmallFilledCircles', + '*': 'Stars', +} + +for i, (hatch, classes) in enumerate(pattern_to_class.items()): + r = Rectangle((0.1, i+0.5), 0.8, 0.8, fill=False, hatch=hatch*2) + ax.add_patch(r) + h = ax.annotate(f"'{hatch}'", xy=(1.2, .5), xycoords=r, + family='monospace', va='center', ha='left') + ax.annotate(pattern_to_class[hatch], xy=(1.5, .5), xycoords=h, + family='monospace', va='center', ha='left', color='tab:blue') + +ax.set(xlim=(0, 5), ylim=(0, i+1.5), yinverted=True) +ax.set_axis_off() diff --git a/doc/_static/FigureInline.png b/doc/_static/FigureInline.png new file mode 100644 index 000000000000..6b7bd42c28f1 Binary files /dev/null and b/doc/_static/FigureInline.png differ diff --git a/doc/_static/FigureNotebook.png b/doc/_static/FigureNotebook.png new file mode 100644 index 000000000000..2d6d11cac3cc Binary files /dev/null and b/doc/_static/FigureNotebook.png differ diff --git a/doc/_static/FigureQtAgg.png b/doc/_static/FigureQtAgg.png new file mode 100644 index 000000000000..8d19e1a309ef Binary files /dev/null and b/doc/_static/FigureQtAgg.png differ diff --git a/doc/_static/John-hunter-crop-2.jpg b/doc/_static/John-hunter-crop-2.jpg deleted file mode 100644 index 48abd2e57626..000000000000 Binary files a/doc/_static/John-hunter-crop-2.jpg and /dev/null differ diff --git a/doc/_static/anatomy.png b/doc/_static/anatomy.png new file mode 100644 index 000000000000..0809d43f7a56 Binary files /dev/null and b/doc/_static/anatomy.png differ diff --git a/doc/_static/basemap_contour1.png b/doc/_static/basemap_contour1.png deleted file mode 100644 index 28198ab6d19f..000000000000 Binary files a/doc/_static/basemap_contour1.png and /dev/null differ diff --git a/doc/_static/boxplot_explanation.png b/doc/_static/boxplot_explanation.png deleted file mode 100644 index d057496e4e44..000000000000 Binary files a/doc/_static/boxplot_explanation.png and /dev/null differ diff --git a/doc/_static/cartopy_hurricane_katrina_01_00.png b/doc/_static/cartopy_hurricane_katrina_01_00.png deleted file mode 100644 index b50ec001ec01..000000000000 Binary files a/doc/_static/cartopy_hurricane_katrina_01_00.png and /dev/null differ diff --git a/doc/_static/cm_fontset.png b/doc/_static/cm_fontset.png deleted file mode 100644 index 328ba1348fa1..000000000000 Binary files a/doc/_static/cm_fontset.png and /dev/null differ diff --git a/doc/_static/contents.png b/doc/_static/contents.png deleted file mode 100644 index 7fb82154a174..000000000000 Binary files a/doc/_static/contents.png and /dev/null differ diff --git a/doc/_static/contour_frontpage.png b/doc/_static/contour_frontpage.png deleted file mode 100644 index 61e567cd38cc..000000000000 Binary files a/doc/_static/contour_frontpage.png and /dev/null differ diff --git a/doc/_static/demo_axes_grid.png b/doc/_static/demo_axes_grid.png deleted file mode 100644 index 9af9fdfe6380..000000000000 Binary files a/doc/_static/demo_axes_grid.png and /dev/null differ diff --git a/doc/_static/eeg_large.png b/doc/_static/eeg_large.png deleted file mode 100644 index 6224f4c2de60..000000000000 Binary files a/doc/_static/eeg_large.png and /dev/null differ diff --git a/doc/_static/eeg_small.png b/doc/_static/eeg_small.png deleted file mode 100644 index fb02af5b4a36..000000000000 Binary files a/doc/_static/eeg_small.png and /dev/null differ diff --git a/doc/_static/fa/LICENSE b/doc/_static/fa/LICENSE new file mode 100644 index 000000000000..ea0d11539513 --- /dev/null +++ b/doc/_static/fa/LICENSE @@ -0,0 +1,5 @@ +Font Awesome SVG Icons are covered by CC BY 4.0 License. + +https://fontawesome.com/license/free + +Icons are based on Font Awesome 5.11.2 and colors have been adapted. diff --git a/doc/_static/fa/discourse-brands.svg b/doc/_static/fa/discourse-brands.svg new file mode 100644 index 000000000000..3b8e2e0fab0f --- /dev/null +++ b/doc/_static/fa/discourse-brands.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/envelope-regular.svg b/doc/_static/fa/envelope-regular.svg new file mode 100644 index 000000000000..9f82026d241c --- /dev/null +++ b/doc/_static/fa/envelope-regular.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/github-brands.svg b/doc/_static/fa/github-brands.svg new file mode 100644 index 000000000000..52e76df0df4a --- /dev/null +++ b/doc/_static/fa/github-brands.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/gitter-brands.svg b/doc/_static/fa/gitter-brands.svg new file mode 100644 index 000000000000..f1d59e045c03 --- /dev/null +++ b/doc/_static/fa/gitter-brands.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/hashtag-solid.svg b/doc/_static/fa/hashtag-solid.svg new file mode 100644 index 000000000000..c7c033faeac2 --- /dev/null +++ b/doc/_static/fa/hashtag-solid.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/plus-square-regular.svg b/doc/_static/fa/plus-square-regular.svg new file mode 100644 index 000000000000..3303fd81116a --- /dev/null +++ b/doc/_static/fa/plus-square-regular.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/question-circle-regular.svg b/doc/_static/fa/question-circle-regular.svg new file mode 100644 index 000000000000..5ddce26452f9 --- /dev/null +++ b/doc/_static/fa/question-circle-regular.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/fa/stack-overflow-brands.svg b/doc/_static/fa/stack-overflow-brands.svg new file mode 100644 index 000000000000..de164d4a2cf0 --- /dev/null +++ b/doc/_static/fa/stack-overflow-brands.svg @@ -0,0 +1 @@ + diff --git a/doc/_static/ggplot.png b/doc/_static/ggplot.png deleted file mode 100644 index f103f2541b75..000000000000 Binary files a/doc/_static/ggplot.png and /dev/null differ diff --git a/doc/_static/histogram_frontpage.png b/doc/_static/histogram_frontpage.png deleted file mode 100644 index 3c2e5ad0b88c..000000000000 Binary files a/doc/_static/histogram_frontpage.png and /dev/null differ diff --git a/doc/_static/holoviews.png b/doc/_static/holoviews.png deleted file mode 100644 index f495d0e25737..000000000000 Binary files a/doc/_static/holoviews.png and /dev/null differ diff --git a/doc/_static/icon.png b/doc/_static/icon.png deleted file mode 100644 index 3ec68e5014a9..000000000000 Binary files a/doc/_static/icon.png and /dev/null differ diff --git a/doc/_static/logo2.png b/doc/_static/logo2.png deleted file mode 100644 index 72843ab1febb..000000000000 Binary files a/doc/_static/logo2.png and /dev/null differ diff --git a/doc/_static/logo_sidebar.png b/doc/_static/logo_sidebar.png deleted file mode 100644 index edc9cbd008a5..000000000000 Binary files a/doc/_static/logo_sidebar.png and /dev/null differ diff --git a/doc/_static/logo_sidebar_horiz.png b/doc/_static/logo_sidebar_horiz.png deleted file mode 100644 index 9274331a0258..000000000000 Binary files a/doc/_static/logo_sidebar_horiz.png and /dev/null differ diff --git a/doc/_static/markers/m00.png b/doc/_static/markers/m00.png new file mode 100644 index 000000000000..59b3ad7fddb0 Binary files /dev/null and b/doc/_static/markers/m00.png differ diff --git a/doc/_static/markers/m01.png b/doc/_static/markers/m01.png new file mode 100644 index 000000000000..e40b5db05243 Binary files /dev/null and b/doc/_static/markers/m01.png differ diff --git a/doc/_static/markers/m02.png b/doc/_static/markers/m02.png new file mode 100644 index 000000000000..1c67ae57345c Binary files /dev/null and b/doc/_static/markers/m02.png differ diff --git a/doc/_static/markers/m03.png b/doc/_static/markers/m03.png new file mode 100644 index 000000000000..552470a2005d Binary files /dev/null and b/doc/_static/markers/m03.png differ diff --git a/doc/_static/markers/m04.png b/doc/_static/markers/m04.png new file mode 100644 index 000000000000..8e2cc09b85b5 Binary files /dev/null and b/doc/_static/markers/m04.png differ diff --git a/doc/_static/markers/m05.png b/doc/_static/markers/m05.png new file mode 100644 index 000000000000..799340390422 Binary files /dev/null and b/doc/_static/markers/m05.png differ diff --git a/doc/_static/markers/m06.png b/doc/_static/markers/m06.png new file mode 100644 index 000000000000..51df3f4b6e2e Binary files /dev/null and b/doc/_static/markers/m06.png differ diff --git a/doc/_static/markers/m07.png b/doc/_static/markers/m07.png new file mode 100644 index 000000000000..cffffd4a25d2 Binary files /dev/null and b/doc/_static/markers/m07.png differ diff --git a/doc/_static/markers/m08.png b/doc/_static/markers/m08.png new file mode 100644 index 000000000000..d8599e7bbd2f Binary files /dev/null and b/doc/_static/markers/m08.png differ diff --git a/doc/_static/markers/m09.png b/doc/_static/markers/m09.png new file mode 100644 index 000000000000..40c754dcd833 Binary files /dev/null and b/doc/_static/markers/m09.png differ diff --git a/doc/_static/markers/m10.png b/doc/_static/markers/m10.png new file mode 100644 index 000000000000..101743620ede Binary files /dev/null and b/doc/_static/markers/m10.png differ diff --git a/doc/_static/markers/m11.png b/doc/_static/markers/m11.png new file mode 100644 index 000000000000..a6a5cbd6935d Binary files /dev/null and b/doc/_static/markers/m11.png differ diff --git a/doc/_static/markers/m12.png b/doc/_static/markers/m12.png new file mode 100644 index 000000000000..68c5ce111d2c Binary files /dev/null and b/doc/_static/markers/m12.png differ diff --git a/doc/_static/markers/m13.png b/doc/_static/markers/m13.png new file mode 100644 index 000000000000..232c8eb686f3 Binary files /dev/null and b/doc/_static/markers/m13.png differ diff --git a/doc/_static/markers/m14.png b/doc/_static/markers/m14.png new file mode 100644 index 000000000000..e49e35635e9b Binary files /dev/null and b/doc/_static/markers/m14.png differ diff --git a/doc/_static/markers/m15.png b/doc/_static/markers/m15.png new file mode 100644 index 000000000000..68bf1ebcebf3 Binary files /dev/null and b/doc/_static/markers/m15.png differ diff --git a/doc/_static/markers/m16.png b/doc/_static/markers/m16.png new file mode 100644 index 000000000000..d3f594b11f4a Binary files /dev/null and b/doc/_static/markers/m16.png differ diff --git a/doc/_static/markers/m17.png b/doc/_static/markers/m17.png new file mode 100644 index 000000000000..2c6c57243b52 Binary files /dev/null and b/doc/_static/markers/m17.png differ diff --git a/doc/_static/markers/m18.png b/doc/_static/markers/m18.png new file mode 100644 index 000000000000..a52d05098b5d Binary files /dev/null and b/doc/_static/markers/m18.png differ diff --git a/doc/_static/markers/m19.png b/doc/_static/markers/m19.png new file mode 100644 index 000000000000..0c40250bd815 Binary files /dev/null and b/doc/_static/markers/m19.png differ diff --git a/doc/_static/markers/m20.png b/doc/_static/markers/m20.png new file mode 100644 index 000000000000..1f75d0297a62 Binary files /dev/null and b/doc/_static/markers/m20.png differ diff --git a/doc/_static/markers/m21.png b/doc/_static/markers/m21.png new file mode 100644 index 000000000000..d3b4dee68ba8 Binary files /dev/null and b/doc/_static/markers/m21.png differ diff --git a/doc/_static/markers/m22.png b/doc/_static/markers/m22.png new file mode 100644 index 000000000000..44e856008fa2 Binary files /dev/null and b/doc/_static/markers/m22.png differ diff --git a/doc/_static/markers/m23.png b/doc/_static/markers/m23.png new file mode 100644 index 000000000000..742b27f0f0f0 Binary files /dev/null and b/doc/_static/markers/m23.png differ diff --git a/doc/_static/markers/m24.png b/doc/_static/markers/m24.png new file mode 100644 index 000000000000..c666d7a8ee1e Binary files /dev/null and b/doc/_static/markers/m24.png differ diff --git a/doc/_static/markers/m25.png b/doc/_static/markers/m25.png new file mode 100644 index 000000000000..d48d2c4659fa Binary files /dev/null and b/doc/_static/markers/m25.png differ diff --git a/doc/_static/markers/m26.png b/doc/_static/markers/m26.png new file mode 100644 index 000000000000..e0b2bbddbd8d Binary files /dev/null and b/doc/_static/markers/m26.png differ diff --git a/doc/_static/markers/m27.png b/doc/_static/markers/m27.png new file mode 100644 index 000000000000..d91c9594ba1a Binary files /dev/null and b/doc/_static/markers/m27.png differ diff --git a/doc/_static/markers/m28.png b/doc/_static/markers/m28.png new file mode 100644 index 000000000000..58ef370d5833 Binary files /dev/null and b/doc/_static/markers/m28.png differ diff --git a/doc/_static/markers/m29.png b/doc/_static/markers/m29.png new file mode 100644 index 000000000000..48b326482ace Binary files /dev/null and b/doc/_static/markers/m29.png differ diff --git a/doc/_static/markers/m30.png b/doc/_static/markers/m30.png new file mode 100644 index 000000000000..bc9b72859ebb Binary files /dev/null and b/doc/_static/markers/m30.png differ diff --git a/doc/_static/markers/m31.png b/doc/_static/markers/m31.png new file mode 100644 index 000000000000..f4aedabe4d29 Binary files /dev/null and b/doc/_static/markers/m31.png differ diff --git a/doc/_static/markers/m32.png b/doc/_static/markers/m32.png new file mode 100644 index 000000000000..e4c8d06605e1 Binary files /dev/null and b/doc/_static/markers/m32.png differ diff --git a/doc/_static/markers/m33.png b/doc/_static/markers/m33.png new file mode 100644 index 000000000000..893ea6a5a8d3 Binary files /dev/null and b/doc/_static/markers/m33.png differ diff --git a/doc/_static/markers/m34.png b/doc/_static/markers/m34.png new file mode 100644 index 000000000000..fd66b50b7dc3 Binary files /dev/null and b/doc/_static/markers/m34.png differ diff --git a/doc/_static/markers/m35.png b/doc/_static/markers/m35.png new file mode 100644 index 000000000000..365d652499c6 Binary files /dev/null and b/doc/_static/markers/m35.png differ diff --git a/doc/_static/markers/m36.png b/doc/_static/markers/m36.png new file mode 100644 index 000000000000..5b6ff5e953e7 Binary files /dev/null and b/doc/_static/markers/m36.png differ diff --git a/doc/_static/markers/m37.png b/doc/_static/markers/m37.png new file mode 100644 index 000000000000..7afebed4557d Binary files /dev/null and b/doc/_static/markers/m37.png differ diff --git a/doc/_static/matplotlib_iterm2_demo.png b/doc/_static/matplotlib_iterm2_demo.png deleted file mode 100644 index 72e316e3f61d..000000000000 Binary files a/doc/_static/matplotlib_iterm2_demo.png and /dev/null differ diff --git a/doc/_static/membrane_frontpage.png b/doc/_static/membrane_frontpage.png deleted file mode 100644 index ec1eba243120..000000000000 Binary files a/doc/_static/membrane_frontpage.png and /dev/null differ diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index a9a7089f239c..25bad17c3938 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -1,736 +1,222 @@ -/* - * Alternate Sphinx design - * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl. - */ - -body { - font-family: "Helvetica Neue", Helvetica, 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif; - font-size: 14px; - line-height: 150%; - text-align: center; - background-color: #BFD1D4; - color: black; - padding: 0; - border: 1px solid #aaa; - color: #333; - margin: 0px 80px 0px 80px; - min-width: 740px; -} - -a { - color: #CA7900; - text-decoration: none; -} - -strong { - font-weight: strong; +:root { + --pst-color-link: var(--pst-color-primary); + --pst-color-link-hover: var(--pst-color-secondary); + --sd-color-primary: var(--pst-color-primary); + --sd-color-primary-text: var(--pst-color-text-base); + --sd-color-secondary: #ee9040; + --sd-color-success: #28a745; + --sd-color-dark: #323232; + --sd-color-danger: #dc3545; + --sd-color-light: #c9c9c9; +} + +.simple li>p { + margin: 0; } -a:hover { - color: #2491CF; +/* multi column TOC */ +.contents ul { + list-style-type: none; + padding-left: 2em; } -pre { - font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; - font-size: 0.90em; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - letter-spacing: 0.015em; - padding: 1em; - border: 1px solid #ccc; - background-color: #f8f8f8; - line-height: 140%; +.contents > ul { + padding-left: 0; } -td.linenos pre { - padding: 0.5em 0; - border: 0; - background-color: transparent; - color: #aaa; +.multicol-toc > ul { + column-width: 250px; + column-gap: 60px; + -webkit-column-width: 250px; + -moz-column-width: 250px; + column-rule: 1px solid #ccc; } -table.highlighttable { - margin-left: 0.5em; +.multicol-toc > li { + /* break inside is not yet broadly supported, but we just try */ + break-inside: avoid-column; + -moz-break-inside: avoid-column; + -webkit-break-inside: avoid-column; } -table.highlighttable td { - padding: 0 0.5em 0 0.5em; +.contents > ul > li > a { + font-size: 1.0em; } -cite, code, tt { - font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.95em; - letter-spacing: 0.01em; -} +/* Hide red ¶ between the thumbnail and caption in gallery -hr { - border: 1px solid #abc; - margin: 2em; +Due the way that sphinx-gallery floats its captions the perma-link +does not float with it. +*/ +.sphx-glr-thumbcontainer p.caption:hover > a.headerlink{ + visibility: hidden; } -tt { - background-color: #f2f2f2; - border-bottom: 1px solid #ddd; - color: #333; +/* slightly reduce horizontal margin compared to gallery.css to + * get four columns of thumbnails in the pydata-sphinx-theme. */ +.sphx-glr-thumbcontainer { + margin: 5px 2px; } -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; - border: 0; +html[data-theme="dark"] .sphx-glr-thumbcontainer { + background-color: rgb(63, 63, 63); } -tt.descclassname { - background-color: transparent; - border: 0; +/* Set a fixed height so that lazy loading does not change heights. Without a fixed + * height lazy loading of images interferes with anchor links: Clicking a link goes to + * a certain position, but then the loaded images add content and move the anchor to a + * different position. + */ +.sphx-glr-thumbcontainer img { + height: 112px; } -tt.xref { - background-color: transparent; - font-weight: bold; - border: 0; +/* hide download buttons in example headers + * https://sphinx-gallery.github.io/stable/advanced.html#hide-the-download-buttons-in-the-example-headers + */ +div.sphx-glr-download-link-note { + display: none; } -a tt { - background-color: transparent; - font-weight: bold; +/* re-style the download button */ +div.sphx-glr-download a { + background-color: #E3F0F6; + background-image: none; + color: #11557c; border: 0; - color: #CA7900; -} - -a tt:hover { - color: #2491CF; -} - -dl { - margin-bottom: 15px; } -dd p { - margin-top: 1px; +div.sphx-glr-download a:hover { + background-color: #BCD4DF; } -dd ul, dd table { - margin-bottom: 10px; +/* Do not fold multiple figures in examples into two column layout. */ +img.sphx-glr-multi-img { + max-width: 100%; } -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; +table.property-table th, +table.property-table td { + padding: 4px 10px; } -.refcount { - color: #060; -} - -dt:target, -.highlight { - background-color: #ffffee; +/* Fix selection of parameter names; remove when fixed in the theme + * https://github.com/sphinx-doc/sphinx/pull/9763 + */ +.classifier:before { + display: inline-block; + margin: 0 0.5em; } -dl.method, dl.attribute { - border-top: 1px solid #aaa; +/* Make the code examples in the API reference index the same height. */ +.api-interface-example pre { + min-height: 6.5rem; } -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; +/* Make inheritance images have a scroll bar if necessary. */ +div.graphviz { + border: 1px solid lightgrey; + max-height: 50em; + overflow: auto; } - -pre a { - color: inherit; - text-decoration: none; +img.graphviz.inheritance { + max-width: none; } -.first { - margin-top: 0 !important; +/* Make tables in notes horizontally scrollable if too large. */ +div.wide-table { + overflow-x: auto; } -div.document { - background-color: white; - text-align: left; - background-image: url(contents.png); - background-repeat: repeat-x; +div.wide-table table th.stub { + background-color: var(--pst-color-background); + background-clip: padding-box; + left: 0; + position: sticky; } -/* -div.documentwrapper { - width: 100%; -} -*/ +.imrot-img { + display: flex; + margin: auto; + max-width:15em; + align-self: center; + } -div.clearer { - clear: both; -} + .imrot-cap { + text-align: center; + font-style: italic; + font-size: large; + } -div.related h3 { - display: none; -} -div.related ul { - background-image: url(navigation.png); - height: 2em; +.checklist { list-style: none; - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 0; - padding-left: 10px; -} - -div.related ul li { - margin: 0; padding: 0; - height: 2em; - float: left; -} - -div.related ul li.right { - float: right; - margin-right: 5px; -} - -div.related ul li a { margin: 0; - padding: 0 5px 0 5px; - line-height: 1.75em; - color: #EE9816; } - -div.related ul li a:hover { - color: #3CA8E7; +.checklist li { + margin-left: 24px; + padding-left: 23px; + margin-right: 6px; } - -div.body { - margin: 0; - padding: 0.5em 20px 20px 20px; -} - -div.bodywrapper { - margin: 0 240px 0 0; - border-right: 1px solid #ccc; -} - -div.sphinxsidebar { - margin: 0; - padding: 0.5em 15px 15px 0; - width: 210px; - float: right; - text-align: left; -/* margin-left: -100%; */ -} -div.sphinxsidebarwrapper { - padding-top: 28px -} - -div.sphinxsidebar h4, div.sphinxsidebar h3 { - margin: 1em 0 0.5em 0; - font-size: 0.9em; - padding: 0.1em 0 0.1em 0.5em; - color: white; - border: 1px solid #86989B; - background-color: #AFC1C4; -} - -div.sphinxsidebar ul { - padding-left: 1.5em; - margin-top: 7px; - list-style: none; - padding: 0; - line-height: 130%; +.checklist li:before { + content: "\2610\2001"; + margin-left: -24px; } - -div.sphinxsidebar ul ul { - list-style: square; - margin-left: 20px; -} - -p { - margin: 0.8em 0 0.8em 0; -} - -p.rubric { - font-weight: bold; -} - -h1 { - margin: 0.5em 0em; - padding-top: 0.5em; - font-size: 2em; - color: #11557C; -} - -h2 { - margin: 0.5em 0 0.2em 0; - padding-top: 0.5em; - font-size: 1.7em; - padding: 0; -} - -h3 { - margin: 0.2em 0 0.1em 0; - font-size: 1.2em; -} - -h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { - color: black!important; -} - -h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { - display: none; - margin: 0 0 0 0.3em; - padding: 0 0.2em 0 0.2em; - color: #aaa!important; -} - -h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, -h5:hover a.anchor, h6:hover a.anchor { +.checklist li p { display: inline; } -h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, -h5 a.anchor:hover, h6 a.anchor:hover { - color: #777; - background-color: #eee; -} - -table { - border-collapse: collapse; - margin: 0 -0.5em 0 -0.5em; -} - -table td, table th { - padding: 0.2em 0.5em 0.2em 0.5em; -} - -div.footer { - background-color: #E3EFF1; - color: #86989B; - padding: 3px 8px 3px 0; - clear: both; - font-size: 0.8em; - text-align: right; -} - -div.footer a { - color: #86989B; - text-decoration: underline; -} - -div.pagination { - margin-top: 2em; - padding-top: 0.5em; - border-top: 1px solid black; - text-align: center; -} - -div.sphinxsidebar ul.toc { - margin: 1em 0 1em 0; - padding: 0 0 0 0.5em; - list-style: none; -} - -div.sphinxsidebar ul.toc li { - margin: 0.5em 0 0.5em 0; - font-size: 0.9em; - line-height: 130%; -} - -div.sphinxsidebar ul.toc li p { - margin: 0; - padding: 0; -} - -div.sphinxsidebar ul.toc ul { - margin: 0.2em 0 0.2em 0; - padding: 0 0 0 1.8em; -} - -div.sphinxsidebar ul.toc ul li { - padding: 0; -} - -div.admonition, div.warning { - font-size: 0.9em; -} - -div.admonition p, div.warning p { - margin: 0.5em 1em 0.5em 1em; - padding: 0; -} - -div.admonition pre, div.warning pre { - margin: 0.4em 1em 0.4em 1em; -} - -div.admonition p.admonition-title, -div.warning p.admonition-title { - margin: 0; - font-weight: bold; - font-size: 14px; -} +/* sdd is a custom class that strips out styling from dropdowns + * Example usage: + * + * .. dropdown:: + * :class-container: sdd + * + */ -div.warning { - border: 1px solid #940000; +.sdd.sd-dropdown { + box-shadow: none!important; } -div.warning p.admonition-title { - background-color: #CF0000; - border-bottom-color: #940000; +.sdd.sd-dropdown.sd-card{ + border-style: solid !important; + border-color: var(--pst-color-border) !important; + border-width: thin !important; + border-radius: .05 } -div.admonition ul, div.admonition ol, -div.warning ul, div.warning ol { - margin: 0.1em 0.5em 0.5em 3em; - padding: 0; +.sdd.sd-dropdown .sd-card-header{ + --pst-sd-dropdown-color: none; } -div.versioninfo { - margin: 1em 0 0 0; - border: 1px solid #ccc; - background-color: #DDEAF0; - padding: 8px; - line-height: 1.3em; - font-size: 0.9em; +.sdd.sd-dropdown .sd-card-header +.sd-card-body{ + --pst-sd-dropdown-color: none; } - -a.headerlink { - color: #c60f0f!important; - font-size: 1em; - margin-left: 6px; - padding: 0 4px 0 4px; - text-decoration: none!important; - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; +/* section-toc is a custom class that removes the page title from a toctree listing + * and shifts the resulting list left + * Example usage: + * + * .. rst-class:: section-toc + * .. toctree:: + * + */ + .section-toc.toctree-wrapper .toctree-l1>a{ + display: none; } - -a.headerlink:hover { - background-color: #ccc; - color: white!important; +.section-toc.toctree-wrapper .toctree-l1>ul{ + padding-left: 0; } -table.indextable td { - text-align: left; - vertical-align: top; +.sidebar-cheatsheets { + margin-bottom: 3em; } -table.indextable dl, table.indextable dd { +.sidebar-cheatsheets > h3 { margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -img.inheritance { - border: 0px -} - -form.pfform { - margin: 10px 0 20px 0; -} - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -table.docutils { - border-spacing: 2px; - border-collapse: collapse; - border-top-width: 1px; - border-right-width: 0px; - border-bottom-width: 1px; - border-left-width: 0px; -} - -/* module summary table */ -.longtable.docutils { - font-size: 12px; - margin-bottom: 30px; -} -.longtable.docutils, .longtable.docutils td { - border-color: #ccc; -} - -.longtable.docutils tr.row-even{ - background-color: #eff3f4; -} - -/* function and class description */ -.descclassname { - color: #aaa; - font-weight: normal; - font-family: monospace; -} -.descname { - font-family: monospace; -} - - -table.docutils th { - padding: 1px 8px 1px 5px; - background-color: #eee; - width: 100px; -} - -table.docutils td { - border-width: 1px 0 1px 0; -} - - -dl.class em, dl.function em, dl.class big, dl.function big { - font-weight: normal; - font-family: monospace; -} - -dl.class dd, dl.function dd { - padding: 10px; -} - -/* function and class description */ -dl.function, dl.method, dl.attribute { - border-top: 1px solid #ccc; - padding-top: 6px; } -dl.function { - border-top: 1px solid #888; - margin-top: 15px; -} - -dl.class { - padding-top: 6px; - margin-top: 15px; -} - -.descclassname { - color: #aaa; - font-weight: normal; - font-family: monospace; -} -.descname { - font-family: monospace; -} - - -.docutils.field-list th { - background-color: #eee; - padding: 10px; - text-align: left; - vertical-align: top; - width: 120px; -} -.docutils.field-list td { - padding: 10px 10px 10px 20px; - text-align: left; - vertical-align: top; -} -.docutils.field-list td blockquote p { - font-size: 13px; - line-height: 18px; -} -p.rubric { - font-weight: bold; - font-size: 19px; - margin: 15px 0 10px 0; -} -p.admonition-title { - font-weight: bold; - text-decoration: underline; -} - - -#matplotlib-examples ul li{ - font-size: large; -} - -#matplotlib-examples ul li ul{ - margin-bottom:20px; - overflow:hidden; - border-top:1px solid #ccc; -} - -#matplotlib-examples ul li ul li { - font-size: small; - line-height:1.75em; - display:inline; - float: left; - width: 22em; -} - -#overview ul li ul{ - margin-bottom:20px; - overflow:hidden; - border-top:1px solid #ccc; -} - -#overview ul li ul li { - display:inline; - float: left; - width: 30em; -} - -figure { - margin: 1em; - display: inline-block; -} - -figure img { - margin-left: auto; - margin-right: auto; -} - -figcaption { - text-align: center; -} - - -.donate_button { - background:#11557C; - font-weight:normal; - border:solid 1px #fff; - outline: solid 1px #11557C; - clear: both; - display: block; - width:200px; - line-height:2.8; - font-size: 16px; - text-align: center; - cursor:pointer; - color:#fff; - text-decoration: none; - margin: 30px auto 0; - z-index:1; - transition: background .25s ease; -} - -.donate_button:last-of-type { - margin: 15px auto 30px; - - -} - -.donate_button:hover, .donate_button:active, .donate_button:focus { - background: #003c63; - outline-color: #003c63; -} - - -div.responsive_screenshots { - /* Horizontally centered */ - display: block; - margin: auto; - - /* Do not go beyond 1:1 scale (and ensure a 1x4 tight layout) */ - max-width: 648px; /* at most 4 x 1:1 subfig width */ - max-height: 139px; /* at most 1 x 1:1 subfig height */ -} - -/* To avoid subfigure parts outside of the responsive_screenshots */ -/* element (see: https://stackoverflow.com/questions/2062258/ */ -/* floating-stuff-within-a-div-floats-outside-of-div-why) */ -span.clear_screenshots { clear: left; display: block; } - -div.responsive_subfig{ - float: left; - width: 25%; /* we want 4 subfigs in a row */ - - /* Include content, padding and border in width. This should */ - /* avoid having to use tricks like "width: 24.9999%" */ - box-sizing: border-box; -} - -div.responsive_subfig img { - /* Horizontally centered */ - display: block; - margin: auto; - - /* Possible downscaling */ - max-width: 162px; /* at most 1 x 1:1 subfig width */ - max-height: 139px; /* at most 1 x 1:1 subfig height */ - +.sidebar-cheatsheets > img { width: 100%; } - -@media only screen and (max-width: 1000px){ - /* The value of 1000px was handcrafted to provide a more or less */ - /* smooth transition between the 1x4 and the 2x2 layouts. It is */ - /* NB: it is slightly below 1024px: so one should still have a */ - /* row in a 1024x768 window */ - - div.responsive_screenshots { - /* Do not go beyond 1:1 scale (and ensure a 2x2 tight layout) */ - max-width: 324px; /* at most 2 x 1:1 subfig width */ - max-height: 278px; /* at most 2 x 1:1 subfig height */ - } - - div.responsive_subfig { - width: 50%; /* we want 2 subfigs in a row */ - } - -} diff --git a/doc/_static/mpl_cheatsheet1.png b/doc/_static/mpl_cheatsheet1.png new file mode 100644 index 000000000000..5b637f29e32c Binary files /dev/null and b/doc/_static/mpl_cheatsheet1.png differ diff --git a/doc/_static/mpl_cheatsheet1_2x.png b/doc/_static/mpl_cheatsheet1_2x.png new file mode 100644 index 000000000000..765ff32d2f83 Binary files /dev/null and b/doc/_static/mpl_cheatsheet1_2x.png differ diff --git a/doc/_static/mplot3d_view_angles.png b/doc/_static/mplot3d_view_angles.png new file mode 100644 index 000000000000..16d3c2f0d699 Binary files /dev/null and b/doc/_static/mplot3d_view_angles.png differ diff --git a/doc/_static/navigation.png b/doc/_static/navigation.png deleted file mode 100644 index 1081dc1439fb..000000000000 Binary files a/doc/_static/navigation.png and /dev/null differ diff --git a/doc/_static/numfocus_badge.png b/doc/_static/numfocus_badge.png deleted file mode 100644 index b8d8e6ca838f..000000000000 Binary files a/doc/_static/numfocus_badge.png and /dev/null differ diff --git a/doc/_static/pgf_fonts.pdf b/doc/_static/pgf_fonts.pdf deleted file mode 100644 index 9f9bf0bae67d..000000000000 Binary files a/doc/_static/pgf_fonts.pdf and /dev/null differ diff --git a/doc/_static/pgf_fonts.png b/doc/_static/pgf_fonts.png deleted file mode 100644 index d4ef689f9b33..000000000000 Binary files a/doc/_static/pgf_fonts.png and /dev/null differ diff --git a/doc/_static/pgf_texsystem.pdf b/doc/_static/pgf_texsystem.pdf deleted file mode 100644 index fbae0ea766ff..000000000000 Binary files a/doc/_static/pgf_texsystem.pdf and /dev/null differ diff --git a/doc/_static/pgf_texsystem.png b/doc/_static/pgf_texsystem.png deleted file mode 100644 index 6075e7b764dd..000000000000 Binary files a/doc/_static/pgf_texsystem.png and /dev/null differ diff --git a/doc/_static/probscale_demo.png b/doc/_static/probscale_demo.png deleted file mode 100644 index 55d8df2d08bf..000000000000 Binary files a/doc/_static/probscale_demo.png and /dev/null differ diff --git a/doc/_static/quiver_sizes.svg b/doc/_static/quiver_sizes.svg new file mode 100644 index 000000000000..afba2c601d09 --- /dev/null +++ b/doc/_static/quiver_sizes.svg @@ -0,0 +1,429 @@ + + + + + + + + + + image/svg+xml + + + + + + + + width + + + + + + + + + + + + + + + + + + + headaxislength + headlength + + + + + + + + + + + + headwidth + + + + + + + + + length + + + + + + + + + diff --git a/doc/_static/readme_preview.png b/doc/_static/readme_preview.png new file mode 100644 index 000000000000..f7e6b7833508 Binary files /dev/null and b/doc/_static/readme_preview.png differ diff --git a/doc/_static/seaborn.png b/doc/_static/seaborn.png deleted file mode 100644 index d1e815ef89b6..000000000000 Binary files a/doc/_static/seaborn.png and /dev/null differ diff --git a/doc/_static/stix_fontset.png b/doc/_static/stix_fontset.png deleted file mode 100644 index ed1815274cea..000000000000 Binary files a/doc/_static/stix_fontset.png and /dev/null differ diff --git a/doc/_static/stixsans_fontset.png b/doc/_static/stixsans_fontset.png deleted file mode 100644 index 62226b6c3067..000000000000 Binary files a/doc/_static/stixsans_fontset.png and /dev/null differ diff --git a/doc/_static/surface3d_frontpage.png b/doc/_static/surface3d_frontpage.png deleted file mode 100644 index 613e391bc5b3..000000000000 Binary files a/doc/_static/surface3d_frontpage.png and /dev/null differ diff --git a/doc/_static/switcher.json b/doc/_static/switcher.json new file mode 100644 index 000000000000..36e743db21b8 --- /dev/null +++ b/doc/_static/switcher.json @@ -0,0 +1,53 @@ +[ + { + "name": "3.10 (stable)", + "version": "3.10.8", + "url": "https://matplotlib.org/stable/", + "preferred": true + }, + { + "name": "3.11 (dev)", + "version": "dev", + "url": "https://matplotlib.org/devdocs/" + }, + { + "name": "3.9", + "version": "3.9.3", + "url": "https://matplotlib.org/3.9.3/" + }, + { + "name": "3.8", + "version": "3.8.4", + "url": "https://matplotlib.org/3.8.4/" + }, + { + "name": "3.7", + "version": "3.7.5", + "url": "https://matplotlib.org/3.7.5/" + }, + { + "name": "3.6", + "version": "3.6.3", + "url": "https://matplotlib.org/3.6.3/" + }, + { + "name": "3.5", + "version": "3.5.3", + "url": "https://matplotlib.org/3.5.3/" + }, + { + "name": "3.4", + "version": "3.4.3", + "url": "https://matplotlib.org/3.4.3/" + }, + { + "name": "3.3", + "version": "3.3.4", + "url": "https://matplotlib.org/3.3.4/" + }, + { + "name": "2.2", + "version": "2.2.4", + "url": "https://matplotlib.org/2.2.4/" + } +] diff --git a/doc/_static/transforms.png b/doc/_static/transforms.png deleted file mode 100644 index ab07fb575961..000000000000 Binary files a/doc/_static/transforms.png and /dev/null differ diff --git a/doc/_static/zenodo_cache/1004650.svg b/doc/_static/zenodo_cache/1004650.svg new file mode 100644 index 000000000000..8d70568301a7 --- /dev/null +++ b/doc/_static/zenodo_cache/1004650.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1004650 + + + 10.5281/zenodo.1004650 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/10059757.svg b/doc/_static/zenodo_cache/10059757.svg new file mode 100644 index 000000000000..d5909613dd75 --- /dev/null +++ b/doc/_static/zenodo_cache/10059757.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10059757 + + + 10.5281/zenodo.10059757 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/10150955.svg b/doc/_static/zenodo_cache/10150955.svg new file mode 100644 index 000000000000..132bc97ab61d --- /dev/null +++ b/doc/_static/zenodo_cache/10150955.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10150955 + + + 10.5281/zenodo.10150955 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/10661079.svg b/doc/_static/zenodo_cache/10661079.svg new file mode 100644 index 000000000000..ac659bcc870f --- /dev/null +++ b/doc/_static/zenodo_cache/10661079.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10661079 + + + 10.5281/zenodo.10661079 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/10916799.svg b/doc/_static/zenodo_cache/10916799.svg new file mode 100644 index 000000000000..ca9c0a454251 --- /dev/null +++ b/doc/_static/zenodo_cache/10916799.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.10916799 + + + 10.5281/zenodo.10916799 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1098480.svg b/doc/_static/zenodo_cache/1098480.svg new file mode 100644 index 000000000000..93eb714978e4 --- /dev/null +++ b/doc/_static/zenodo_cache/1098480.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1098480 + + + 10.5281/zenodo.1098480 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/11201097.svg b/doc/_static/zenodo_cache/11201097.svg new file mode 100644 index 000000000000..70f35a7a659f --- /dev/null +++ b/doc/_static/zenodo_cache/11201097.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.11201097 + + + 10.5281/zenodo.11201097 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/11451.svg b/doc/_static/zenodo_cache/11451.svg new file mode 100644 index 000000000000..87edde75d917 --- /dev/null +++ b/doc/_static/zenodo_cache/11451.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.11451 + + + 10.5281/zenodo.11451 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1154287.svg b/doc/_static/zenodo_cache/1154287.svg new file mode 100644 index 000000000000..e19917debda9 --- /dev/null +++ b/doc/_static/zenodo_cache/1154287.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1154287 + + + 10.5281/zenodo.1154287 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1189358.svg b/doc/_static/zenodo_cache/1189358.svg new file mode 100644 index 000000000000..2792f3ef69b4 --- /dev/null +++ b/doc/_static/zenodo_cache/1189358.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1189358 + + + 10.5281/zenodo.1189358 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1202050.svg b/doc/_static/zenodo_cache/1202050.svg new file mode 100644 index 000000000000..45c04ceb3f8f --- /dev/null +++ b/doc/_static/zenodo_cache/1202050.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1202050 + + + 10.5281/zenodo.1202050 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1202077.svg b/doc/_static/zenodo_cache/1202077.svg new file mode 100644 index 000000000000..ec73136ad802 --- /dev/null +++ b/doc/_static/zenodo_cache/1202077.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1202077 + + + 10.5281/zenodo.1202077 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/12287.svg b/doc/_static/zenodo_cache/12287.svg new file mode 100644 index 000000000000..799bcddc4fbc --- /dev/null +++ b/doc/_static/zenodo_cache/12287.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.12287 + + + 10.5281/zenodo.12287 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/12400.svg b/doc/_static/zenodo_cache/12400.svg new file mode 100644 index 000000000000..82cdfe33b7e2 --- /dev/null +++ b/doc/_static/zenodo_cache/12400.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.12400 + + + 10.5281/zenodo.12400 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/12652732.svg b/doc/_static/zenodo_cache/12652732.svg new file mode 100644 index 000000000000..cde5c5f37839 --- /dev/null +++ b/doc/_static/zenodo_cache/12652732.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.12652732 + + + 10.5281/zenodo.12652732 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/13308876.svg b/doc/_static/zenodo_cache/13308876.svg new file mode 100644 index 000000000000..749bc3c19026 --- /dev/null +++ b/doc/_static/zenodo_cache/13308876.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.13308876 + + + 10.5281/zenodo.13308876 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1343133.svg b/doc/_static/zenodo_cache/1343133.svg new file mode 100644 index 000000000000..32a2f172ea87 --- /dev/null +++ b/doc/_static/zenodo_cache/1343133.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1343133 + + + 10.5281/zenodo.1343133 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1420605.svg b/doc/_static/zenodo_cache/1420605.svg new file mode 100644 index 000000000000..1655f9f66373 --- /dev/null +++ b/doc/_static/zenodo_cache/1420605.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1420605 + + + 10.5281/zenodo.1420605 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14249941.svg b/doc/_static/zenodo_cache/14249941.svg new file mode 100644 index 000000000000..f9165f17fdf0 --- /dev/null +++ b/doc/_static/zenodo_cache/14249941.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14249941 + + + 10.5281/zenodo.14249941 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14436121.svg b/doc/_static/zenodo_cache/14436121.svg new file mode 100644 index 000000000000..1e4a7cd5b7a4 --- /dev/null +++ b/doc/_static/zenodo_cache/14436121.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14436121 + + + 10.5281/zenodo.14436121 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14464227.svg b/doc/_static/zenodo_cache/14464227.svg new file mode 100644 index 000000000000..7126d239d6a5 --- /dev/null +++ b/doc/_static/zenodo_cache/14464227.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14464227 + + + 10.5281/zenodo.14464227 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1482098.svg b/doc/_static/zenodo_cache/1482098.svg new file mode 100644 index 000000000000..ba7adb122829 --- /dev/null +++ b/doc/_static/zenodo_cache/1482098.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1482098 + + + 10.5281/zenodo.1482098 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/1482099.svg b/doc/_static/zenodo_cache/1482099.svg new file mode 100644 index 000000000000..2f9155ddb267 --- /dev/null +++ b/doc/_static/zenodo_cache/1482099.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.1482099 + + + 10.5281/zenodo.1482099 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/14940554.svg b/doc/_static/zenodo_cache/14940554.svg new file mode 100644 index 000000000000..6e7d5c37bf7b --- /dev/null +++ b/doc/_static/zenodo_cache/14940554.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.14940554 + + + 10.5281/zenodo.14940554 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/15375714.svg b/doc/_static/zenodo_cache/15375714.svg new file mode 100644 index 000000000000..d5e403138561 --- /dev/null +++ b/doc/_static/zenodo_cache/15375714.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.15375714 + + + 10.5281/zenodo.15375714 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/15423.svg b/doc/_static/zenodo_cache/15423.svg new file mode 100644 index 000000000000..bec3f657cf0c --- /dev/null +++ b/doc/_static/zenodo_cache/15423.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.15423 + + + 10.5281/zenodo.15423 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/16644850.svg b/doc/_static/zenodo_cache/16644850.svg new file mode 100644 index 000000000000..89910032da4e --- /dev/null +++ b/doc/_static/zenodo_cache/16644850.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.16644850 + + + 10.5281/zenodo.16644850 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/16999430.svg b/doc/_static/zenodo_cache/16999430.svg new file mode 100644 index 000000000000..44c448643e91 --- /dev/null +++ b/doc/_static/zenodo_cache/16999430.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.16999430 + + + 10.5281/zenodo.16999430 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/17298696.svg b/doc/_static/zenodo_cache/17298696.svg new file mode 100644 index 000000000000..9aa8d7c94349 --- /dev/null +++ b/doc/_static/zenodo_cache/17298696.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.17298696 + + + 10.5281/zenodo.17298696 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/248351.svg b/doc/_static/zenodo_cache/248351.svg new file mode 100644 index 000000000000..e8e38ac9c1be --- /dev/null +++ b/doc/_static/zenodo_cache/248351.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.248351 + + + 10.5281/zenodo.248351 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/2577644.svg b/doc/_static/zenodo_cache/2577644.svg new file mode 100644 index 000000000000..492bbbbc60cf --- /dev/null +++ b/doc/_static/zenodo_cache/2577644.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.2577644 + + + 10.5281/zenodo.2577644 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/2669103.svg b/doc/_static/zenodo_cache/2669103.svg new file mode 100644 index 000000000000..fef871d56e50 --- /dev/null +++ b/doc/_static/zenodo_cache/2669103.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.2669103 + + + 10.5281/zenodo.2669103 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/2893252.svg b/doc/_static/zenodo_cache/2893252.svg new file mode 100644 index 000000000000..2e39a0b456b1 --- /dev/null +++ b/doc/_static/zenodo_cache/2893252.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.2893252 + + + 10.5281/zenodo.2893252 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3264781.svg b/doc/_static/zenodo_cache/3264781.svg new file mode 100644 index 000000000000..7924a7dcaa22 --- /dev/null +++ b/doc/_static/zenodo_cache/3264781.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3264781 + + + 10.5281/zenodo.3264781 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/32914.svg b/doc/_static/zenodo_cache/32914.svg new file mode 100644 index 000000000000..0656fd8b062b --- /dev/null +++ b/doc/_static/zenodo_cache/32914.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.32914 + + + 10.5281/zenodo.32914 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3563226.svg b/doc/_static/zenodo_cache/3563226.svg new file mode 100644 index 000000000000..4731dfab137a --- /dev/null +++ b/doc/_static/zenodo_cache/3563226.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3563226 + + + 10.5281/zenodo.3563226 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3633833.svg b/doc/_static/zenodo_cache/3633833.svg new file mode 100644 index 000000000000..34a894f0ccc6 --- /dev/null +++ b/doc/_static/zenodo_cache/3633833.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3633833 + + + 10.5281/zenodo.3633833 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3633844.svg b/doc/_static/zenodo_cache/3633844.svg new file mode 100644 index 000000000000..a3e6b7724224 --- /dev/null +++ b/doc/_static/zenodo_cache/3633844.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3633844 + + + 10.5281/zenodo.3633844 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3695547.svg b/doc/_static/zenodo_cache/3695547.svg new file mode 100644 index 000000000000..b0bdfe3ba830 --- /dev/null +++ b/doc/_static/zenodo_cache/3695547.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3695547 + + + 10.5281/zenodo.3695547 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3714460.svg b/doc/_static/zenodo_cache/3714460.svg new file mode 100644 index 000000000000..07e433ea0313 --- /dev/null +++ b/doc/_static/zenodo_cache/3714460.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3714460 + + + 10.5281/zenodo.3714460 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3898017.svg b/doc/_static/zenodo_cache/3898017.svg new file mode 100644 index 000000000000..b435f0e8316a --- /dev/null +++ b/doc/_static/zenodo_cache/3898017.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3898017 + + + 10.5281/zenodo.3898017 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3948793.svg b/doc/_static/zenodo_cache/3948793.svg new file mode 100644 index 000000000000..f95c418b3e8b --- /dev/null +++ b/doc/_static/zenodo_cache/3948793.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3948793 + + + 10.5281/zenodo.3948793 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/3984190.svg b/doc/_static/zenodo_cache/3984190.svg new file mode 100644 index 000000000000..bb548f560222 --- /dev/null +++ b/doc/_static/zenodo_cache/3984190.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.3984190 + + + 10.5281/zenodo.3984190 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4030140.svg b/doc/_static/zenodo_cache/4030140.svg new file mode 100644 index 000000000000..8fcb71dead83 --- /dev/null +++ b/doc/_static/zenodo_cache/4030140.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4030140 + + + 10.5281/zenodo.4030140 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4268928.svg b/doc/_static/zenodo_cache/4268928.svg new file mode 100644 index 000000000000..e7d632a40e54 --- /dev/null +++ b/doc/_static/zenodo_cache/4268928.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4268928 + + + 10.5281/zenodo.4268928 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/44579.svg b/doc/_static/zenodo_cache/44579.svg new file mode 100644 index 000000000000..4e5854a3e770 --- /dev/null +++ b/doc/_static/zenodo_cache/44579.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.44579 + + + 10.5281/zenodo.44579 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4475376.svg b/doc/_static/zenodo_cache/4475376.svg new file mode 100644 index 000000000000..bf223b01ddc3 --- /dev/null +++ b/doc/_static/zenodo_cache/4475376.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4475376 + + + 10.5281/zenodo.4475376 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4638398.svg b/doc/_static/zenodo_cache/4638398.svg new file mode 100644 index 000000000000..8b50f14790dd --- /dev/null +++ b/doc/_static/zenodo_cache/4638398.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4638398 + + + 10.5281/zenodo.4638398 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4649959.svg b/doc/_static/zenodo_cache/4649959.svg new file mode 100644 index 000000000000..a604de20cdd5 --- /dev/null +++ b/doc/_static/zenodo_cache/4649959.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4649959 + + + 10.5281/zenodo.4649959 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/4743323.svg b/doc/_static/zenodo_cache/4743323.svg new file mode 100644 index 000000000000..204cf037ad27 --- /dev/null +++ b/doc/_static/zenodo_cache/4743323.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.4743323 + + + 10.5281/zenodo.4743323 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5194481.svg b/doc/_static/zenodo_cache/5194481.svg new file mode 100644 index 000000000000..728ae0c15a6a --- /dev/null +++ b/doc/_static/zenodo_cache/5194481.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5194481 + + + 10.5281/zenodo.5194481 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/56926.svg b/doc/_static/zenodo_cache/56926.svg new file mode 100644 index 000000000000..5358db519e44 --- /dev/null +++ b/doc/_static/zenodo_cache/56926.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.56926 + + + 10.5281/zenodo.56926 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/570311.svg b/doc/_static/zenodo_cache/570311.svg new file mode 100644 index 000000000000..289b4f407a9b --- /dev/null +++ b/doc/_static/zenodo_cache/570311.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.570311 + + + 10.5281/zenodo.570311 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5706396.svg b/doc/_static/zenodo_cache/5706396.svg new file mode 100644 index 000000000000..54718543c9c8 --- /dev/null +++ b/doc/_static/zenodo_cache/5706396.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5706396 + + + 10.5281/zenodo.5706396 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/573577.svg b/doc/_static/zenodo_cache/573577.svg new file mode 100644 index 000000000000..5aea1629ed35 --- /dev/null +++ b/doc/_static/zenodo_cache/573577.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.573577 + + + 10.5281/zenodo.573577 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/5773480.svg b/doc/_static/zenodo_cache/5773480.svg new file mode 100644 index 000000000000..431dbd803973 --- /dev/null +++ b/doc/_static/zenodo_cache/5773480.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.5773480 + + + 10.5281/zenodo.5773480 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/61948.svg b/doc/_static/zenodo_cache/61948.svg new file mode 100644 index 000000000000..8761c190e8f1 --- /dev/null +++ b/doc/_static/zenodo_cache/61948.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.61948 + + + 10.5281/zenodo.61948 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/6513224.svg b/doc/_static/zenodo_cache/6513224.svg new file mode 100644 index 000000000000..fd54dfcb9abb --- /dev/null +++ b/doc/_static/zenodo_cache/6513224.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.6513224 + + + 10.5281/zenodo.6513224 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/6982547.svg b/doc/_static/zenodo_cache/6982547.svg new file mode 100644 index 000000000000..6eb000d892da --- /dev/null +++ b/doc/_static/zenodo_cache/6982547.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.6982547 + + + 10.5281/zenodo.6982547 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7084615.svg b/doc/_static/zenodo_cache/7084615.svg new file mode 100644 index 000000000000..9bb362063414 --- /dev/null +++ b/doc/_static/zenodo_cache/7084615.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7084615 + + + 10.5281/zenodo.7084615 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7162185.svg b/doc/_static/zenodo_cache/7162185.svg new file mode 100644 index 000000000000..ea0966377194 --- /dev/null +++ b/doc/_static/zenodo_cache/7162185.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7162185 + + + 10.5281/zenodo.7162185 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7275322.svg b/doc/_static/zenodo_cache/7275322.svg new file mode 100644 index 000000000000..2d0fd408b504 --- /dev/null +++ b/doc/_static/zenodo_cache/7275322.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7275322 + + + 10.5281/zenodo.7275322 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7527665.svg b/doc/_static/zenodo_cache/7527665.svg new file mode 100644 index 000000000000..3c3e0b7a8b2a --- /dev/null +++ b/doc/_static/zenodo_cache/7527665.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7527665 + + + 10.5281/zenodo.7527665 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7637593.svg b/doc/_static/zenodo_cache/7637593.svg new file mode 100644 index 000000000000..4e91dea5e805 --- /dev/null +++ b/doc/_static/zenodo_cache/7637593.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7637593 + + + 10.5281/zenodo.7637593 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/7697899.svg b/doc/_static/zenodo_cache/7697899.svg new file mode 100644 index 000000000000..b540a1909046 --- /dev/null +++ b/doc/_static/zenodo_cache/7697899.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.7697899 + + + 10.5281/zenodo.7697899 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/8118151.svg b/doc/_static/zenodo_cache/8118151.svg new file mode 100644 index 000000000000..e9d33ec5bf34 --- /dev/null +++ b/doc/_static/zenodo_cache/8118151.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.8118151 + + + 10.5281/zenodo.8118151 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/8336761.svg b/doc/_static/zenodo_cache/8336761.svg new file mode 100644 index 000000000000..24c222a8a5f5 --- /dev/null +++ b/doc/_static/zenodo_cache/8336761.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.8336761 + + + 10.5281/zenodo.8336761 + + + \ No newline at end of file diff --git a/doc/_static/zenodo_cache/8347255.svg b/doc/_static/zenodo_cache/8347255.svg new file mode 100644 index 000000000000..318d9e6bea73 --- /dev/null +++ b/doc/_static/zenodo_cache/8347255.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + DOI + + + DOI + + + 10.5281/zenodo.8347255 + + + 10.5281/zenodo.8347255 + + + \ No newline at end of file diff --git a/doc/_templates/automodule.rst b/doc/_templates/automodule.rst new file mode 100644 index 000000000000..df3e8283f2f6 --- /dev/null +++ b/doc/_templates/automodule.rst @@ -0,0 +1,37 @@ +{{ fullname | escape | underline}} + +.. automodule:: {{ fullname }} + :no-members: + :no-inherited-members: + +{% block classes %} +{% if classes %} + +Classes +------- + +.. autosummary:: + :template: autosummary.rst + :toctree: + +{% for item in classes %}{% if item not in ['zip', 'map', 'reduce'] %} + {{ item }}{% endif %}{% endfor %} + +{% endif %} +{% endblock %} + +{% block functions %} +{% if functions %} + +Functions +--------- + +.. autosummary:: + :template: autosummary.rst + :toctree: + +{% for item in functions %}{% if item not in ['zip', 'map', 'reduce'] %} + {{ item }}{% endif %}{% endfor %} + +{% endif %} +{% endblock %} diff --git a/doc/_templates/autosummary.rst b/doc/_templates/autosummary.rst new file mode 100644 index 000000000000..824dbe5b9a4b --- /dev/null +++ b/doc/_templates/autosummary.rst @@ -0,0 +1,31 @@ +{{ fullname | escape | underline }} + + +.. currentmodule:: {{ module }} + + +{% if objtype in ['class'] %} + +.. auto{{ objtype }}:: {{ objname }} + :show-inheritance: + :special-members: __call__ + +{% else %} +.. auto{{ objtype }}:: {{ objname }} + +{% endif %} + +{% if objtype in ['class', 'method', 'function'] %} +{% if objname in ['AxesGrid', 'Scalable', 'HostAxes', 'FloatingAxes', +'ParasiteAxesAuxTrans', 'ParasiteAxes'] %} + +.. Filter out the above aliases to other classes, as sphinx gallery + creates no example file for those (sphinx-gallery/sphinx-gallery#365) + +{% else %} + +.. minigallery:: {{module}}.{{objname}} + :add-heading: + +{% endif %} +{% endif %} diff --git a/doc/_templates/autosummary_class_only.rst b/doc/_templates/autosummary_class_only.rst new file mode 100644 index 000000000000..d10f1b375fd3 --- /dev/null +++ b/doc/_templates/autosummary_class_only.rst @@ -0,0 +1,12 @@ +{{ fullname | escape | underline }} + + +.. currentmodule:: {{ module }} + + +{% if objtype in ['class'] %} + +.. auto{{ objtype }}:: {{ objname }} + :no-members: + +{% endif %} diff --git a/doc/_templates/badgesidebar.html b/doc/_templates/badgesidebar.html deleted file mode 100644 index 7db65edfcdc4..000000000000 --- a/doc/_templates/badgesidebar.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - -
- -Travis-CI: - - -
diff --git a/doc/_templates/cheatsheet_sidebar.html b/doc/_templates/cheatsheet_sidebar.html new file mode 100644 index 000000000000..2ca6548ddd4d --- /dev/null +++ b/doc/_templates/cheatsheet_sidebar.html @@ -0,0 +1,9 @@ + + diff --git a/doc/_templates/citing.html b/doc/_templates/citing.html deleted file mode 100644 index 74f8af84a10a..000000000000 --- a/doc/_templates/citing.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "layout.html" %} -{% set title = "Citing matplotlib" %} -{% block body %} - -

Citing matplotlib

-

-If matplotlib contributes to a project that leads to a scientific publication, -please acknowledge this fact by citing the project. You can use this -BibTeX entry: -

-
-@Article{Hunter:2007,
-  Author    = {Hunter, J. D.},
-  Title     = {Matplotlib: A 2D graphics environment},
-  Journal   = {Computing In Science \& Engineering},
-  Volume    = {9},
-  Number    = {3},
-  Pages     = {90--95},
-  abstract  = {Matplotlib is a 2D graphics package used for Python
-  for application development, interactive scripting, and
-  publication-quality image generation across user
-  interfaces and operating systems.},
-  publisher = {IEEE COMPUTER SOC},
-  doi = {10.1109/MCSE.2007.55},
-  year      = 2007
-}
-
- - -

DOIs

-
-
v1.5.3
10.5281/zenodo.61948
-
v1.5.2
10.5281/zenodo.56926
-
v1.5.1
10.5281/zenodo.44579
-
v1.5.0
10.5281/zenodo.32914
-
v1.4.3
10.5281/zenodo.15423
-
v1.4.2
10.5281/zenodo.12400
-
v1.4.1
10.5281/zenodo.12287
-
v1.4.0
10.5281/zenodo.11451
-
- -{% endblock %} diff --git a/doc/_templates/donate_sidebar.html b/doc/_templates/donate_sidebar.html index 4cc2bf15a125..071c92888c3c 100644 --- a/doc/_templates/donate_sidebar.html +++ b/doc/_templates/donate_sidebar.html @@ -1,5 +1,5 @@ - -
- - + diff --git a/doc/_templates/empty_sidebar.html b/doc/_templates/empty_sidebar.html new file mode 100644 index 000000000000..2ebb29fad4dd --- /dev/null +++ b/doc/_templates/empty_sidebar.html @@ -0,0 +1 @@ + diff --git a/doc/_templates/function.rst b/doc/_templates/function.rst new file mode 100644 index 000000000000..0b0e8480d332 --- /dev/null +++ b/doc/_templates/function.rst @@ -0,0 +1,9 @@ +:mod:`{{module}}`.{{objname}} +{{ underline }}==================== + +.. currentmodule:: {{ module }} + +.. autofunction:: {{ objname }} + +.. minigallery:: {{module}}.{{objname}} + :add-heading: diff --git a/doc/_templates/index.html b/doc/_templates/index.html deleted file mode 100644 index b7f6653957ef..000000000000 --- a/doc/_templates/index.html +++ /dev/null @@ -1,240 +0,0 @@ -{% extends "layout.html" %} -{% set title = 'matplotlib: python plotting' %} - -{% block extrahead %} - - - {{ super() }} -{% endblock %} - -{% block body %} - -

Introduction

- -

matplotlib is a python 2D plotting library which produces - publication quality figures in a variety of hardcopy formats and - interactive environments across platforms. matplotlib can be used - in python scripts, the python and ipython shell (ala - MATLAB®* - or - Mathematica®), - web application servers, and six graphical user - interface toolkits.

- - - - -

matplotlib tries to make easy things easy and hard things possible. - You can generate plots, histograms, power spectra, bar charts, - errorcharts, scatterplots, etc, with just a few lines of code. - For a sampling, see the screenshots, thumbnail gallery, and - examples directory

- -

For simple plotting the pyplot interface provides a - MATLAB-like interface, particularly when combined - with IPython. For the power user, you have full control - of line styles, font properties, axes properties, etc, via an object - oriented interface or via a set of functions familiar to MATLAB - users.

- -
-

John Hunter (1968-2012)

- - - - - -
- - -

- On August 28 2012, John D. Hunter, the creator of matplotlib, died - from complications arising from cancer treatment, after a brief but - intense battle with this terrible illness. John is survived by his - wife Miriam, his three daughters Rahel, Ava and Clara, his sisters - Layne and Mary, and his mother Sarah.

- -

- If you have benefited from John's many contributions, please say - thanks in the way that would matter most to him. Please consider - making a donation to - the John Hunter Technology - Fellowship.

-
-
- -

Installation

- - Visit the matplotlib installation instructions. - -

Documentation

- - This is the documentation for matplotlib version {{ version }}. - -

- - -

Trying to learn how to do a particular kind of plot? Check out - the gallery, examples, - or the list of plotting - commands.

- -

Other learning resources

- -

There are many external learning - resources available including printed material, videos and tutorials.

- -

Need help?

- -

matplotlib is a welcoming, inclusive project, and we try to follow -the Python Software -Foundation Code of Conduct in everything we do.

- -

Check the faq, -the api docs, -mailing -list archives, and join the matplotlib -mailing lists Users, -Announce and -Devel. -Check out the matplotlib questions -on stackoverflow. -The search tool searches all of -the documentation, including full text search of over 350 complete -examples which exercise almost every corner of matplotlib.

- -

You can file bugs, patches and feature requests on the -github -tracker, -but it is a good idea to ping us on the mailing list too.

- -

To keep up to date with what's going on in matplotlib, see -the what's new -page or browse the source -code. Anything that could require changes to your existing code -is logged in the api -changes file.

- -

Toolkits

- -

Matplotlib ships with several add-on toolkits, - Including 3d plotting with mplot3d, - axes helpers in axes_grid1 and - axis helpers in axisartist. -

- -

Third party packages

- -

A large number of third party packages - extend and build on Matplotlib functionality, including several higher-level plotting interfaces - seaborn, - holoviews, - ggplot, and - two projection and mapping toolkits - basemap and - cartopy. -

- -

Citing matplotlib

- -

- matplotlib is the brainchild of John Hunter (1968-2012), who, along with its many - contributors, have put an immeasurable amount of time and effort into producing a - piece of software utilized by thousands of scientists worldwide. - - If matplotlib contributes to a project that leads to a scientific publication, - please acknowledge this work by citing the project. You can use this - ready-made citation entry. -

- -

Open source

- A Fiscally Sponsored Project of NUMFocus -

-Please -consider donating -to the matplotlib project through the Numfocus organization or to -the John Hunter Technology Fellowship. -

- -

-The matplotlib license is based on the Python Software Foundation -(PSF) license. -

- -

-There is an active developer community and a long list of people -who have made significant contributions. -

- -

-Matplotlib is hosted on Github. -Issues and -Pull requests -are tracked at Github too. -

- - -

-* -MATLAB is a registered trademark of The MathWorks, Inc. -

-

- -Mathematica is a registered trademark of Wolfram Research, Inc. -

- -{% endblock %} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html deleted file mode 100644 index ad1c55a25161..000000000000 --- a/doc/_templates/layout.html +++ /dev/null @@ -1,394 +0,0 @@ -{# - basic/layout.html - ~~~~~~~~~~~~~~~~~ - - Master layout template for Sphinx themes. - - :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -#} -{%- block doctype -%} - -{%- endblock %} -{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} -{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} -{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and - (sidebars != []) %} -{%- set url_root = pathto('', 1) %} -{# XXX necessary? #} -{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} -{%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} -{%- else %} - {%- set titlesuffix = "" %} -{%- endif %} - -{%- macro relbar() %} - -{%- endmacro %} - -{%- macro sidebar() %} - {%- if render_sidebar %} -
-
- {%- block sidebarlogo %} - {%- if logo %} - - {%- endif %} - {%- endblock %} - {%- if sidebars != None %} - {#- new style sidebar: explicitly include/exclude templates #} - {%- for sidebartemplate in sidebars %} - {%- include sidebartemplate %} - {%- endfor %} - {%- endif %} -
-
- {%- endif %} -{%- endmacro %} - -{%- macro script() %} - - {%- for scriptfile in script_files %} - - {%- endfor %} -{%- endmacro %} - -{%- macro css() %} - - - {%- for cssfile in css_files %} - - {%- endfor %} -{%- endmacro %} - - - - - {{ metatags }} - {%- block htmltitle %} - {{ title|striptags|e }}{{ titlesuffix }} - {%- endblock %} - {{ css() }} - {%- if not embedded %} - {{ script() }} - {%- if use_opensearch %} - - {%- endif %} - {%- if favicon %} - - {%- endif %} - {%- endif %} -{%- block linktags %} - {%- if hasdoc('about') %} - - {%- endif %} - {%- if hasdoc('genindex') %} - - {%- endif %} - {%- if hasdoc('search') %} - - {%- endif %} - {%- if hasdoc('copyright') %} - - {%- endif %} - - {%- if parents %} - - {%- endif %} - {%- if next %} - - {%- endif %} - {%- if prev %} - - {%- endif %} -{%- endblock %} -{%- block extrahead %} {% endblock %} - - - - -{%- block header %}{% endblock %} - -{% block relbar1 %} - - - - - - - -
-{%- if builder in ('htmlhelp', 'devhelp', 'latex') %} -matplotlib -{%- else %} -matplotlib -{%- endif %} -
- -{% endblock %} - -{%- block relbar2 %} - -{{ relbar() }} - -{% endblock %} - - - -{%- block content %} - {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} - - {%- block sidebar2 %}{{ sidebar() }}{% endblock %} - -
- {%- block document %} -
- {%- if render_sidebar %} -
- {%- endif %} -
- {% block body %} {% endblock %} -
- {%- if render_sidebar %} -
- {%- endif %} -
- {%- endblock %} - -
-
-{%- endblock %} - -{%- block footer %} - - {%- endblock %} - - - -
- - - - - - -
-
-
-
-
-
- - -
- diff --git a/doc/_templates/pagesource.html b/doc/_templates/pagesource.html new file mode 100644 index 000000000000..54428f9d6910 --- /dev/null +++ b/doc/_templates/pagesource.html @@ -0,0 +1,7 @@ +{%- if show_source and has_source and sourcename %} + +{%- endif %} diff --git a/doc/_templates/search.html b/doc/_templates/search.html index f24c6ab13fd8..aacae3dac7c2 100644 --- a/doc/_templates/search.html +++ b/doc/_templates/search.html @@ -1,20 +1,23 @@ {% extends "layout.html" %} {% set title = _('Search') %} -{% set script_files = script_files + ['_static/searchtools.js'] %} +{%- block scripts %} + {{ super() }} + + +{%- endblock %} {% block body %}

{{ _('Search') }}

- {% trans %}From here you can search these documents. Enter your - search words into the box below and click "search". Note that the - search function will automatically search for all of the - words. Pages containing less words won't appear in the result - list.{% endtrans %} If you want to limit your search to working code examples, - include the keyword "codex" (mnemonic for code example) in your - search, e.g., "codex ellipse"; - see search examples. + {% trans %}Searching for multiple words only shows matches that contain + all words.{% endtrans %} +

+

+ If you want to limit your search to working code examples, include the + keyword "codex" (mnemonic for code example) in your search, e.g., + "codex ellipse".

- +
@@ -38,7 +41,5 @@

{{ _('Search Results') }}

{% endblock %} {% block footer %} {{ super() }} - + {% endblock %} diff --git a/doc/_templates/sections/announcement.html b/doc/_templates/sections/announcement.html new file mode 100644 index 000000000000..b134acef9af5 --- /dev/null +++ b/doc/_templates/sections/announcement.html @@ -0,0 +1,13 @@ +{%- if theme_announcement == "unreleased" -%} +{% set header_classes = ["bd-header-announcement", "container-fluid"] %} +
+
+ You are reading documentation for the unreleased version of Matplotlib. + + Try searching for the released version of this page instead? + +
+
+{%- else -%} + {%- extends "!sections/announcement.html" -%} +{%- endif %} diff --git a/doc/_templates/sidebar_announcement.html b/doc/_templates/sidebar_announcement.html new file mode 100644 index 000000000000..f8fa4d8a5efb --- /dev/null +++ b/doc/_templates/sidebar_announcement.html @@ -0,0 +1,5 @@ + diff --git a/doc/_templates/sidebar_versions.html b/doc/_templates/sidebar_versions.html new file mode 100644 index 000000000000..35a965661d90 --- /dev/null +++ b/doc/_templates/sidebar_versions.html @@ -0,0 +1,38 @@ + diff --git a/doc/api/.gitignore b/doc/api/.gitignore new file mode 100644 index 000000000000..dbed88d89836 --- /dev/null +++ b/doc/api/.gitignore @@ -0,0 +1 @@ +scalarmappable.gen_rst diff --git a/doc/api/_afm_api.rst b/doc/api/_afm_api.rst new file mode 100644 index 000000000000..4e2ac4997272 --- /dev/null +++ b/doc/api/_afm_api.rst @@ -0,0 +1,8 @@ +******************* +``matplotlib._afm`` +******************* + +.. automodule:: matplotlib._afm + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/_api_api.rst b/doc/api/_api_api.rst new file mode 100644 index 000000000000..a41af9009bcf --- /dev/null +++ b/doc/api/_api_api.rst @@ -0,0 +1,13 @@ +******************* +``matplotlib._api`` +******************* + +.. automodule:: matplotlib._api + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: matplotlib._api.deprecation + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/_docstring_api.rst b/doc/api/_docstring_api.rst new file mode 100644 index 000000000000..040a3653a87b --- /dev/null +++ b/doc/api/_docstring_api.rst @@ -0,0 +1,8 @@ +************************* +``matplotlib._docstring`` +************************* + +.. automodule:: matplotlib._docstring + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/_enums_api.rst b/doc/api/_enums_api.rst new file mode 100644 index 000000000000..c38d535f3573 --- /dev/null +++ b/doc/api/_enums_api.rst @@ -0,0 +1,14 @@ +********************** +``matplotlib._enums`` +********************** + +.. automodule:: matplotlib._enums + :no-members: + + .. autoclass:: JoinStyle + :members: demo + :exclude-members: bevel, miter, round, input_description + + .. autoclass:: CapStyle + :members: demo + :exclude-members: butt, round, projecting, input_description diff --git a/doc/api/_tight_bbox_api.rst b/doc/api/_tight_bbox_api.rst new file mode 100644 index 000000000000..826e051fcf6d --- /dev/null +++ b/doc/api/_tight_bbox_api.rst @@ -0,0 +1,8 @@ +************************** +``matplotlib._tight_bbox`` +************************** + +.. automodule:: matplotlib._tight_bbox + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/_tight_layout_api.rst b/doc/api/_tight_layout_api.rst new file mode 100644 index 000000000000..ac4f66f280e1 --- /dev/null +++ b/doc/api/_tight_layout_api.rst @@ -0,0 +1,8 @@ +**************************** +``matplotlib._tight_layout`` +**************************** + +.. automodule:: matplotlib._tight_layout + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/_type1font.rst b/doc/api/_type1font.rst new file mode 100644 index 000000000000..1a9ff2292887 --- /dev/null +++ b/doc/api/_type1font.rst @@ -0,0 +1,8 @@ +************************* +``matplotlib._type1font`` +************************* + +.. automodule:: matplotlib._type1font + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/afm_api.rst b/doc/api/afm_api.rst deleted file mode 100644 index 3bffbd1c30a3..000000000000 --- a/doc/api/afm_api.rst +++ /dev/null @@ -1,12 +0,0 @@ -********************************** -afm (Adobe Font Metrics interface) -********************************** - - -:mod:`matplotlib.afm` -===================== - -.. automodule:: matplotlib.afm - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index d43dc351abae..1233b482165d 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -1,12 +1,327 @@ -********* -animation -********* +************************ +``matplotlib.animation`` +************************ +.. automodule:: matplotlib.animation + :no-members: + :no-undoc-members: -:mod:`matplotlib.animation` -=========================== +.. contents:: Table of Contents + :depth: 1 + :local: + :backlinks: entry -.. automodule:: matplotlib.animation - :members: - :undoc-members: - :show-inheritance: + +Animation +========= + +The easiest way to make a live animation in Matplotlib is to use one of the +`Animation` classes. + +.. seealso:: + - :ref:`animations` + +.. inheritance-diagram:: matplotlib.animation.FuncAnimation matplotlib.animation.ArtistAnimation + :parts: 1 + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + Animation + FuncAnimation + ArtistAnimation + +In both cases it is critical to keep a reference to the instance +object. The animation is advanced by a timer (typically from the host +GUI framework) which the `Animation` object holds the only reference +to. If you do not hold a reference to the `Animation` object, it (and +hence the timers) will be garbage collected which will stop the +animation. + +To save an animation use `Animation.save`, `Animation.to_html5_video`, +or `Animation.to_jshtml`. + +See :ref:`ani_writer_classes` below for details about what movie formats are +supported. + + +.. _func-animation: + +``FuncAnimation`` +----------------- + +The inner workings of `FuncAnimation` is more-or-less:: + + for d in frames: + artists = func(d, *fargs) + fig.canvas.draw_idle() + fig.canvas.start_event_loop(interval) + +with details to handle 'blitting' (to dramatically improve the live +performance), to be non-blocking, not repeatedly start/stop the GUI +event loop, handle repeats, multiple animated axes, and easily save +the animation to a movie file. + +'Blitting' is a `standard technique +`__ in computer graphics. The +general gist is to take an existing bit map (in our case a mostly +rasterized figure) and then 'blit' one more artist on top. Thus, by +managing a saved 'clean' bitmap, we can only re-draw the few artists +that are changing at each frame and possibly save significant amounts of +time. When we use blitting (by passing ``blit=True``), the core loop of +`FuncAnimation` gets a bit more complicated:: + + ax = fig.gca() + + def update_blit(artists): + fig.canvas.restore_region(bg_cache) + for a in artists: + a.axes.draw_artist(a) + + ax.figure.canvas.blit(ax.bbox) + + artists = init_func() + + for a in artists: + a.set_animated(True) + + fig.canvas.draw() + bg_cache = fig.canvas.copy_from_bbox(ax.bbox) + + for f in frames: + artists = func(f, *fargs) + update_blit(artists) + fig.canvas.start_event_loop(interval) + +This is of course leaving out many details (such as updating the +background when the figure is resized or fully re-drawn). However, +this hopefully minimalist example gives a sense of how ``init_func`` +and ``func`` are used inside of `FuncAnimation` and the theory of how +'blitting' works. + +.. note:: + + The zorder of artists is not taken into account when 'blitting' + because the 'blitted' artists are always drawn on top. + +The expected signature on ``func`` and ``init_func`` is very simple to +keep `FuncAnimation` out of your book keeping and plotting logic, but +this means that the callable objects you pass in must know what +artists they should be working on. There are several approaches to +handling this, of varying complexity and encapsulation. The simplest +approach, which works quite well in the case of a script, is to define the +artist at a global scope and let Python sort things out. For example:: + + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.animation import FuncAnimation + + fig, ax = plt.subplots() + xdata, ydata = [], [] + ln, = ax.plot([], [], 'ro') + + def init(): + ax.set_xlim(0, 2*np.pi) + ax.set_ylim(-1, 1) + return ln, + + def update(frame): + xdata.append(frame) + ydata.append(np.sin(frame)) + ln.set_data(xdata, ydata) + return ln, + + ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128), + init_func=init, blit=True) + plt.show() + +The second method is to use `functools.partial` to pass arguments to the +function:: + + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.animation import FuncAnimation + from functools import partial + + fig, ax = plt.subplots() + line1, = ax.plot([], [], 'ro') + + def init(): + ax.set_xlim(0, 2*np.pi) + ax.set_ylim(-1, 1) + return line1, + + def update(frame, ln, x, y): + x.append(frame) + y.append(np.sin(frame)) + ln.set_data(x, y) + return ln, + + ani = FuncAnimation( + fig, partial(update, ln=line1, x=[], y=[]), + frames=np.linspace(0, 2*np.pi, 128), + init_func=init, blit=True) + + plt.show() + +A third method is to use closures to build up the required +artists and functions. A fourth method is to create a class. + +Examples +^^^^^^^^ + +* :doc:`../gallery/animation/animate_decay` +* :doc:`../gallery/animation/bayes_update` +* :doc:`../gallery/animation/double_pendulum` +* :doc:`../gallery/animation/animated_histogram` +* :doc:`../gallery/animation/rain` +* :doc:`../gallery/animation/random_walk` +* :doc:`../gallery/animation/simple_anim` +* :doc:`../gallery/animation/strip_chart` +* :doc:`../gallery/animation/unchained` + +``ArtistAnimation`` +------------------- + +Examples +^^^^^^^^ + +* :doc:`../gallery/animation/dynamic_image` + +Writer Classes +============== + +.. inheritance-diagram:: matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter matplotlib.animation.PillowWriter matplotlib.animation.HTMLWriter + :top-classes: matplotlib.animation.AbstractMovieWriter + :parts: 1 + +The provided writers fall into a few broad categories. + +The Pillow writer relies on the Pillow library to write the animation, keeping +all data in memory. + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + PillowWriter + +The HTML writer generates JavaScript-based animations. + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + HTMLWriter + +The pipe-based writers stream the captured frames over a pipe to an external +process. The pipe-based variants tend to be more performant, but may not work +on all systems. + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + FFMpegWriter + ImageMagickWriter + +The file-based writers save temporary files for each frame which are stitched +into a single file at the end. Although slower, these writers can be easier to +debug. + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + FFMpegFileWriter + ImageMagickFileWriter + +The writer classes provide a way to grab sequential frames from the same +underlying `~matplotlib.figure.Figure`. They all provide three methods that +must be called in sequence: + +- `~.AbstractMovieWriter.setup` prepares the writer (e.g. opening a pipe). + Pipe-based and file-based writers take different arguments to ``setup()``. +- `~.AbstractMovieWriter.grab_frame` can then be called as often as + needed to capture a single frame at a time +- `~.AbstractMovieWriter.finish` finalizes the movie and writes the output + file to disk. + +Example:: + + moviewriter = MovieWriter(...) + moviewriter.setup(fig, 'my_movie.ext', dpi=100) + for j in range(n): + update_figure(j) + moviewriter.grab_frame() + moviewriter.finish() + +If using the writer classes directly (not through `Animation.save`), it is +strongly encouraged to use the `~.AbstractMovieWriter.saving` context manager:: + + with moviewriter.saving(fig, 'myfile.mp4', dpi=100): + for j in range(n): + update_figure(j) + moviewriter.grab_frame() + +to ensure that setup and cleanup are performed as necessary. + +Examples +-------- + +* :doc:`../gallery/animation/frame_grabbing_sgskip` + +.. _ani_writer_classes: + +Helper Classes +============== + +Animation Base Classes +---------------------- + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + Animation + TimedAnimation + +Writer Registry +--------------- + +A module-level registry is provided to map between the name of the +writer and the class to allow a string to be passed to +`Animation.save` instead of a writer instance. + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + MovieWriterRegistry + +Writer Base Classes +------------------- + +To reduce code duplication base classes + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + AbstractMovieWriter + MovieWriter + FileMovieWriter + +and mixins + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + FFMpegBase + ImageMagickBase + +are provided. + +See the source code for how to easily implement new `MovieWriter` classes. diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst deleted file mode 100644 index 12761d775e5c..000000000000 --- a/doc/api/api_changes.rst +++ /dev/null @@ -1,2905 +0,0 @@ - -============= - API Changes -============= - -Log of changes to matplotlib that affect the outward-facing API. If -updating matplotlib breaks your scripts, this list may help you figure -out what caused the breakage and how to fix it by updating your code. - -For new features that were added to matplotlib, please see -:ref:`whats-new`. - - -API Changes in 2.0.0 -==================== - -Deprecation and removal ------------------------ - -Color of Axes -~~~~~~~~~~~~~ -The ``axisbg`` and ``axis_bgcolor`` properties on ``Axes`` have been -deprecated in favor of ``facecolor``. - - -GTK and GDK backends deprecated -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The GDK and GTK backends have been deprecated. These obsolete backends -allow figures to be rendered via the GDK API to files and GTK2 figures. -They are untested and known to be broken, and their use has been -discouraged for some time. Instead, use the `GTKAgg` and `GTKCairo` -backends for rendering to GTK2 windows. - -WX backend deprecated -~~~~~~~~~~~~~~~~~~~~~ -The WX backend has been deprecated. It is untested, and its -use has been discouraged for some time. Instead, use the `WXAgg` -backend for rendering figures to WX windows. - -CocoaAgg backend removed -~~~~~~~~~~~~~~~~~~~~~~~~ -The deprecated and not fully functional CocoaAgg backend has been removed. - - -`Artist.update` has return value --------------------------------- - -The methods `matplotlib.artist.Artist.set`, -`matplotlib.Artist.update`, and the function `matplotlib.artist.setp` -now use a common codepath to look up how to update the given artist -properties (either using the setter methods or an attribute/property). - -The behavior of `matplotlib.Artist.update` is slightly changed to -return a list of the values returned from the setter methods to avoid -changing the API of `matplotlib.Artist.set` and -`matplotlib.artist.setp`. - -The keys passed into `matplotlib.Artist.update` are now converted to -lower case before being processed, to match the behavior of -`matplotlib.Artist.set` and `matplotlib.artist.setp`. This should not -break any user code because there are no set methods with capitals in -their names, but this puts a constraint on naming properties in the future. - - -`Legend` initializers gain edgecolor and facecolor kwargs ---------------------------------------------------------- - -The :class:`~matplotlib.legend.Legend` background patch (or 'frame') -can have its ``edgecolor`` and ``facecolor`` determined by the -corresponding keyword arguments to the :class:`matplotlib.legend.Legend` -initializer, or to any of the methods or functions that call that -initializer. If left to their default values of `None`, their values -will be taken from ``matplotlib.rcParams``. The previously-existing -``framealpha`` kwarg still controls the alpha transparency of the -patch. - - -Qualitative colormaps ---------------------- - -Colorbrewer's qualitative/discrete colormaps ("Accent", "Dark2", "Paired", -"Pastel1", "Pastel2", "Set1", "Set2", "Set3") are now implemented as -``ListedColormap`` instead of ``LinearSegmentedColormap``. - -To use these for images where categories are specified as integers, for -instance, use:: - - plt.imshow(x, cmap='Dark2', norm=colors.NoNorm()) - - -Change in the ``draw_image`` backend API ----------------------------------------- - -The ``draw_image`` method implemented by backends has changed its interface. - -This change is only relevant if the backend declares that it is able -to transform images by returning ``True`` from ``option_scale_image``. -See the ``draw_image`` docstring for more information. - - - -`matplotlib.ticker.LinearLocator` algorithm update --------------------------------------------------- - -The ``matplotlib.ticker.LinearLocator`` is used to define the range and -location of axis ticks when the user wants an exact number of ticks. -``LinearLocator`` thus differs from the default locator ``MaxNLocator``, -for which the user specifies a maximum number of intervals rather than -a precise number of ticks. - -The view range algorithm in ``matplotlib.ticker.LinearLocator`` has been -changed so that more convenient tick locations are chosen. The new algorithm -returns a plot view range that is a multiple of the user-requested number of -ticks. This ensures tick marks will be located at whole integers more -consistently. For example, when both y-axes of a``twinx`` plot use -``matplotlib.ticker.LinearLocator`` with the same number of ticks, -their y-tick locations and grid lines will coincide. - -`matplotlib.ticker.LogLocator` gains numticks kwarg ---------------------------------------------------- - -The maximum number of ticks generated by the -`~matplotlib.ticker.LogLocator` can now be controlled explicitly -via setting the new 'numticks' kwarg to an integer. By default -the kwarg is None which internally sets it to the 'auto' string, -triggering a new algorithm for adjusting the maximum according -to the axis length relative to the ticklabel font size. - - -New defaults for 3D quiver function in mpl_toolkits.mplot3d.axes3d.py ---------------------------------------------------------------------- - -Matplotlib has both a 2D and a 3D ``quiver`` function. These changes -affect only the 3D function and make the default behavior of the 3D -function match the 2D version. There are two changes: - -1) The 3D quiver function previously normalized the arrows to be the - same length, which makes it unusable for situations where the - arrows should be different lengths and does not match the behavior - of the 2D function. This normalization behavior is now controlled - with the ``normalize`` keyword, which defaults to False. - -2) The ``pivot`` keyword now defaults to ``tail`` instead of - ``tip``. This was done in order to match the default behavior of - the 2D quiver function. - -To obtain the previous behavior with the 3D quiver function, one can -call the function with :: - - ax.quiver(x, y, z, u, v, w, normalize=True, pivot='tip') - -where "ax" is an ``Axes3d`` object created with something like :: - - import mpl_toolkits.mplot3d.axes3d - ax = plt.sublot(111, projection='3d') - - -Changes in 1.5.3 -================ - -``ax.plot(..., marker=None)`` gives default marker --------------------------------------------------- - -Prior to 1.5.3 kwargs passed to `~matplotlib.Axes.plot` were handled -in two parts -- default kwargs generated internal to -`~matplotlib.Axes.plot` (such as the cycled styles) and user supplied -kwargs. The internally generated kwargs were passed to the -`matplotlib.lines.Line2D.__init__` and the user kwargs were passed to -``ln.set(**kwargs)`` to update the artist after it was created. Now -both sets of kwargs are merged and passed to -`~matplotlib.lines.Line2D.__init__`. This change was made to allow `None` -to be passed in via the user kwargs to mean 'do the default thing' as -is the convention through out mpl rather than raising an exception. - -Unlike most `~matplotlib.lines.Line2D` setter methods -`~matplotlib.lines.Line2D.set_marker` did accept `None` as a valid -input which was mapped to 'no marker'. Thus, by routing this -``marker=None`` through ``__init__`` rather than ``set(...)`` the meaning -of ``ax.plot(..., marker=None)`` changed from 'no markers' to 'default markers -from rcparams'. - -This is change is only evident if ``mpl.rcParams['lines.marker']`` has a value -other than ``'None'`` (which is string ``'None'`` which means 'no marker'). - - -Changes in 1.5.2 -================ - - -Default Behavior Changes ------------------------- - -Changed default ``autorange`` behavior in boxplots -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Prior to v1.5.2, the whiskers of boxplots would extend to the mininum -and maximum values if the quartiles were all equal (i.e., Q1 = median -= Q3). This behavior has been disabled by default to restore consistency -with other plotting packages. - -To restore the old behavior, simply set ``autorange=True`` when -calling ``plt.boxplot``. - - -Changes in 1.5.0 -================ - -Code Changes ------------- - -Reversed `matplotlib.cbook.ls_mapper`, added `ls_mapper_r` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Formerly, `matplotlib.cbook.ls_mapper` was a dictionary with -the long-form line-style names (`"solid"`) as keys and the short -forms (`"-"`) as values. This long-to-short mapping is now done -by `ls_mapper_r`, and the short-to-long mapping is done by the -`ls_mapper`. - -Prevent moving artists between Axes, Property-ify Artist.axes, deprecate Artist.{get,set}_axes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This was done to prevent an Artist that is -already associated with an Axes from being moved/added to a different Axes. -This was never supported as it causes havoc with the transform stack. -The apparent support for this (as it did not raise an exception) was -the source of multiple bug reports and questions on SO. - -For almost all use-cases, the assignment of the axes to an artist should be -taken care of by the axes as part of the ``Axes.add_*`` method, hence the -deprecation of {get,set}_axes. - -Removing the ``set_axes`` method will also remove the 'axes' line from -the ACCEPTS kwarg tables (assuming that the removal date gets here -before that gets overhauled). - -Tightened input validation on 'pivot' kwarg to quiver -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Tightened validation so that only {'tip', 'tail', 'mid', and 'middle'} -(but any capitalization) are valid values for the 'pivot' kwarg in -the `Quiver.__init__` (and hence `Axes.quiver` and -`plt.quiver` which both fully delegate to `Quiver`). Previously any -input matching 'mid.*' would be interpreted as 'middle', 'tip.*' as -'tip' and any string not matching one of those patterns as 'tail'. - -The value of `Quiver.pivot` is normalized to be in the set {'tip', -'tail', 'middle'} in `Quiver.__init__`. - -Reordered `Axes.get_children` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The artist order returned by `Axes.get_children` did not -match the one used by `Axes.draw`. They now use the same -order, as `Axes.draw` now calls `Axes.get_children`. - -Changed behaviour of contour plots -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The default behaviour of :func:`~matplotlib.pyplot.contour` and -:func:`~matplotlib.pyplot.contourf` when using a masked array is now determined -by the new keyword argument `corner_mask`, or if this is not specified then -the new rcParam `contour.corner_mask` instead. The new default behaviour is -equivalent to using `corner_mask=True`; the previous behaviour can be obtained -using `corner_mask=False` or by changing the rcParam. The example -http://matplotlib.org/examples/pylab_examples/contour_corner_mask.html -demonstrates the difference. Use of the old contouring algorithm, which is -obtained with `corner_mask='legacy'`, is now deprecated. - -Contour labels may now appear in different places than in earlier versions of -matplotlib. - -In addition, the keyword argument `nchunk` now applies to -:func:`~matplotlib.pyplot.contour` as well as -:func:`~matplotlib.pyplot.contourf`, and it subdivides the domain into -subdomains of exactly `nchunk` by `nchunk` quads, whereas previously it was -only roughly `nchunk` by `nchunk` quads. - -The C/C++ object that performs contour calculations used to be stored in the -public attribute QuadContourSet.Cntr, but is now stored in a private attribute -and should not be accessed by end users. - -Added set_params function to all Locator types -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This was a bug fix targeted at making the api for Locators more consistent. - -In the old behavior, only locators of type MaxNLocator have set_params() -defined, causing its use on any other Locator to raise an AttributeError *( -aside: set_params(args) is a function that sets the parameters of a Locator -instance to be as specified within args)*. The fix involves moving set_params() -to the Locator class such that all subtypes will have this function defined. - -Since each of the Locator subtypes have their own modifiable parameters, a -universal set_params() in Locator isn't ideal. Instead, a default no-operation -function that raises a warning is implemented in Locator. Subtypes extending -Locator will then override with their own implementations. Subtypes that do -not have a need for set_params() will fall back onto their parent's -implementation, which raises a warning as intended. - -In the new behavior, Locator instances will not raise an AttributeError -when set_params() is called. For Locators that do not implement set_params(), -the default implementation in Locator is used. - -Disallow ``None`` as x or y value in ax.plot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Do not allow ``None`` as a valid input for the ``x`` or ``y`` args in -`ax.plot`. This may break some user code, but this was never officially -supported (ex documented) and allowing ``None`` objects through can lead -to confusing exceptions downstream. - -To create an empty line use :: - - ln1, = ax.plot([], [], ...) - ln2, = ax.plot([], ...) - -In either case to update the data in the `Line2D` object you must update -both the ``x`` and ``y`` data. - - -Removed `args` and `kwargs` from `MicrosecondLocator.__call__` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The call signature of :meth:`~matplotlib.dates.MicrosecondLocator.__call__` -has changed from `__call__(self, *args, **kwargs)` to `__call__(self)`. -This is consistent with the superclass :class:`~matplotlib.ticker.Locator` -and also all the other Locators derived from this superclass. - - -No `ValueError` for the MicrosecondLocator and YearLocator -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :class:`~matplotlib.dates.MicrosecondLocator` and -:class:`~matplotlib.dates.YearLocator` objects when called will return -an empty list if the axes have no data or the view has no interval. -Previously, they raised a `ValueError`. This is consistent with all -the Date Locators. - -'OffsetBox.DrawingArea' respects the 'clip' keyword argument -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The call signature was `OffsetBox.DrawingArea(..., clip=True)` but nothing -was done with the `clip` argument. The object did not do any clipping -regardless of that parameter. Now the object can and does clip the -child `Artists` if they are set to be clipped. - -You can turn off the clipping on a per-child basis using -`child.set_clip_on(False)`. - -Add salt to clipPath id -~~~~~~~~~~~~~~~~~~~~~~~ - -Add salt to the hash used to determine the id of the ``clipPath`` -nodes. This is to avoid conflicts when two svg documents with the same -clip path are included in the same document (see -https://github.com/ipython/ipython/issues/8133 and -https://github.com/matplotlib/matplotlib/issues/4349 ), however this -means that the svg output is no longer deterministic if the same -figure is saved twice. It is not expected that this will affect any -users as the current ids are generated from an md5 hash of properties -of the clip path and any user would have a very difficult time -anticipating the value of the id. - -Changed snap threshold for circle markers to inf -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When drawing circle markers above some marker size (previously 6.0) -the path used to generate the marker was snapped to pixel centers. However, -this ends up distorting the marker away from a circle. By setting the -snap threshold to inf snapping is never done on circles. - -This change broke several tests, but is an improvement. - -Preserve units with Text position -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Previously the 'get_position' method on Text would strip away unit information -even though the units were still present. There was no inherent need to do -this, so it has been changed so that unit data (if present) will be preserved. -Essentially a call to 'get_position' will return the exact value from a call to -'set_position'. - -If you wish to get the old behaviour, then you can use the new method called -'get_unitless_position'. - -New API for custom Axes view changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Interactive pan and zoom were previously implemented using a Cartesian-specific -algorithm that was not necessarily applicable to custom Axes. Three new private -methods, :meth:`~matplotlib.axes._base._AxesBase._get_view`, -:meth:`~matplotlib.axes._base._AxesBase._set_view`, and -:meth:`~matplotlib.axes._base._AxesBase._set_view_from_bbox`, allow for custom -``Axes`` classes to override the pan and zoom algorithms. Implementors of -custom ``Axes`` who override these methods may provide suitable behaviour for -both pan and zoom as well as the view navigation buttons on the interactive -toolbars. - -MathTex visual changes ----------------------- - -The spacing commands in mathtext have been changed to more closely -match vanilla TeX. - - -Improved spacing in mathtext -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The extra space that appeared after subscripts and superscripts has -been removed. - -No annotation coordinates wrap -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In #2351 for 1.4.0 the behavior of ['axes points', 'axes pixel', -'figure points', 'figure pixel'] as coordinates was change to -no longer wrap for negative values. In 1.4.3 this change was -reverted for 'axes points' and 'axes pixel' and in addition caused -'axes fraction' to wrap. For 1.5 the behavior has been reverted to -as it was in 1.4.0-1.4.2, no wrapping for any type of coordinate. - -Deprecation ------------ - -Deprecated `GraphicsContextBase.set_graylevel` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `GraphicsContextBase.set_graylevel` function has been deprecated in 1.5 and -will be removed in 1.6. It has been unused. The -`GraphicsContextBase.set_foreground` could be used instead. - -deprecated idle_event -~~~~~~~~~~~~~~~~~~~~~ - -The `idle_event` was broken or missing in most backends and causes spurious -warnings in some cases, and its use in creating animations is now obsolete due -to the animations module. Therefore code involving it has been removed from all -but the wx backend (where it partially works), and its use is deprecated. The -animations module may be used instead to create animations. - -`color_cycle` deprecated -~~~~~~~~~~~~~~~~~~~~~~~~ - -In light of the new property cycling feature, -the Axes method *set_color_cycle* is now deprecated. -Calling this method will replace the current property cycle with -one that cycles just the given colors. - -Similarly, the rc parameter *axes.color_cycle* is also deprecated in -lieu of the new *axes.prop_cycle* parameter. Having both parameters in -the same rc file is not recommended as the result cannot be -predicted. For compatibility, setting *axes.color_cycle* will -replace the cycler in *axes.prop_cycle* with a color cycle. -Accessing *axes.color_cycle* will return just the color portion -of the property cycle, if it exists. - -Timeline for removal has not been set. - - -Bundled jquery --------------- - -The version of jquery bundled with the webagg backend has been upgraded -from 1.7.1 to 1.11.3. If you are using the version of jquery bundled -with webagg you will need to update your html files as such - -.. code-block:: diff - - - - + - - -Code Removed ------------- - -Removed `Image` from main namespace -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`Image` was imported from PIL/pillow to test if PIL is available, but -there is no reason to keep `Image` in the namespace once the availability -has been determined. - -Removed `lod` from Artist -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Removed the method *set_lod* and all references to -the attribute *_lod* as the are not used anywhere else in the -code base. It appears to be a feature stub that was never built -out. - -Removed threading related classes from cbook -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The classes ``Scheduler``, ``Timeout``, and ``Idle`` were in cbook, but -are not used internally. They appear to be a prototype for the idle event -system which was not working and has recently been pulled out. - -Removed `Lena` images from sample_data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``lena.png`` and ``lena.jpg`` images have been removed from -matplotlib's sample_data directory. The images are also no longer -available from `matplotlib.cbook.get_sample_data`. We suggest using -`matplotlib.cbook.get_sample_data('grace_hopper.png')` or -`matplotlib.cbook.get_sample_data('grace_hopper.jpg')` instead. - - -Legend -~~~~~~ -Removed handling of `loc` as a positional argument to `Legend` - - -Legend handlers -~~~~~~~~~~~~~~~ -Remove code to allow legend handlers to be callable. They must now -implement a method ``legend_artist``. - - -Axis -~~~~ -Removed method ``set_scale``. This is now handled via a private method which -should not be used directly by users. It is called via ``Axes.set_{x,y}scale`` -which takes care of ensuring the related changes are also made to the Axes -object. - -finance.py -~~~~~~~~~~ - -Removed functions with ambiguous argument order from finance.py - - -Annotation -~~~~~~~~~~ - -Removed ``textcoords`` and ``xytext`` proprieties from Annotation objects. - - -spinxext.ipython_*.py -~~~~~~~~~~~~~~~~~~~~~ - -Both ``ipython_console_highlighting`` and ``ipython_directive`` have been -moved to `IPython`. - -Change your import from 'matplotlib.sphinxext.ipython_directive' to -'IPython.sphinxext.ipython_directive' and from -'matplotlib.sphinxext.ipython_directive' to -'IPython.sphinxext.ipython_directive' - - -LineCollection.color -~~~~~~~~~~~~~~~~~~~~ - -Deprecated in 2005, use ``set_color`` - - -remove ``'faceted'`` as a valid value for `shading` in ``tri.tripcolor`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Use `edgecolor` instead. Added validation on ``shading`` to -only be valid values. - - -Remove ``faceted`` kwarg from scatter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Remove support for the ``faceted`` kwarg. This was deprecated in -d48b34288e9651ff95c3b8a071ef5ac5cf50bae7 (2008-04-18!) and replaced by -``edgecolor``. - - -Remove ``set_colorbar`` method from ``ScalarMappable`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Remove ``set_colorbar`` method, use `colorbar` attribute directly. - - -patheffects.svg -~~~~~~~~~~~~~~~ - - - remove ``get_proxy_renderer`` method from ``AbstarctPathEffect`` class - - remove ``patch_alpha`` and ``offset_xy`` from ``SimplePatchShadow`` - - -Remove ``testing.image_util.py`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Contained only a no-longer used port of functionality from PIL - - -Remove ``mlab.FIFOBuffer`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Not used internally and not part of core mission of mpl. - - -Remove ``mlab.prepca`` -~~~~~~~~~~~~~~~~~~~~~~ -Deprecated in 2009. - - -Remove ``NavigationToolbar2QTAgg`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Added no functionality over the base ``NavigationToolbar2Qt`` - - -mpl.py -~~~~~~ - -Remove the module `matplotlib.mpl`. Deprecated in 1.3 by -PR #1670 and commit 78ce67d161625833cacff23cfe5d74920248c5b2 - - -Changes in 1.4.x -================ - -Code changes ------------- - -* A major refactoring of the axes module was made. The axes module has been - split into smaller modules: - - - the `_base` module, which contains a new private _AxesBase class. This - class contains all methods except plotting and labelling methods. - - the `axes` module, which contains the Axes class. This class inherits - from _AxesBase, and contains all plotting and labelling methods. - - the `_subplot` module, with all the classes concerning subplotting. - -There are a couple of things that do not exists in the `axes` module's -namespace anymore. If you use them, you need to import them from their -original location: - - - math -> `import math` - - ma -> `from numpy import ma` - - cbook -> `from matplotlib import cbook` - - docstring -> `from matplotlib import docstring` - - is_sequence_of_strings -> `from matplotlib.cbook import is_sequence_of_strings` - - is_string_like -> `from matplotlib.cbook import is_string_like` - - iterable -> `from matplotlib.cbook import iterable` - - itertools -> `import itertools` - - martist -> `from matplotlib import artist as martist` - - matplotlib -> `import matplotlib` - - mcoll -> `from matplotlib import collections as mcoll` - - mcolors -> `from matplotlib import colors as mcolors` - - mcontour -> `from matplotlib import contour as mcontour` - - mpatches -> `from matplotlib import patches as mpatches` - - mpath -> `from matplotlib import path as mpath` - - mquiver -> `from matplotlib import quiver as mquiver` - - mstack -> `from matplotlib import stack as mstack` - - mstream -> `from matplotlib import stream as mstream` - - mtable -> `from matplotlib import table as mtable` - -* As part of the refactoring to enable Qt5 support, the module - `matplotlib.backends.qt4_compat` was renamed to - `matplotlib.qt_compat`. `qt4_compat` is deprecated in 1.4 and - will be removed in 1.5. - -* The :func:`~matplotlib.pyplot.errorbar` method has been changed such that - the upper and lower limits (*lolims*, *uplims*, *xlolims*, *xuplims*) now - point in the correct direction. - -* The *fmt* kwarg for :func:`~matplotlib.pyplot.errorbar now supports - the string 'none' to suppress drawing of a line and markers; use - of the *None* object for this is deprecated. The default *fmt* - value is changed to the empty string (''), so the line and markers - are governed by the :func:`~matplotlib.pyplot.plot` defaults. - -* A bug has been fixed in the path effects rendering of fonts, which now means - that the font size is consistent with non-path effect fonts. See - https://github.com/matplotlib/matplotlib/issues/2889 for more detail. - -* The Sphinx extensions `ipython_directive` and - `ipython_console_highlighting` have been moved to the IPython - project itself. While they remain in matplotlib for this release, - they have been deprecated. Update your extensions in `conf.py` to - point to `IPython.sphinxext.ipython_directive` instead of - `matplotlib.sphinxext.ipython_directive`. - -* In `~matplotlib.finance`, almost all functions have been deprecated - and replaced with a pair of functions name `*_ochl` and `*_ohlc`. - The former is the 'open-close-high-low' order of quotes used - previously in this module, and the latter is the - 'open-high-low-close' order that is standard in finance. - -* For consistency the ``face_alpha`` keyword to - :class:`matplotlib.patheffects.SimplePatchShadow` has been deprecated in - favour of the ``alpha`` keyword. Similarly, the keyword ``offset_xy`` is now - named ``offset`` across all :class:`~matplotlib.patheffects.AbstractPathEffect`s. - ``matplotlib.patheffects._Base`` has - been renamed to :class:`matplotlib.patheffects.AbstractPathEffect`. - ``matplotlib.patheffect.ProxyRenderer`` has been renamed to - :class:`matplotlib.patheffects.PathEffectRenderer` and is now a full - RendererBase subclass. - -* The artist used to draw the outline of a `colorbar` has been changed - from a `matplotlib.lines.Line2D` to `matplotlib.patches.Polygon`, - thus `colorbar.ColorbarBase.outline` is now a - `matplotlib.patches.Polygon` object. - -* The legend handler interface has changed from a callable, to any object - which implements the ``legend_artists`` method (a deprecation phase will - see this interface be maintained for v1.4). See - :ref:`plotting-guide-legend` for further details. Further legend changes - include: - - * :func:`matplotlib.axes.Axes._get_legend_handles` now returns a generator - of handles, rather than a list. - - * The :func:`~matplotlib.pyplot.legend` function's "loc" positional - argument has been deprecated. Use the "loc" keyword instead. - -* The rcParams `savefig.transparent` has been added to control - default transparency when saving figures. - -* Slightly refactored the `Annotation` family. The text location in - `Annotation` is now handled entirely handled by the underlying `Text` - object so `set_position` works as expected. The attributes `xytext` and - `textcoords` have been deprecated in favor of `xyann` and `anncoords` so - that `Annotation` and `AnnotaionBbox` can share a common sensibly named - api for getting/setting the location of the text or box. - - - `xyann` -> set the location of the annotation - - `xy` -> set where the arrow points to - - `anncoords` -> set the units of the annotation location - - `xycoords` -> set the units of the point location - - `set_position()` -> `Annotation` only set location of annotation - -* `matplotlib.mlab.specgram`, `matplotlib.mlab.psd`, `matplotlib.mlab.csd`, - `matplotlib.mlab.cohere`, `matplotlib.mlab.cohere_pairs`, - `matplotlib.pyplot.specgram`, `matplotlib.pyplot.psd`, - `matplotlib.pyplot.csd`, and `matplotlib.pyplot.cohere` now raise - ValueError where they previously raised AssertionError. - -* For `matplotlib.mlab.psd`, `matplotlib.mlab.csd`, - `matplotlib.mlab.cohere`, `matplotlib.mlab.cohere_pairs`, - `matplotlib.pyplot.specgram`, `matplotlib.pyplot.psd`, - `matplotlib.pyplot.csd`, and `matplotlib.pyplot.cohere`, in cases - where a shape (n, 1) array is returned, this is now converted to a (n, ) - array. Previously, (n, m) arrays were averaged to an (n, ) array, but - (n, 1) arrays were returend unchanged. This change makes the dimensions - consistent in both cases. - -* Added the rcParam `axes.fromatter.useoffset` to control the default value - of `useOffset` in `ticker.ScalarFormatter` - -* Added `Formatter` sub-class `StrMethodFormatter` which - does the exact same thing as `FormatStrFormatter`, but for new-style - formatting strings. - -* Deprecated `matplotlib.testing.image_util` and the only function within, - `matplotlib.testing.image_util.autocontrast`. These will be removed - completely in v1.5.0. - -* The ``fmt`` argument of :meth:`~matplotlib.axes.Axes.plot_date` has been - changed from ``bo`` to just ``o``, so color cycling can happen by default. - -* Removed the class `FigureManagerQTAgg` and deprecated `NavigationToolbar2QTAgg` - which will be removed in 1.5. - -* Removed formerly public (non-prefixed) attributes `rect` and - `drawRect` from `FigureCanvasQTAgg`; they were always an - implementation detail of the (preserved) `drawRectangle()` function. - -* The function signatures of `tight_bbox.adjust_bbox` and - `tight_bbox.process_figure_for_rasterizing` have been changed. A new - `fixed_dpi` parameter allows for overriding the `figure.dpi` setting - instead of trying to deduce the intended behaviour from the file format. - -* Added support for horizontal/vertical axes padding to - `mpl_toolkits.axes_grid1.ImageGrid` --- argument ``axes_pad`` can now be - tuple-like if separate axis padding is required. - The original behavior is preserved. - -* Added support for skewed transforms to `matplotlib.transforms.Affine2D`, - which can be created using the `skew` and `skew_deg` methods. - -* Added clockwise parameter to control sectors direction in `axes.pie` - -* In `matplotlib.lines.Line2D` the `markevery` functionality has been extended. - Previously an integer start-index and stride-length could be specified using - either a two-element-list or a two-element-tuple. Now this can only be done - using a two-element-tuple. If a two-element-list is used then it will be - treated as numpy fancy indexing and only the two markers corresponding to the - given indexes will be shown. - -* removed prop kwarg from `mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar` - call. It was passed through to the base-class `__init__` and is only used for - setting padding. Now `fontproperties` (which is what is really used to set - the font properties of `AnchoredSizeBar`) is passed through in place of `prop`. - If `fontpropreties` is not passed in, but `prop` is, then `prop` is used inplace - of `fontpropreties`. If both are passed in, `prop` is silently ignored. - - -* The use of the index 0 in `plt.subplot` and related commands is - deprecated. Due to a lack of validation calling `plt.subplots(2, 2, - 0)` does not raise an exception, but puts an axes in the _last_ - position. This is due to the indexing in subplot being 1-based (to - mirror MATLAB) so before indexing into the `GridSpec` object used to - determine where the axes should go, 1 is subtracted off. Passing in - 0 results in passing -1 to `GridSpec` which results in getting the - last position back. Even though this behavior is clearly wrong and - not intended, we are going through a deprecation cycle in an - abundance of caution that any users are exploiting this 'feature'. - The use of 0 as an index will raise a warning in 1.4 and an - exception in 1.5. - -* Clipping is now off by default on offset boxes. - -* matplotlib now uses a less-aggressive call to ``gc.collect(1)`` when - closing figures to avoid major delays with large numbers of user objects - in memory. - -* The default clip value of *all* pie artists now defaults to ``False``. - - -Code removal ------------- - -* Removed ``mlab.levypdf``. The code raised a numpy error (and has for - a long time) and was not the standard form of the Levy distribution. - ``scipy.stats.levy`` should be used instead - - -.. _changes_in_1_3: - - -Changes in 1.3.x -================ - -Changes in 1.3.1 ----------------- - -It is rare that we make an API change in a bugfix release, however, -for 1.3.1 since 1.3.0 the following change was made: - -- `text.Text.cached` (used to cache font objects) has been made into a - private variable. Among the obvious encapsulation benefit, this - removes this confusing-looking member from the documentation. - -- The method :meth:`~matplotlib.axes.Axes.hist` now always returns bin - occupancies as an array of type `float`. Previously, it was sometimes - an array of type `int`, depending on the call. - -Code removal ------------- - -* The following items that were deprecated in version 1.2 or earlier - have now been removed completely. - - - The Qt 3.x backends (`qt` and `qtagg`) have been removed in - favor of the Qt 4.x backends (`qt4` and `qt4agg`). - - - The FltkAgg and Emf backends have been removed. - - - The `matplotlib.nxutils` module has been removed. Use the - functionality on `matplotlib.path.Path.contains_point` and - friends instead. - - - Instead of `axes.Axes.get_frame`, use `axes.Axes.patch`. - - - The following `kwargs` to the `legend` function have been - renamed: - - - `pad` -> `borderpad` - - `labelsep` -> `labelspacing` - - `handlelen` -> `handlelength` - - `handletextsep` -> `handletextpad` - - `axespad` -> `borderaxespad` - - Related to this, the following rcParams have been removed: - - - `legend.pad`, `legend.labelsep`, `legend.handlelen`, - `legend.handletextsep` and `legend.axespad` - - - For the `hist` function, instead of `width`, use `rwidth` - (relative width). - - - On `patches.Circle`, the `resolution` kwarg has been removed. - For a circle made up of line segments, use - `patches.CirclePolygon`. - - - The printing functions in the Wx backend have been removed due - to the burden of keeping them up-to-date. - - - `mlab.liaupunov` has been removed. - - - `mlab.save`, `mlab.load`, `pylab.save` and `pylab.load` have - been removed. We recommend using `numpy.savetxt` and - `numpy.loadtxt` instead. - - - `widgets.HorizontalSpanSelector` has been removed. Use - `widgets.SpanSelector` instead. - -Code deprecation ----------------- - -* The CocoaAgg backend has been deprecated, with the possibility for - deletion or resurrection in a future release. - -* The top-level functions in `matplotlib.path` that are implemented in - C++ were never meant to be public. Instead, users should use the - Pythonic wrappers for them in the `path.Path` and - `collections.Collection` classes. Use the following mapping to update - your code: - - - `point_in_path` -> `path.Path.contains_point` - - `get_path_extents` -> `path.Path.get_extents` - - `point_in_path_collection` -> `collection.Collection.contains` - - `path_in_path` -> `path.Path.contains_path` - - `path_intersects_path` -> `path.Path.intersects_path` - - `convert_path_to_polygons` -> `path.Path.to_polygons` - - `cleanup_path` -> `path.Path.cleaned` - - `points_in_path` -> `path.Path.contains_points` - - `clip_path_to_rect` -> `path.Path.clip_to_bbox` - -* `matplotlib.colors.normalize` and `matplotlib.colors.no_norm` have - been deprecated in favour of `matplotlib.colors.Normalize` and - `matplotlib.colors.NoNorm` respectively. - -* The `ScalarMappable` class' `set_colorbar` is now - deprecated. Instead, the - :attr:`matplotlib.cm.ScalarMappable.colorbar` attribute should be - used. In previous matplotlib versions this attribute was an - undocumented tuple of ``(colorbar_instance, colorbar_axes)`` but is - now just ``colorbar_instance``. To get the colorbar axes it is - possible to just use the - :attr:`~matplotlib.colorbar.ColorbarBase.ax` attribute on a colorbar - instance. - -* The `~matplotlib.mpl` module is now deprecated. Those who relied on this - module should transition to simply using ``import matplotlib as mpl``. - -Code changes ------------- - -* :class:`~matplotlib.patches.Patch` now fully supports using RGBA values for - its ``facecolor`` and ``edgecolor`` attributes, which enables faces and - edges to have different alpha values. If the - :class:`~matplotlib.patches.Patch` object's ``alpha`` attribute is set to - anything other than ``None``, that value will override any alpha-channel - value in both the face and edge colors. Previously, if - :class:`~matplotlib.patches.Patch` had ``alpha=None``, the alpha component - of ``edgecolor`` would be applied to both the edge and face. - -* The optional ``isRGB`` argument to - :meth:`~matplotlib.backend_bases.GraphicsContextBase.set_foreground` (and - the other GraphicsContext classes that descend from it) has been renamed to - ``isRGBA``, and should now only be set to ``True`` if the ``fg`` color - argument is known to be an RGBA tuple. - -* For :class:`~matplotlib.patches.Patch`, the ``capstyle`` used is now - ``butt``, to be consistent with the default for most other objects, and to - avoid problems with non-solid ``linestyle`` appearing solid when using a - large ``linewidth``. Previously, :class:`~matplotlib.patches.Patch` used - ``capstyle='projecting'``. - -* `Path` objects can now be marked as `readonly` by passing - `readonly=True` to its constructor. The built-in path singletons, - obtained through `Path.unit*` class methods return readonly paths. - If you have code that modified these, you will need to make a - deepcopy first, using either:: - - import copy - path = copy.deepcopy(Path.unit_circle()) - - # or - - path = Path.unit_circle().deepcopy() - - Deep copying a `Path` always creates an editable (i.e. non-readonly) - `Path`. - -* The list at ``Path.NUM_VERTICES`` was replaced by a dictionary mapping - Path codes to the number of expected vertices at - :attr:`~matplotlib.path.Path.NUM_VERTICES_FOR_CODE`. - -* To support XKCD style plots, the :func:`matplotlib.path.cleanup_path` - method's signature was updated to require a sketch argument. Users of - :func:`matplotlib.path.cleanup_path` are encouraged to use the new - :meth:`~matplotlib.path.Path.cleaned` Path method. - -* Data limits on a plot now start from a state of having "null" - limits, rather than limits in the range (0, 1). This has an effect - on artists that only control limits in one direction, such as - `axvline` and `axhline`, since their limits will not longer also - include the range (0, 1). This fixes some problems where the - computed limits would be dependent on the order in which artists - were added to the axes. - -* Fixed a bug in setting the position for the right/top spine with data - position type. Previously, it would draw the right or top spine at - +1 data offset. - -* In :class:`~matplotlib.patches.FancyArrow`, the default arrow head - width, ``head_width``, has been made larger to produce a visible - arrow head. The new value of this kwarg is ``head_width = 20 * - width``. - -* It is now possible to provide ``number of levels + 1`` colors in the case of - `extend='both'` for contourf (or just ``number of levels`` colors for an - extend value ``min`` or ``max``) such that the resulting colormap's - ``set_under`` and ``set_over`` are defined appropriately. Any other number - of colors will continue to behave as before (if more colors are provided - than levels, the colors will be unused). A similar change has been applied - to contour, where ``extend='both'`` would expect ``number of levels + 2`` - colors. - -* A new keyword *extendrect* in :meth:`~matplotlib.pyplot.colorbar` and - :class:`~matplotlib.colorbar.ColorbarBase` allows one to control the shape - of colorbar extensions. - -* The extension of :class:`~matplotlib.widgets.MultiCursor` to both vertical - (default) and/or horizontal cursor implied that ``self.line`` is replaced - by ``self.vline`` for vertical cursors lines and ``self.hline`` is added - for the horizontal cursors lines. - -* On POSIX platforms, the :func:`~matplotlib.cbook.report_memory` function - raises :class:`NotImplementedError` instead of :class:`OSError` if the - :command:`ps` command cannot be run. - -* The :func:`matplotlib.cbook.check_output` function has been moved to - :func:`matplotlib.compat.subprocess`. - -Configuration and rcParams --------------------------- - -* On Linux, the user-specific `matplotlibrc` configuration file is now - located in `~/.config/matplotlib/matplotlibrc` to conform to the - `XDG Base Directory Specification - `_. - -* The `font.*` rcParams now affect only text objects created after the - rcParam has been set, and will not retroactively affect already - existing text objects. This brings their behavior in line with most - other rcParams. - -* Removed call of :meth:`~matplotlib.axes.Axes.grid` in - :meth:`~matplotlib.pyplot.plotfile`. To draw the axes grid, set the - ``axes.grid`` rcParam to *True*, or explicitly call - :meth:`~matplotlib.axes.Axes.grid`. - -Changes in 1.2.x -================ - -* The ``classic`` option of the rc parameter ``toolbar`` is deprecated - and will be removed in the next release. - -* The :meth:`~matplotlib.cbook.isvector` method has been removed since it - is no longer functional. - -* The `rasterization_zorder` property on `~matplotlib.axes.Axes` a - zorder below which artists are rasterized. This has defaulted to - -30000.0, but it now defaults to `None`, meaning no artists will be - rasterized. In order to rasterize artists below a given zorder - value, `set_rasterization_zorder` must be explicitly called. - -* In :meth:`~matplotlib.axes.Axes.scatter`, and `~pyplot.scatter`, - when specifying a marker using a tuple, the angle is now specified - in degrees, not radians. - -* Using :meth:`~matplotlib.axes.Axes.twinx` or - :meth:`~matplotlib.axes.Axes.twiny` no longer overrides the current locaters - and formatters on the axes. - -* In :meth:`~matplotlib.axes.Axes.contourf`, the handling of the *extend* - kwarg has changed. Formerly, the extended ranges were mapped - after to 0, 1 after being normed, so that they always corresponded - to the extreme values of the colormap. Now they are mapped - outside this range so that they correspond to the special - colormap values determined by the - :meth:`~matplotlib.colors.Colormap.set_under` and - :meth:`~matplotlib.colors.Colormap.set_over` methods, which - default to the colormap end points. - -* The new rc parameter ``savefig.format`` replaces ``cairo.format`` and - ``savefig.extension``, and sets the default file format used by - :meth:`matplotlib.figure.Figure.savefig`. - -* In :meth:`~matplotlib.pyplot.pie` and :meth:`~matplotlib.Axes.pie`, one can - now set the radius of the pie; setting the *radius* to 'None' (the default - value), will result in a pie with a radius of 1 as before. - -* Use of :func:`~matplotlib.projections.projection_factory` is now deprecated - in favour of axes class identification using - :func:`~matplotlib.projections.process_projection_requirements` followed by - direct axes class invocation (at the time of writing, functions which do this - are: :meth:`~matplotlib.figure.Figure.add_axes`, - :meth:`~matplotlib.figure.Figure.add_subplot` and - :meth:`~matplotlib.figure.Figure.gca`). Therefore:: - - - key = figure._make_key(*args, **kwargs) - ispolar = kwargs.pop('polar', False) - projection = kwargs.pop('projection', None) - if ispolar: - if projection is not None and projection != 'polar': - raise ValueError('polar and projection args are inconsistent') - projection = 'polar' - ax = projection_factory(projection, self, rect, **kwargs) - key = self._make_key(*args, **kwargs) - - # is now - - projection_class, kwargs, key = \ - process_projection_requirements(self, *args, **kwargs) - ax = projection_class(self, rect, **kwargs) - - This change means that third party objects can expose themselves as - matplotlib axes by providing a ``_as_mpl_axes`` method. See - :ref:`adding-new-scales` for more detail. - -* A new keyword *extendfrac* in :meth:`~matplotlib.pyplot.colorbar` and - :class:`~matplotlib.colorbar.ColorbarBase` allows one to control the size of - the triangular minimum and maximum extensions on colorbars. - -* A new keyword *capthick* in :meth:`~matplotlib.pyplot.errorbar` has been - added as an intuitive alias to the *markeredgewidth* and *mew* keyword - arguments, which indirectly controlled the thickness of the caps on - the errorbars. For backwards compatibility, specifying either of the - original keyword arguments will override any value provided by - *capthick*. - -* Transform subclassing behaviour is now subtly changed. If your transform - implements a non-affine transformation, then it should override the - ``transform_non_affine`` method, rather than the generic ``transform`` method. - Previously transforms would define ``transform`` and then copy the - method into ``transform_non_affine``:: - - class MyTransform(mtrans.Transform): - def transform(self, xy): - ... - transform_non_affine = transform - - - This approach will no longer function correctly and should be changed to:: - - class MyTransform(mtrans.Transform): - def transform_non_affine(self, xy): - ... - - -* Artists no longer have ``x_isdata`` or ``y_isdata`` attributes; instead - any artist's transform can be interrogated with - ``artist_instance.get_transform().contains_branch(ax.transData)`` - -* Lines added to an axes now take into account their transform when updating the - data and view limits. This means transforms can now be used as a pre-transform. - For instance:: - - >>> import matplotlib.pyplot as plt - >>> import matplotlib.transforms as mtrans - >>> ax = plt.axes() - >>> ax.plot(range(10), transform=mtrans.Affine2D().scale(10) + ax.transData) - >>> print(ax.viewLim) - Bbox('array([[ 0., 0.],\n [ 90., 90.]])') - -* One can now easily get a transform which goes from one transform's coordinate - system to another, in an optimized way, using the new subtract method on a - transform. For instance, to go from data coordinates to axes coordinates:: - - >>> import matplotlib.pyplot as plt - >>> ax = plt.axes() - >>> data2ax = ax.transData - ax.transAxes - >>> print(ax.transData.depth, ax.transAxes.depth) - 3, 1 - >>> print(data2ax.depth) - 2 - - for versions before 1.2 this could only be achieved in a sub-optimal way, - using ``ax.transData + ax.transAxes.inverted()`` (depth is a new concept, - but had it existed it would return 4 for this example). - -* ``twinx`` and ``twiny`` now returns an instance of SubplotBase if - parent axes is an instance of SubplotBase. - -* All Qt3-based backends are now deprecated due to the lack of py3k bindings. - Qt and QtAgg backends will continue to work in v1.2.x for py2.6 - and py2.7. It is anticipated that the Qt3 support will be completely - removed for the next release. - -* :class:`~matplotlib.colors.ColorConverter`, - :class:`~matplotlib.colors.Colormap` and - :class:`~matplotlib.colors.Normalize` now subclasses ``object`` - -* ContourSet instances no longer have a ``transform`` attribute. Instead, - access the transform with the ``get_transform`` method. - -Changes in 1.1.x -================ - -* Added new :class:`matplotlib.sankey.Sankey` for generating Sankey diagrams. - -* In :meth:`~matplotlib.pyplot.imshow`, setting *interpolation* to 'nearest' - will now always mean that the nearest-neighbor interpolation is performed. - If you want the no-op interpolation to be performed, choose 'none'. - -* There were errors in how the tri-functions were handling input parameters - that had to be fixed. If your tri-plots are not working correctly anymore, - or you were working around apparent mistakes, please see issue #203 in the - github tracker. When in doubt, use kwargs. - -* The 'symlog' scale had some bad behavior in previous versions. This has now - been fixed and users should now be able to use it without frustrations. - The fixes did result in some minor changes in appearance for some users who - may have been depending on the bad behavior. - -* There is now a common set of markers for all plotting functions. Previously, - some markers existed only for :meth:`~matplotlib.pyplot.scatter` or just for - :meth:`~matplotlib.pyplot.plot`. This is now no longer the case. This merge - did result in a conflict. The string 'd' now means "thin diamond" while - 'D' will mean "regular diamond". - -Changes beyond 0.99.x -===================== - -* The default behavior of :meth:`matplotlib.axes.Axes.set_xlim`, - :meth:`matplotlib.axes.Axes.set_ylim`, and - :meth:`matplotlib.axes.Axes.axis`, and their corresponding - pyplot functions, has been changed: when view limits are - set explicitly with one of these methods, autoscaling is turned - off for the matching axis. A new *auto* kwarg is available to - control this behavior. The limit kwargs have been renamed to - *left* and *right* instead of *xmin* and *xmax*, and *bottom* - and *top* instead of *ymin* and *ymax*. The old names may still - be used, however. - -* There are five new Axes methods with corresponding pyplot - functions to facilitate autoscaling, tick location, and tick - label formatting, and the general appearance of ticks and - tick labels: - - + :meth:`matplotlib.axes.Axes.autoscale` turns autoscaling - on or off, and applies it. - - + :meth:`matplotlib.axes.Axes.margins` sets margins used to - autoscale the :attr:`matplotlib.axes.Axes.viewLim` based on - the :attr:`matplotlib.axes.Axes.dataLim`. - - + :meth:`matplotlib.axes.Axes.locator_params` allows one to - adjust axes locator parameters such as *nbins*. - - + :meth:`matplotlib.axes.Axes.ticklabel_format` is a convenience - method for controlling the :class:`matplotlib.ticker.ScalarFormatter` - that is used by default with linear axes. - - + :meth:`matplotlib.axes.Axes.tick_params` controls direction, size, - visibility, and color of ticks and their labels. - -* The :meth:`matplotlib.axes.Axes.bar` method accepts a *error_kw* - kwarg; it is a dictionary of kwargs to be passed to the - errorbar function. - -* The :meth:`matplotlib.axes.Axes.hist` *color* kwarg now accepts - a sequence of color specs to match a sequence of datasets. - -* The :class:`~matplotlib.collections.EllipseCollection` has been - changed in two ways: - - + There is a new *units* option, 'xy', that scales the ellipse with - the data units. This matches the :class:'~matplotlib.patches.Ellipse` - scaling. - - + The *height* and *width* kwargs have been changed to specify - the height and width, again for consistency with - :class:`~matplotlib.patches.Ellipse`, and to better match - their names; previously they specified the half-height and - half-width. - -* There is a new rc parameter ``axes.color_cycle``, and the color - cycle is now independent of the rc parameter ``lines.color``. - :func:`matplotlib.Axes.set_default_color_cycle` is deprecated. - -* You can now print several figures to one pdf file and modify the - document information dictionary of a pdf file. See the docstrings - of the class :class:`matplotlib.backends.backend_pdf.PdfPages` for - more information. - -* Removed configobj_ and `enthought.traits`_ packages, which are only - required by the experimental traited config and are somewhat out of - date. If needed, install them independently. - -.. _configobj: http://www.voidspace.org.uk/python/configobj.html -.. _`enthought.traits`: http://code.enthought.com/projects/traits - -* The new rc parameter ``savefig.extension`` sets the filename extension - that is used by :meth:`matplotlib.figure.Figure.savefig` if its *fname* - argument lacks an extension. - -* In an effort to simplify the backend API, all clipping rectangles - and paths are now passed in using GraphicsContext objects, even - on collections and images. Therefore:: - - draw_path_collection(self, master_transform, cliprect, clippath, - clippath_trans, paths, all_transforms, offsets, - offsetTrans, facecolors, edgecolors, linewidths, - linestyles, antialiaseds, urls) - - # is now - - draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offsetTrans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls) - - - draw_quad_mesh(self, master_transform, cliprect, clippath, - clippath_trans, meshWidth, meshHeight, coordinates, - offsets, offsetTrans, facecolors, antialiased, - showedges) - - # is now - - draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, - coordinates, offsets, offsetTrans, facecolors, - antialiased, showedges) - - - draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None) - - # is now - - draw_image(self, gc, x, y, im) - -* There are four new Axes methods with corresponding pyplot - functions that deal with unstructured triangular grids: - - + :meth:`matplotlib.axes.Axes.tricontour` draws contour lines - on a triangular grid. - - + :meth:`matplotlib.axes.Axes.tricontourf` draws filled contours - on a triangular grid. - - + :meth:`matplotlib.axes.Axes.tripcolor` draws a pseudocolor - plot on a triangular grid. - - + :meth:`matplotlib.axes.Axes.triplot` draws a triangular grid - as lines and/or markers. - -Changes in 0.99 -=============== - -* pylab no longer provides a load and save function. These are - available in matplotlib.mlab, or you can use numpy.loadtxt and - numpy.savetxt for text files, or np.save and np.load for binary - numpy arrays. - -* User-generated colormaps can now be added to the set recognized - by :func:`matplotlib.cm.get_cmap`. Colormaps can be made the - default and applied to the current image using - :func:`matplotlib.pyplot.set_cmap`. - -* changed use_mrecords default to False in mlab.csv2rec since this is - partially broken - -* Axes instances no longer have a "frame" attribute. Instead, use the - new "spines" attribute. Spines is a dictionary where the keys are - the names of the spines (e.g., 'left','right' and so on) and the - values are the artists that draw the spines. For normal - (rectilinear) axes, these artists are Line2D instances. For other - axes (such as polar axes), these artists may be Patch instances. - -* Polar plots no longer accept a resolution kwarg. Instead, each Path - must specify its own number of interpolation steps. This is - unlikely to be a user-visible change -- if interpolation of data is - required, that should be done before passing it to matplotlib. - -Changes for 0.98.x -================== -* psd(), csd(), and cohere() will now automatically wrap negative - frequency components to the beginning of the returned arrays. - This is much more sensible behavior and makes them consistent - with specgram(). The previous behavior was more of an oversight - than a design decision. - -* Added new keyword parameters *nonposx*, *nonposy* to - :class:`matplotlib.axes.Axes` methods that set log scale - parameters. The default is still to mask out non-positive - values, but the kwargs accept 'clip', which causes non-positive - values to be replaced with a very small positive value. - -* Added new :func:`matplotlib.pyplot.fignum_exists` and - :func:`matplotlib.pyplot.get_fignums`; they merely expose - information that had been hidden in :mod:`matplotlib._pylab_helpers`. - -* Deprecated numerix package. - -* Added new :func:`matplotlib.image.imsave` and exposed it to the - :mod:`matplotlib.pyplot` interface. - -* Remove support for pyExcelerator in exceltools -- use xlwt - instead - -* Changed the defaults of acorr and xcorr to use usevlines=True, - maxlags=10 and normed=True since these are the best defaults - -* Following keyword parameters for :class:`matplotlib.label.Label` are now - deprecated and new set of parameters are introduced. The new parameters - are given as a fraction of the font-size. Also, *scatteryoffsets*, - *fancybox* and *columnspacing* are added as keyword parameters. - - ================ ================ - Deprecated New - ================ ================ - pad borderpad - labelsep labelspacing - handlelen handlelength - handlestextsep handletextpad - axespad borderaxespad - ================ ================ - - -* Removed the configobj and experimental traits rc support - -* Modified :func:`matplotlib.mlab.psd`, :func:`matplotlib.mlab.csd`, - :func:`matplotlib.mlab.cohere`, and :func:`matplotlib.mlab.specgram` - to scale one-sided densities by a factor of 2. Also, optionally - scale the densities by the sampling frequency, which gives true values - of densities that can be integrated by the returned frequency values. - This also gives better MATLAB compatibility. The corresponding - :class:`matplotlib.axes.Axes` methods and :mod:`matplotlib.pyplot` - functions were updated as well. - -* Font lookup now uses a nearest-neighbor approach rather than an - exact match. Some fonts may be different in plots, but should be - closer to what was requested. - -* :meth:`matplotlib.axes.Axes.set_xlim`, - :meth:`matplotlib.axes.Axes.set_ylim` now return a copy of the - :attr:`viewlim` array to avoid modify-in-place surprises. - -* :meth:`matplotlib.afm.AFM.get_fullname` and - :meth:`matplotlib.afm.AFM.get_familyname` no longer raise an - exception if the AFM file does not specify these optional - attributes, but returns a guess based on the required FontName - attribute. - -* Changed precision kwarg in :func:`matplotlib.pyplot.spy`; default is - 0, and the string value 'present' is used for sparse arrays only to - show filled locations. - -* :class:`matplotlib.collections.EllipseCollection` added. - -* Added ``angles`` kwarg to :func:`matplotlib.pyplot.quiver` for more - flexible specification of the arrow angles. - -* Deprecated (raise NotImplementedError) all the mlab2 functions from - :mod:`matplotlib.mlab` out of concern that some of them were not - clean room implementations. - -* Methods :meth:`matplotlib.collections.Collection.get_offsets` and - :meth:`matplotlib.collections.Collection.set_offsets` added to - :class:`~matplotlib.collections.Collection` base class. - -* :attr:`matplotlib.figure.Figure.figurePatch` renamed - :attr:`matplotlib.figure.Figure.patch`; - :attr:`matplotlib.axes.Axes.axesPatch` renamed - :attr:`matplotlib.axes.Axes.patch`; - :attr:`matplotlib.axes.Axes.axesFrame` renamed - :attr:`matplotlib.axes.Axes.frame`. - :meth:`matplotlib.axes.Axes.get_frame`, which returns - :attr:`matplotlib.axes.Axes.patch`, is deprecated. - -* Changes in the :class:`matplotlib.contour.ContourLabeler` attributes - (:func:`matplotlib.pyplot.clabel` function) so that they all have a - form like ``.labelAttribute``. The three attributes that are most - likely to be used by end users, ``.cl``, ``.cl_xy`` and - ``.cl_cvalues`` have been maintained for the moment (in addition to - their renamed versions), but they are deprecated and will eventually - be removed. - -* Moved several functions in :mod:`matplotlib.mlab` and - :mod:`matplotlib.cbook` into a separate module - :mod:`matplotlib.numerical_methods` because they were unrelated to - the initial purpose of mlab or cbook and appeared more coherent - elsewhere. - -Changes for 0.98.1 -================== - -* Removed broken :mod:`matplotlib.axes3d` support and replaced it with - a non-implemented error pointing to 0.91.x - -Changes for 0.98.0 -================== - -* :func:`matplotlib.image.imread` now no longer always returns RGBA data---if - the image is luminance or RGB, it will return a MxN or MxNx3 array - if possible. Also uint8 is no longer always forced to float. - -* Rewrote the :class:`matplotlib.cm.ScalarMappable` callback - infrastructure to use :class:`matplotlib.cbook.CallbackRegistry` - rather than custom callback handling. Any users of - :meth:`matplotlib.cm.ScalarMappable.add_observer` of the - :class:`~matplotlib.cm.ScalarMappable` should use the - :attr:`matplotlib.cm.ScalarMappable.callbacks` - :class:`~matplotlib.cbook.CallbackRegistry` instead. - -* New axes function and Axes method provide control over the plot - color cycle: :func:`matplotlib.axes.set_default_color_cycle` and - :meth:`matplotlib.axes.Axes.set_color_cycle`. - -* matplotlib now requires Python 2.4, so :mod:`matplotlib.cbook` will - no longer provide :class:`set`, :func:`enumerate`, :func:`reversed` - or :func:`izip` compatibility functions. - -* In Numpy 1.0, bins are specified by the left edges only. The axes - method :meth:`matplotlib.axes.Axes.hist` now uses future Numpy 1.3 - semantics for histograms. Providing ``binedges``, the last value gives - the upper-right edge now, which was implicitly set to +infinity in - Numpy 1.0. This also means that the last bin doesn't contain upper - outliers any more by default. - -* New axes method and pyplot function, - :func:`~matplotlib.pyplot.hexbin`, is an alternative to - :func:`~matplotlib.pyplot.scatter` for large datasets. It makes - something like a :func:`~matplotlib.pyplot.pcolor` of a 2-D - histogram, but uses hexagonal bins. - -* New kwarg, ``symmetric``, in :class:`matplotlib.ticker.MaxNLocator` - allows one require an axis to be centered around zero. - -* Toolkits must now be imported from ``mpl_toolkits`` (not ``matplotlib.toolkits``) - -Notes about the transforms refactoring --------------------------------------- - -A major new feature of the 0.98 series is a more flexible and -extensible transformation infrastructure, written in Python/Numpy -rather than a custom C extension. - -The primary goal of this refactoring was to make it easier to -extend matplotlib to support new kinds of projections. This is -mostly an internal improvement, and the possible user-visible -changes it allows are yet to come. - -See :mod:`matplotlib.transforms` for a description of the design of -the new transformation framework. - -For efficiency, many of these functions return views into Numpy -arrays. This means that if you hold on to a reference to them, -their contents may change. If you want to store a snapshot of -their current values, use the Numpy array method copy(). - -The view intervals are now stored only in one place -- in the -:class:`matplotlib.axes.Axes` instance, not in the locator instances -as well. This means locators must get their limits from their -:class:`matplotlib.axis.Axis`, which in turn looks up its limits from -the :class:`~matplotlib.axes.Axes`. If a locator is used temporarily -and not assigned to an Axis or Axes, (e.g., in -:mod:`matplotlib.contour`), a dummy axis must be created to store its -bounds. Call :meth:`matplotlib.ticker.Locator.create_dummy_axis` to -do so. - -The functionality of :class:`Pbox` has been merged with -:class:`~matplotlib.transforms.Bbox`. Its methods now all return -copies rather than modifying in place. - -The following lists many of the simple changes necessary to update -code from the old transformation framework to the new one. In -particular, methods that return a copy are named with a verb in the -past tense, whereas methods that alter an object in place are named -with a verb in the present tense. - -:mod:`matplotlib.transforms` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -:meth:`Bbox.get_bounds` :attr:`transforms.Bbox.bounds` ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.width` :attr:`transforms.Bbox.width` ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.height` :attr:`transforms.Bbox.height` ------------------------------------------------------------- ------------------------------------------------------------ -`Bbox.intervalx().get_bounds()` :attr:`transforms.Bbox.intervalx` -`Bbox.intervalx().set_bounds()` [:attr:`Bbox.intervalx` is now a property.] ------------------------------------------------------------- ------------------------------------------------------------ -`Bbox.intervaly().get_bounds()` :attr:`transforms.Bbox.intervaly` -`Bbox.intervaly().set_bounds()` [:attr:`Bbox.intervaly` is now a property.] ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.xmin` :attr:`transforms.Bbox.x0` or - :attr:`transforms.Bbox.xmin` [1]_ ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.ymin` :attr:`transforms.Bbox.y0` or - :attr:`transforms.Bbox.ymin` [1]_ ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.xmax` :attr:`transforms.Bbox.x1` or - :attr:`transforms.Bbox.xmax` [1]_ ------------------------------------------------------------- ------------------------------------------------------------ -:meth:`Bbox.ymax` :attr:`transforms.Bbox.y1` or - :attr:`transforms.Bbox.ymax` [1]_ ------------------------------------------------------------- ------------------------------------------------------------ -`Bbox.overlaps(bboxes)` `Bbox.count_overlaps(bboxes)` ------------------------------------------------------------- ------------------------------------------------------------ -`bbox_all(bboxes)` `Bbox.union(bboxes)` - [:meth:`transforms.Bbox.union` is a staticmethod.] ------------------------------------------------------------- ------------------------------------------------------------ -`lbwh_to_bbox(l, b, w, h)` `Bbox.from_bounds(x0, y0, w, h)` - [:meth:`transforms.Bbox.from_bounds` is a staticmethod.] ------------------------------------------------------------- ------------------------------------------------------------ -`inverse_transform_bbox(trans, bbox)` `Bbox.inverse_transformed(trans)` ------------------------------------------------------------- ------------------------------------------------------------ -`Interval.contains_open(v)` `interval_contains_open(tuple, v)` ------------------------------------------------------------- ------------------------------------------------------------ -`Interval.contains(v)` `interval_contains(tuple, v)` ------------------------------------------------------------- ------------------------------------------------------------ -`identity_transform()` :class:`matplotlib.transforms.IdentityTransform` ------------------------------------------------------------- ------------------------------------------------------------ -`blend_xy_sep_transform(xtrans, ytrans)` `blended_transform_factory(xtrans, ytrans)` ------------------------------------------------------------- ------------------------------------------------------------ -`scale_transform(xs, ys)` `Affine2D().scale(xs[, ys])` ------------------------------------------------------------- ------------------------------------------------------------ -`get_bbox_transform(boxin, boxout)` `BboxTransform(boxin, boxout)` or - `BboxTransformFrom(boxin)` or - `BboxTransformTo(boxout)` ------------------------------------------------------------- ------------------------------------------------------------ -`Transform.seq_xy_tup(points)` `Transform.transform(points)` ------------------------------------------------------------- ------------------------------------------------------------ -`Transform.inverse_xy_tup(points)` `Transform.inverted().transform(points)` -============================================================ ============================================================ - -.. [1] The :class:`~matplotlib.transforms.Bbox` is bound by the points - (x0, y0) to (x1, y1) and there is no defined order to these points, - that is, x0 is not necessarily the left edge of the box. To get - the left edge of the :class:`Bbox`, use the read-only property - :attr:`~matplotlib.transforms.Bbox.xmin`. - -:mod:`matplotlib.axes` -~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`Axes.get_position()` :meth:`matplotlib.axes.Axes.get_position` [2]_ ------------------------------------------------------------- ------------------------------------------------------------ -`Axes.set_position()` :meth:`matplotlib.axes.Axes.set_position` [3]_ ------------------------------------------------------------- ------------------------------------------------------------ -`Axes.toggle_log_lineary()` :meth:`matplotlib.axes.Axes.set_yscale` [4]_ ------------------------------------------------------------- ------------------------------------------------------------ -`Subplot` class removed. -============================================================ ============================================================ - -The :class:`Polar` class has moved to :mod:`matplotlib.projections.polar`. - -.. [2] :meth:`matplotlib.axes.Axes.get_position` used to return a list - of points, now it returns a :class:`matplotlib.transforms.Bbox` - instance. - -.. [3] :meth:`matplotlib.axes.Axes.set_position` now accepts either - four scalars or a :class:`matplotlib.transforms.Bbox` instance. - -.. [4] Since the recfactoring allows for more than two scale types - ('log' or 'linear'), it no longer makes sense to have a toggle. - `Axes.toggle_log_lineary()` has been removed. - -:mod:`matplotlib.artist` -~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`Artist.set_clip_path(path)` `Artist.set_clip_path(path, transform)` [5]_ -============================================================ ============================================================ - -.. [5] :meth:`matplotlib.artist.Artist.set_clip_path` now accepts a - :class:`matplotlib.path.Path` instance and a - :class:`matplotlib.transforms.Transform` that will be applied to - the path immediately before clipping. - -:mod:`matplotlib.collections` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`linestyle` `linestyles` [6]_ -============================================================ ============================================================ - -.. [6] Linestyles are now treated like all other collection - attributes, i.e. a single value or multiple values may be - provided. - -:mod:`matplotlib.colors` -~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`ColorConvertor.to_rgba_list(c)` `ColorConvertor.to_rgba_array(c)` - [:meth:`matplotlib.colors.ColorConvertor.to_rgba_array` - returns an Nx4 Numpy array of RGBA color quadruples.] -============================================================ ============================================================ - -:mod:`matplotlib.contour` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`Contour._segments` :meth:`matplotlib.contour.Contour.get_paths`` [Returns a - list of :class:`matplotlib.path.Path` instances.] -============================================================ ============================================================ - -:mod:`matplotlib.figure` -~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`Figure.dpi.get()` / `Figure.dpi.set()` :attr:`matplotlib.figure.Figure.dpi` *(a property)* -============================================================ ============================================================ - -:mod:`matplotlib.patches` -~~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`Patch.get_verts()` :meth:`matplotlib.patches.Patch.get_path` [Returns a - :class:`matplotlib.path.Path` instance] -============================================================ ============================================================ - -:mod:`matplotlib.backend_bases` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -============================================================ ============================================================ -Old method New method -============================================================ ============================================================ -`GraphicsContext.set_clip_rectangle(tuple)` `GraphicsContext.set_clip_rectangle(bbox)` ------------------------------------------------------------- ------------------------------------------------------------ -`GraphicsContext.get_clip_path()` `GraphicsContext.get_clip_path()` [7]_ ------------------------------------------------------------- ------------------------------------------------------------ -`GraphicsContext.set_clip_path()` `GraphicsContext.set_clip_path()` [8]_ -============================================================ ============================================================ - -:class:`~matplotlib.backend_bases.RendererBase` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -New methods: - - * :meth:`draw_path(self, gc, path, transform, rgbFace) - ` - - * :meth:`draw_markers(self, gc, marker_path, marker_trans, path, - trans, rgbFace) - ` - - * :meth:`draw_path_collection(self, master_transform, cliprect, - clippath, clippath_trans, paths, all_transforms, offsets, - offsetTrans, facecolors, edgecolors, linewidths, linestyles, - antialiaseds) - ` - *[optional]* - -Changed methods: - - * `draw_image(self, x, y, im, bbox)` is now - :meth:`draw_image(self, x, y, im, bbox, clippath, clippath_trans) - ` - -Removed methods: - - * `draw_arc` - - * `draw_line_collection` - - * `draw_line` - - * `draw_lines` - - * `draw_point` - - * `draw_quad_mesh` - - * `draw_poly_collection` - - * `draw_polygon` - - * `draw_rectangle` - - * `draw_regpoly_collection` - -.. [7] :meth:`matplotlib.backend_bases.GraphicsContext.get_clip_path` - returns a tuple of the form (*path*, *affine_transform*), where - *path* is a :class:`matplotlib.path.Path` instance and - *affine_transform* is a :class:`matplotlib.transforms.Affine2D` - instance. - -.. [8] :meth:`matplotlib.backend_bases.GraphicsContext.set_clip_path` - now only accepts a :class:`matplotlib.transforms.TransformedPath` - instance. - -Changes for 0.91.2 -================== - -* For :func:`csv2rec`, checkrows=0 is the new default indicating all rows - will be checked for type inference - -* A warning is issued when an image is drawn on log-scaled axes, since - it will not log-scale the image data. - -* Moved :func:`rec2gtk` to :mod:`matplotlib.toolkits.gtktools` - -* Moved :func:`rec2excel` to :mod:`matplotlib.toolkits.exceltools` - -* Removed, dead/experimental ExampleInfo, Namespace and Importer - code from :mod:`matplotlib.__init__` - -Changes for 0.91.1 -================== - -Changes for 0.91.0 -================== - -* Changed :func:`cbook.is_file_like` to - :func:`cbook.is_writable_file_like` and corrected behavior. - -* Added ax kwarg to :func:`pyplot.colorbar` and - :meth:`Figure.colorbar` so that one can specify the axes object from - which space for the colorbar is to be taken, if one does not want to - make the colorbar axes manually. - -* Changed :func:`cbook.reversed` so it yields a tuple rather than a - (index, tuple). This agrees with the python reversed builtin, - and cbook only defines reversed if python doesn't provide the - builtin. - -* Made skiprows=1 the default on :func:`csv2rec` - -* The gd and paint backends have been deleted. - -* The errorbar method and function now accept additional kwargs - so that upper and lower limits can be indicated by capping the - bar with a caret instead of a straight line segment. - -* The :mod:`matplotlib.dviread` file now has a parser for files like - psfonts.map and pdftex.map, to map TeX font names to external files. - -* The file :mod:`matplotlib.type1font` contains a new class for Type 1 - fonts. Currently it simply reads pfa and pfb format files and - stores the data in a way that is suitable for embedding in pdf - files. In the future the class might actually parse the font to - allow e.g., subsetting. - -* :mod:`matplotlib.FT2Font` now supports :meth:`FT_Attach_File`. In - practice this can be used to read an afm file in addition to a - pfa/pfb file, to get metrics and kerning information for a Type 1 - font. - -* The :class:`AFM` class now supports querying CapHeight and stem - widths. The get_name_char method now has an isord kwarg like - get_width_char. - -* Changed :func:`pcolor` default to shading='flat'; but as noted now in the - docstring, it is preferable to simply use the edgecolor kwarg. - -* The mathtext font commands (``\cal``, ``\rm``, ``\it``, ``\tt``) now - behave as TeX does: they are in effect until the next font change - command or the end of the grouping. Therefore uses of ``$\cal{R}$`` - should be changed to ``${\cal R}$``. Alternatively, you may use the - new LaTeX-style font commands (``\mathcal``, ``\mathrm``, - ``\mathit``, ``\mathtt``) which do affect the following group, - e.g., ``$\mathcal{R}$``. - -* Text creation commands have a new default linespacing and a new - ``linespacing`` kwarg, which is a multiple of the maximum vertical - extent of a line of ordinary text. The default is 1.2; - ``linespacing=2`` would be like ordinary double spacing, for example. - -* Changed default kwarg in - :meth:`matplotlib.colors.Normalize.__init__`` to ``clip=False``; - clipping silently defeats the purpose of the special over, under, - and bad values in the colormap, thereby leading to unexpected - behavior. The new default should reduce such surprises. - -* Made the emit property of :meth:`~matplotlib.axes.Axes.set_xlim` and - :meth:`~matplotlib.axes.Axes.set_ylim` ``True`` by default; removed - the Axes custom callback handling into a 'callbacks' attribute which - is a :class:`~matplotlib.cbook.CallbackRegistry` instance. This now - supports the 'xlim_changed' and 'ylim_changed' Axes events. - -Changes for 0.90.1 -================== - -:: - - The file dviread.py has a (very limited and fragile) dvi reader - for usetex support. The API might change in the future so don't - depend on it yet. - - Removed deprecated support for a float value as a gray-scale; - now it must be a string, like '0.5'. Added alpha kwarg to - ColorConverter.to_rgba_list. - - New method set_bounds(vmin, vmax) for formatters, locators sets - the viewInterval and dataInterval from floats. - - Removed deprecated colorbar_classic. - - Line2D.get_xdata and get_ydata valid_only=False kwarg is replaced - by orig=True. When True, it returns the original data, otherwise - the processed data (masked, converted) - - Some modifications to the units interface. - units.ConversionInterface.tickers renamed to - units.ConversionInterface.axisinfo and it now returns a - units.AxisInfo object rather than a tuple. This will make it - easier to add axis info functionality (e.g., I added a default label - on this iteration) w/o having to change the tuple length and hence - the API of the client code every time new functionality is added. - Also, units.ConversionInterface.convert_to_value is now simply - named units.ConversionInterface.convert. - - Axes.errorbar uses Axes.vlines and Axes.hlines to draw its error - limits int he vertical and horizontal direction. As you'll see - in the changes below, these functions now return a LineCollection - rather than a list of lines. The new return signature for - errorbar is ylins, caplines, errorcollections where - errorcollections is a xerrcollection, yerrcollection - - Axes.vlines and Axes.hlines now create and returns a LineCollection, not a list - of lines. This is much faster. The kwarg signature has changed, - so consult the docs - - MaxNLocator accepts a new Boolean kwarg ('integer') to force - ticks to integer locations. - - Commands that pass an argument to the Text constructor or to - Text.set_text() now accept any object that can be converted - with '%s'. This affects xlabel(), title(), etc. - - Barh now takes a **kwargs dict instead of most of the old - arguments. This helps ensure that bar and barh are kept in sync, - but as a side effect you can no longer pass e.g., color as a - positional argument. - - ft2font.get_charmap() now returns a dict that maps character codes - to glyph indices (until now it was reversed) - - Moved data files into lib/matplotlib so that setuptools' develop - mode works. Re-organized the mpl-data layout so that this source - structure is maintained in the installation. (i.e., the 'fonts' and - 'images' sub-directories are maintained in site-packages.). - Suggest removing site-packages/matplotlib/mpl-data and - ~/.matplotlib/ttffont.cache before installing - -Changes for 0.90.0 -================== - -:: - - All artists now implement a "pick" method which users should not - call. Rather, set the "picker" property of any artist you want to - pick on (the epsilon distance in points for a hit test) and - register with the "pick_event" callback. See - examples/pick_event_demo.py for details - - Bar, barh, and hist have "log" binary kwarg: log=True - sets the ordinate to a log scale. - - Boxplot can handle a list of vectors instead of just - an array, so vectors can have different lengths. - - Plot can handle 2-D x and/or y; it plots the columns. - - Added linewidth kwarg to bar and barh. - - Made the default Artist._transform None (rather than invoking - identity_transform for each artist only to have it overridden - later). Use artist.get_transform() rather than artist._transform, - even in derived classes, so that the default transform will be - created lazily as needed - - New LogNorm subclass of Normalize added to colors.py. - All Normalize subclasses have new inverse() method, and - the __call__() method has a new clip kwarg. - - Changed class names in colors.py to match convention: - normalize -> Normalize, no_norm -> NoNorm. Old names - are still available for now. - - Removed obsolete pcolor_classic command and method. - - Removed lineprops and markerprops from the Annotation code and - replaced them with an arrow configurable with kwarg arrowprops. - See examples/annotation_demo.py - JDH - -Changes for 0.87.7 -================== - -:: - - Completely reworked the annotations API because I found the old - API cumbersome. The new design is much more legible and easy to - read. See matplotlib.text.Annotation and - examples/annotation_demo.py - - markeredgecolor and markerfacecolor cannot be configured in - matplotlibrc any more. Instead, markers are generally colored - automatically based on the color of the line, unless marker colors - are explicitly set as kwargs - NN - - Changed default comment character for load to '#' - JDH - - math_parse_s_ft2font_svg from mathtext.py & mathtext2.py now returns - width, height, svg_elements. svg_elements is an instance of Bunch ( - cmbook.py) and has the attributes svg_glyphs and svg_lines, which are both - lists. - - Renderer.draw_arc now takes an additional parameter, rotation. - It specifies to draw the artist rotated in degrees anti- - clockwise. It was added for rotated ellipses. - - Renamed Figure.set_figsize_inches to Figure.set_size_inches to - better match the get method, Figure.get_size_inches. - - Removed the copy_bbox_transform from transforms.py; added - shallowcopy methods to all transforms. All transforms already - had deepcopy methods. - - FigureManager.resize(width, height): resize the window - specified in pixels - - barh: x and y args have been renamed to width and bottom - respectively, and their order has been swapped to maintain - a (position, value) order. - - bar and barh: now accept kwarg 'edgecolor'. - - bar and barh: The left, height, width and bottom args can - now all be scalars or sequences; see docstring. - - barh: now defaults to edge aligned instead of center - aligned bars - - bar, barh and hist: Added a keyword arg 'align' that - controls between edge or center bar alignment. - - Collections: PolyCollection and LineCollection now accept - vertices or segments either in the original form [(x,y), - (x,y), ...] or as a 2D numerix array, with X as the first column - and Y as the second. Contour and quiver output the numerix - form. The transforms methods Bbox.update() and - Transformation.seq_xy_tups() now accept either form. - - Collections: LineCollection is now a ScalarMappable like - PolyCollection, etc. - - Specifying a grayscale color as a float is deprecated; use - a string instead, e.g., 0.75 -> '0.75'. - - Collections: initializers now accept any mpl color arg, or - sequence of such args; previously only a sequence of rgba - tuples was accepted. - - Colorbar: completely new version and api; see docstring. The - original version is still accessible as colorbar_classic, but - is deprecated. - - Contourf: "extend" kwarg replaces "clip_ends"; see docstring. - Masked array support added to pcolormesh. - - Modified aspect-ratio handling: - Removed aspect kwarg from imshow - Axes methods: - set_aspect(self, aspect, adjustable=None, anchor=None) - set_adjustable(self, adjustable) - set_anchor(self, anchor) - Pylab interface: - axis('image') - - Backend developers: ft2font's load_char now takes a flags - argument, which you can OR together from the LOAD_XXX - constants. - -Changes for 0.86 -================ - -:: - - Matplotlib data is installed into the matplotlib module. - This is similar to package_data. This should get rid of - having to check for many possibilities in _get_data_path(). - The MATPLOTLIBDATA env key is still checked first to allow - for flexibility. - - 1) Separated the color table data from cm.py out into - a new file, _cm.py, to make it easier to find the actual - code in cm.py and to add new colormaps. Everything - from _cm.py is imported by cm.py, so the split should be - transparent. - 2) Enabled automatic generation of a colormap from - a list of colors in contour; see modified - examples/contour_demo.py. - 3) Support for imshow of a masked array, with the - ability to specify colors (or no color at all) for - masked regions, and for regions that are above or - below the normally mapped region. See - examples/image_masked.py. - 4) In support of the above, added two new classes, - ListedColormap, and no_norm, to colors.py, and modified - the Colormap class to include common functionality. Added - a clip kwarg to the normalize class. - -Changes for 0.85 -================ - -:: - - Made xtick and ytick separate props in rc - - made pos=None the default for tick formatters rather than 0 to - indicate "not supplied" - - Removed "feature" of minor ticks which prevents them from - overlapping major ticks. Often you want major and minor ticks at - the same place, and can offset the major ticks with the pad. This - could be made configurable - - Changed the internal structure of contour.py to a more OO style. - Calls to contour or contourf in axes.py or pylab.py now return - a ContourSet object which contains references to the - LineCollections or PolyCollections created by the call, - as well as the configuration variables that were used. - The ContourSet object is a "mappable" if a colormap was used. - - Added a clip_ends kwarg to contourf. From the docstring: - * clip_ends = True - If False, the limits for color scaling are set to the - minimum and maximum contour levels. - True (default) clips the scaling limits. Example: - if the contour boundaries are V = [-100, 2, 1, 0, 1, 2, 100], - then the scaling limits will be [-100, 100] if clip_ends - is False, and [-3, 3] if clip_ends is True. - Added kwargs linewidths, antialiased, and nchunk to contourf. These - are experimental; see the docstring. - - Changed Figure.colorbar(): - kw argument order changed; - if mappable arg is a non-filled ContourSet, colorbar() shows - lines instead hof polygons. - if mappable arg is a filled ContourSet with clip_ends=True, - the endpoints are not labelled, so as to give the - correct impression of open-endedness. - - Changed LineCollection.get_linewidths to get_linewidth, for - consistency. - - -Changes for 0.84 -================ - -:: - - Unified argument handling between hlines and vlines. Both now - take optionally a fmt argument (as in plot) and a keyword args - that can be passed onto Line2D. - - Removed all references to "data clipping" in rc and lines.py since - these were not used and not optimized. I'm sure they'll be - resurrected later with a better implementation when needed. - - 'set' removed - no more deprecation warnings. Use 'setp' instead. - - Backend developers: Added flipud method to image and removed it - from to_str. Removed origin kwarg from backend.draw_image. - origin is handled entirely by the frontend now. - -Changes for 0.83 -================ - -:: - - - Made HOME/.matplotlib the new config dir where the matplotlibrc - file, the ttf.cache, and the tex.cache live. The new default - filenames in .matplotlib have no leading dot and are not hidden. - e.g., the new names are matplotlibrc, tex.cache, and ttffont.cache. - This is how ipython does it so it must be right. - - If old files are found, a warning is issued and they are moved to - the new location. - - - backends/__init__.py no longer imports new_figure_manager, - draw_if_interactive and show from the default backend, but puts - these imports into a call to pylab_setup. Also, the Toolbar is no - longer imported from WX/WXAgg. New usage: - - from backends import pylab_setup - new_figure_manager, draw_if_interactive, show = pylab_setup() - - - Moved Figure.get_width_height() to FigureCanvasBase. It now - returns int instead of float. - -Changes for 0.82 -================ - -:: - - - toolbar import change in GTKAgg, GTKCairo and WXAgg - - - Added subplot config tool to GTK* backends -- note you must now - import the NavigationToolbar2 from your backend of choice rather - than from backend_gtk because it needs to know about the backend - specific canvas -- see examples/embedding_in_gtk2.py. Ditto for - wx backend -- see examples/embedding_in_wxagg.py - - - - hist bin change - - Sean Richards notes there was a problem in the way we created - the binning for histogram, which made the last bin - underrepresented. From his post: - - I see that hist uses the linspace function to create the bins - and then uses searchsorted to put the values in their correct - bin. That's all good but I am confused over the use of linspace - for the bin creation. I wouldn't have thought that it does - what is needed, to quote the docstring it creates a "Linear - spaced array from min to max". For it to work correctly - shouldn't the values in the bins array be the same bound for - each bin? (i.e. each value should be the lower bound of a - bin). To provide the correct bins for hist would it not be - something like - - def bins(xmin, xmax, N): - if N==1: return xmax - dx = (xmax-xmin)/N # instead of N-1 - return xmin + dx*arange(N) - - - This suggestion is implemented in 0.81. My test script with these - changes does not reveal any bias in the binning - - from matplotlib.numerix.mlab import randn, rand, zeros, Float - from matplotlib.mlab import hist, mean - - Nbins = 50 - Ntests = 200 - results = zeros((Ntests,Nbins), typecode=Float) - for i in range(Ntests): - print 'computing', i - x = rand(10000) - n, bins = hist(x, Nbins) - results[i] = n - print mean(results) - - -Changes for 0.81 -================ - -:: - - - pylab and artist "set" functions renamed to setp to avoid clash - with python2.4 built-in set. Current version will issue a - deprecation warning which will be removed in future versions - - - imshow interpolation arguments changes for advanced interpolation - schemes. See help imshow, particularly the interpolation, - filternorm and filterrad kwargs - - - Support for masked arrays has been added to the plot command and - to the Line2D object. Only the valid points are plotted. A - "valid_only" kwarg was added to the get_xdata() and get_ydata() - methods of Line2D; by default it is False, so that the original - data arrays are returned. Setting it to True returns the plottable - points. - - - contour changes: - - Masked arrays: contour and contourf now accept masked arrays as - the variable to be contoured. Masking works correctly for - contour, but a bug remains to be fixed before it will work for - contourf. The "badmask" kwarg has been removed from both - functions. - - Level argument changes: - - Old version: a list of levels as one of the positional - arguments specified the lower bound of each filled region; the - upper bound of the last region was taken as a very large - number. Hence, it was not possible to specify that z values - between 0 and 1, for example, be filled, and that values - outside that range remain unfilled. - - New version: a list of N levels is taken as specifying the - boundaries of N-1 z ranges. Now the user has more control over - what is colored and what is not. Repeated calls to contourf - (with different colormaps or color specifications, for example) - can be used to color different ranges of z. Values of z - outside an expected range are left uncolored. - - Example: - Old: contourf(z, [0, 1, 2]) would yield 3 regions: 0-1, 1-2, and >2. - New: it would yield 2 regions: 0-1, 1-2. If the same 3 regions were - desired, the equivalent list of levels would be [0, 1, 2, - 1e38]. - -Changes for 0.80 -================ - -:: - - - xlim/ylim/axis always return the new limits regardless of - arguments. They now take kwargs which allow you to selectively - change the upper or lower limits while leaving unnamed limits - unchanged. See help(xlim) for example - -Changes for 0.73 -================ - -:: - - - Removed deprecated ColormapJet and friends - - - Removed all error handling from the verbose object - - - figure num of zero is now allowed - -Changes for 0.72 -================ - -:: - - - Line2D, Text, and Patch copy_properties renamed update_from and - moved into artist base class - - - LineCollecitons.color renamed to LineCollections.set_color for - consistency with set/get introspection mechanism, - - - pylab figure now defaults to num=None, which creates a new figure - with a guaranteed unique number - - - contour method syntax changed - now it is MATLAB compatible - - unchanged: contour(Z) - old: contour(Z, x=Y, y=Y) - new: contour(X, Y, Z) - - see http://matplotlib.sf.net/matplotlib.pylab.html#-contour - - - - Increased the default resolution for save command. - - - Renamed the base attribute of the ticker classes to _base to avoid conflict - with the base method. Sitt for subs - - - subs=none now does autosubbing in the tick locator. - - - New subplots that overlap old will delete the old axes. If you - do not want this behavior, use fig.add_subplot or the axes - command - -Changes for 0.71 -================ - -:: - - Significant numerix namespace changes, introduced to resolve - namespace clashes between python built-ins and mlab names. - Refactored numerix to maintain separate modules, rather than - folding all these names into a single namespace. See the following - mailing list threads for more information and background - - http://sourceforge.net/mailarchive/forum.php?thread_id=6398890&forum_id=36187 - http://sourceforge.net/mailarchive/forum.php?thread_id=6323208&forum_id=36187 - - - OLD usage - - from matplotlib.numerix import array, mean, fft - - NEW usage - - from matplotlib.numerix import array - from matplotlib.numerix.mlab import mean - from matplotlib.numerix.fft import fft - - numerix dir structure mirrors numarray (though it is an incomplete - implementation) - - numerix - numerix/mlab - numerix/linear_algebra - numerix/fft - numerix/random_array - - but of course you can use 'numerix : Numeric' and still get the - symbols. - - pylab still imports most of the symbols from Numerix, MLab, fft, - etc, but is more cautious. For names that clash with python names - (min, max, sum), pylab keeps the builtins and provides the numeric - versions with an a* prefix, e.g., (amin, amax, asum) - -Changes for 0.70 -================ - -:: - - MplEvent factored into a base class Event and derived classes - MouseEvent and KeyEvent - - Removed definct set_measurement in wx toolbar - -Changes for 0.65.1 -================== - -:: - - removed add_axes and add_subplot from backend_bases. Use - figure.add_axes and add_subplot instead. The figure now manages the - current axes with gca and sca for get and set current axes. If you - have code you are porting which called, e.g., figmanager.add_axes, you - can now simply do figmanager.canvas.figure.add_axes. - -Changes for 0.65 -================ - -:: - - - mpl_connect and mpl_disconnect in the MATLAB interface renamed to - connect and disconnect - - Did away with the text methods for angle since they were ambiguous. - fontangle could mean fontstyle (obligue, etc) or the rotation of the - text. Use style and rotation instead. - -Changes for 0.63 -================ - -:: - - Dates are now represented internally as float days since 0001-01-01, - UTC. - - All date tickers and formatters are now in matplotlib.dates, rather - than matplotlib.tickers - - converters have been abolished from all functions and classes. - num2date and date2num are now the converter functions for all date - plots - - Most of the date tick locators have a different meaning in their - constructors. In the prior implementation, the first argument was a - base and multiples of the base were ticked. e.g., - - HourLocator(5) # old: tick every 5 minutes - - In the new implementation, the explicit points you want to tick are - provided as a number or sequence - - HourLocator(range(0,5,61)) # new: tick every 5 minutes - - This gives much greater flexibility. I have tried to make the - default constructors (no args) behave similarly, where possible. - - Note that YearLocator still works under the base/multiple scheme. - The difference between the YearLocator and the other locators is - that years are not recurrent. - - - Financial functions: - - matplotlib.finance.quotes_historical_yahoo(ticker, date1, date2) - - date1, date2 are now datetime instances. Return value is a list - of quotes where the quote time is a float - days since gregorian - start, as returned by date2num - - See examples/finance_demo.py for example usage of new API - -Changes for 0.61 -================ - -:: - - canvas.connect is now deprecated for event handling. use - mpl_connect and mpl_disconnect instead. The callback signature is - func(event) rather than func(widget, event) - -Changes for 0.60 -================ - -:: - - ColormapJet and Grayscale are deprecated. For backwards - compatibility, they can be obtained either by doing - - from matplotlib.cm import ColormapJet - - or - - from matplotlib.matlab import * - - They are replaced by cm.jet and cm.grey - -Changes for 0.54.3 -================== - -:: - - removed the set_default_font / get_default_font scheme from the - font_manager to unify customization of font defaults with the rest of - the rc scheme. See examples/font_properties_demo.py and help(rc) in - matplotlib.matlab. - -Changes for 0.54 -================ - -MATLAB interface ----------------- - -dpi -~~~ - -Several of the backends used a PIXELS_PER_INCH hack that I added to -try and make images render consistently across backends. This just -complicated matters. So you may find that some font sizes and line -widths appear different than before. Apologies for the -inconvenience. You should set the dpi to an accurate value for your -screen to get true sizes. - - -pcolor and scatter -~~~~~~~~~~~~~~~~~~ - -There are two changes to the MATLAB interface API, both involving the -patch drawing commands. For efficiency, pcolor and scatter have been -rewritten to use polygon collections, which are a new set of objects -from matplotlib.collections designed to enable efficient handling of -large collections of objects. These new collections make it possible -to build large scatter plots or pcolor plots with no loops at the -python level, and are significantly faster than their predecessors. -The original pcolor and scatter functions are retained as -pcolor_classic and scatter_classic. - -The return value from pcolor is a PolyCollection. Most of the -propertes that are available on rectangles or other patches are also -available on PolyCollections, e.g., you can say:: - - c = scatter(blah, blah) - c.set_linewidth(1.0) - c.set_facecolor('r') - c.set_alpha(0.5) - -or:: - - c = scatter(blah, blah) - set(c, 'linewidth', 1.0, 'facecolor', 'r', 'alpha', 0.5) - - -Because the collection is a single object, you no longer need to loop -over the return value of scatter or pcolor to set properties for the -entire list. - -If you want the different elements of a collection to vary on a -property, e.g., to have different line widths, see matplotlib.collections -for a discussion on how to set the properties as a sequence. - -For scatter, the size argument is now in points^2 (the area of the -symbol in points) as in MATLAB and is not in data coords as before. -Using sizes in data coords caused several problems. So you will need -to adjust your size arguments accordingly or use scatter_classic. - -mathtext spacing -~~~~~~~~~~~~~~~~ - -For reasons not clear to me (and which I'll eventually fix) spacing no -longer works in font groups. However, I added three new spacing -commands which compensate for this '\ ' (regular space), '\/' (small -space) and '\hspace{frac}' where frac is a fraction of fontsize in -points. You will need to quote spaces in font strings, is:: - - title(r'$\rm{Histogram\ of\ IQ:}\ \mu=100,\ \sigma=15$') - - - -Object interface - Application programmers ------------------------------------------- - -Autoscaling -~~~~~~~~~~~ - - The x and y axis instances no longer have autoscale view. These are - handled by axes.autoscale_view - -Axes creation -~~~~~~~~~~~~~ - - You should not instantiate your own Axes any more using the OO API. - Rather, create a Figure as before and in place of:: - - f = Figure(figsize=(5,4), dpi=100) - a = Subplot(f, 111) - f.add_axis(a) - - use:: - - f = Figure(figsize=(5,4), dpi=100) - a = f.add_subplot(111) - - That is, add_axis no longer exists and is replaced by:: - - add_axes(rect, axisbg=defaultcolor, frameon=True) - add_subplot(num, axisbg=defaultcolor, frameon=True) - -Artist methods -~~~~~~~~~~~~~~ - - If you define your own Artists, you need to rename the _draw method - to draw - -Bounding boxes -~~~~~~~~~~~~~~ - - matplotlib.transforms.Bound2D is replaced by - matplotlib.transforms.Bbox. If you want to construct a bbox from - left, bottom, width, height (the signature for Bound2D), use - matplotlib.transforms.lbwh_to_bbox, as in - - bbox = clickBBox = lbwh_to_bbox(left, bottom, width, height) - - The Bbox has a different API than the Bound2D. e.g., if you want to - get the width and height of the bbox - - OLD:: - width = fig.bbox.x.interval() - height = fig.bbox.y.interval() - - New:: - width = fig.bbox.width() - height = fig.bbox.height() - - - - -Object constructors -~~~~~~~~~~~~~~~~~~~ - - You no longer pass the bbox, dpi, or transforms to the various - Artist constructors. The old way or creating lines and rectangles - was cumbersome because you had to pass so many attributes to the - Line2D and Rectangle classes not related directly to the geometry - and properties of the object. Now default values are added to the - object when you call axes.add_line or axes.add_patch, so they are - hidden from the user. - - If you want to define a custom transformation on these objects, call - o.set_transform(trans) where trans is a Transformation instance. - - In prior versions of you wanted to add a custom line in data coords, - you would have to do - - l = Line2D(dpi, bbox, x, y, - color = color, - transx = transx, - transy = transy, - ) - - now all you need is - - l = Line2D(x, y, color=color) - - and the axes will set the transformation for you (unless you have - set your own already, in which case it will eave it unchanged) - -Transformations -~~~~~~~~~~~~~~~ - - The entire transformation architecture has been rewritten. - Previously the x and y transformations where stored in the xaxis and - yaxis instances. The problem with this approach is it only allows - for separable transforms (where the x and y transformations don't - depend on one another). But for cases like polar, they do. Now - transformations operate on x,y together. There is a new base class - matplotlib.transforms.Transformation and two concrete - implementations, matplotlib.transforms.SeparableTransformation and - matplotlib.transforms.Affine. The SeparableTransformation is - constructed with the bounding box of the input (this determines the - rectangular coordinate system of the input, i.e., the x and y view - limits), the bounding box of the display, and possibly nonlinear - transformations of x and y. The 2 most frequently used - transformations, data coordinates -> display and axes coordinates -> - display are available as ax.transData and ax.transAxes. See - alignment_demo.py which uses axes coords. - - Also, the transformations should be much faster now, for two reasons - - * they are written entirely in extension code - - * because they operate on x and y together, they can do the entire - transformation in one loop. Earlier I did something along the - lines of:: - - xt = sx*func(x) + tx - yt = sy*func(y) + ty - - Although this was done in numerix, it still involves 6 length(x) - for-loops (the multiply, add, and function evaluation each for x - and y). Now all of that is done in a single pass. - - - If you are using transformations and bounding boxes to get the - cursor position in data coordinates, the method calls are a little - different now. See the updated examples/coords_demo.py which shows - you how to do this. - - Likewise, if you are using the artist bounding boxes to pick items - on the canvas with the GUI, the bbox methods are somewhat - different. You will need to see the updated - examples/object_picker.py. - - See unit/transforms_unit.py for many examples using the new - transformations. - - -Changes for 0.50 -================ - -:: - - * refactored Figure class so it is no longer backend dependent. - FigureCanvasBackend takes over the backend specific duties of the - Figure. matplotlib.backend_bases.FigureBase moved to - matplotlib.figure.Figure. - - * backends must implement FigureCanvasBackend (the thing that - controls the figure and handles the events if any) and - FigureManagerBackend (wraps the canvas and the window for MATLAB - interface). FigureCanvasBase implements a backend switching - mechanism - - * Figure is now an Artist (like everything else in the figure) and - is totally backend independent - - * GDFONTPATH renamed to TTFPATH - - * backend faceColor argument changed to rgbFace - - * colormap stuff moved to colors.py - - * arg_to_rgb in backend_bases moved to class ColorConverter in - colors.py - - * GD users must upgrade to gd-2.0.22 and gdmodule-0.52 since new gd - features (clipping, antialiased lines) are now used. - - * Renderer must implement points_to_pixels - - Migrating code: - - MATLAB interface: - - The only API change for those using the MATLAB interface is in how - you call figure redraws for dynamically updating figures. In the - old API, you did - - fig.draw() - - In the new API, you do - - manager = get_current_fig_manager() - manager.canvas.draw() - - See the examples system_monitor.py, dynamic_demo.py, and anim.py - - API - - There is one important API change for application developers. - Figure instances used subclass GUI widgets that enabled them to be - placed directly into figures. e.g., FigureGTK subclassed - gtk.DrawingArea. Now the Figure class is independent of the - backend, and FigureCanvas takes over the functionality formerly - handled by Figure. In order to include figures into your apps, - you now need to do, for example - - # gtk example - fig = Figure(figsize=(5,4), dpi=100) - canvas = FigureCanvasGTK(fig) # a gtk.DrawingArea - canvas.show() - vbox.pack_start(canvas) - - If you use the NavigationToolbar, this in now intialized with a - FigureCanvas, not a Figure. The examples embedding_in_gtk.py, - embedding_in_gtk2.py, and mpl_with_glade.py all reflect the new - API so use these as a guide. - - All prior calls to - - figure.draw() and - figure.print_figure(args) - - should now be - - canvas.draw() and - canvas.print_figure(args) - - Apologies for the inconvenience. This refactorization brings - significant more freedom in developing matplotlib and should bring - better plotting capabilities, so I hope the inconvenience is worth - it. - -Changes for 0.42 -================ - -:: - - * Refactoring AxisText to be backend independent. Text drawing and - get_window_extent functionality will be moved to the Renderer. - - * backend_bases.AxisTextBase is now text.Text module - - * All the erase and reset functionality removed from AxisText - not - needed with double buffered drawing. Ditto with state change. - Text instances have a get_prop_tup method that returns a hashable - tuple of text properties which you can use to see if text props - have changed, e.g., by caching a font or layout instance in a dict - with the prop tup as a key -- see RendererGTK.get_pango_layout in - backend_gtk for an example. - - * Text._get_xy_display renamed Text.get_xy_display - - * Artist set_renderer and wash_brushes methods removed - - * Moved Legend class from matplotlib.axes into matplotlib.legend - - * Moved Tick, XTick, YTick, Axis, XAxis, YAxis from matplotlib.axes - to matplotlib.axis - - * moved process_text_args to matplotlib.text - - * After getting Text handled in a backend independent fashion, the - import process is much cleaner since there are no longer cyclic - dependencies - - * matplotlib.matlab._get_current_fig_manager renamed to - matplotlib.matlab.get_current_fig_manager to allow user access to - the GUI window attribute, e.g., figManager.window for GTK and - figManager.frame for wx - -Changes for 0.40 -================ - -:: - - - Artist - * __init__ takes a DPI instance and a Bound2D instance which is - the bounding box of the artist in display coords - * get_window_extent returns a Bound2D instance - * set_size is removed; replaced by bbox and dpi - * the clip_gc method is removed. Artists now clip themselves with - their box - * added _clipOn boolean attribute. If True, gc clip to bbox. - - - AxisTextBase - * Initialized with a transx, transy which are Transform instances - * set_drawing_area removed - * get_left_right and get_top_bottom are replaced by get_window_extent - - - Line2D Patches now take transx, transy - * Initialized with a transx, transy which are Transform instances - - - Patches - * Initialized with a transx, transy which are Transform instances - - - FigureBase attributes dpi is a DPI intance rather than scalar and - new attribute bbox is a Bound2D in display coords, and I got rid - of the left, width, height, etc... attributes. These are now - accessible as, for example, bbox.x.min is left, bbox.x.interval() - is width, bbox.y.max is top, etc... - - - GcfBase attribute pagesize renamed to figsize - - - Axes - * removed figbg attribute - * added fig instance to __init__ - * resizing is handled by figure call to resize. - - - Subplot - * added fig instance to __init__ - - - Renderer methods for patches now take gcEdge and gcFace instances. - gcFace=None takes the place of filled=False - - - True and False symbols provided by cbook in a python2.3 compatible - way - - - new module transforms supplies Bound1D, Bound2D and Transform - instances and more - - - Changes to the MATLAB helpers API - - * _matlab_helpers.GcfBase is renamed by Gcf. Backends no longer - need to derive from this class. Instead, they provide a factory - function new_figure_manager(num, figsize, dpi). The destroy - method of the GcfDerived from the backends is moved to the derived - FigureManager. - - * FigureManagerBase moved to backend_bases - - * Gcf.get_all_figwins renamed to Gcf.get_all_fig_managers - - Jeremy: - - Make sure to self._reset = False in AxisTextWX._set_font. This was - something missing in my backend code. diff --git a/doc/api/api_changes/2015-12-30-JHN.rst b/doc/api/api_changes/2015-12-30-JHN.rst deleted file mode 100644 index 64b826c26ef7..000000000000 --- a/doc/api/api_changes/2015-12-30-JHN.rst +++ /dev/null @@ -1,7 +0,0 @@ -`mpl_toolkits.axes_grid` has been deprecated -```````````````````````````````````````````` - -All functionallity from `mpl_toolkits.axes_grid` can be found in either -`mpl_toolkits.axes_grid1` or `mpl_toolkits.axisartist`. Axes classes from -`mpl_toolkits.axes_grid` based on `Axis` from `mpl_toolkits.axisartist` can be -found in `mpl_toolkits.axisartist` diff --git a/doc/api/api_changes/2016-08-02-toggle-grids.rst b/doc/api/api_changes/2016-08-02-toggle-grids.rst deleted file mode 100644 index fb70385afd0d..000000000000 --- a/doc/api/api_changes/2016-08-02-toggle-grids.rst +++ /dev/null @@ -1,9 +0,0 @@ -Improved toggling of the axes grids ------------------------------------ -The `g` key binding now switches the states of the `x` and `y` grids -independently (by cycling through all four on/off combinations). - -The new `G` key binding switches the states of the minor grids. - -Both bindings are disabled if only a subset of the grid lines (in either -direction) is visible, to avoid making irreversible changes to the figure. diff --git a/doc/api/api_changes/2016-09-28-AL_emptylegend.rst b/doc/api/api_changes/2016-09-28-AL_emptylegend.rst deleted file mode 100644 index 00c78cf6b95a..000000000000 --- a/doc/api/api_changes/2016-09-28-AL_emptylegend.rst +++ /dev/null @@ -1,5 +0,0 @@ -Removal of warning on empty legends -``````````````````````````````````` - -``plt.legend`` used to issue a warning when no labeled artist could be found. -This warning has been removed. diff --git a/doc/api/api_changes/README.rst b/doc/api/api_changes/README.rst deleted file mode 100644 index f317cab10d41..000000000000 --- a/doc/api/api_changes/README.rst +++ /dev/null @@ -1,9 +0,0 @@ -For changes which require an entry in `api_changes.rst` please create -a file in this folder with the name :file:`YYYY-MM-DD-[initials].rst` -(ex :file:`2014-07-31-TAC.rst`) with contents following the form: :: - - Brief description of change - ``````````````````````````` - - Long description of change, justification, and work-arounds to - maintain old behavior (if any). diff --git a/doc/api/api_changes/code_removal.rst b/doc/api/api_changes/code_removal.rst deleted file mode 100644 index bed0560a353e..000000000000 --- a/doc/api/api_changes/code_removal.rst +++ /dev/null @@ -1,27 +0,0 @@ -Code Removal -```````````` - -matplotlib.delaunay -------------------- -Remove the delaunay triangulation code which is now handled by Qhull -via ``matplotlib.tri`` - - -qt4_compat.py -------------- -Moved to ``qt_compat.py``. Renamed because it now handles Qt5 as well. - - -Deprecated methods ------------------- - -The ``GraphicsContextBase.set_graylevel``, ``FigureCanvasBase.onHilite`` and -``mpl_toolkits.axes_grid1.mpl_axes.Axes.toggle_axisline`` methods have been -removed. - - -`Axes.set_aspect("normal")` ---------------------------- - -Support for setting an ``Axes``' aspect to ``"normal"`` has been removed, in -favor of the synonym ``"auto"``. diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index a146df58c9f8..f256d2b7164e 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -1,86 +1,70 @@ .. _artist-api: -=================== - ``artist`` Module -=================== - -.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.BrokenBarHCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ClabelText matplotlib.figure.Figure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.Wedge matplotlib.patches.YAArrow matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.CustomCell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text matplotlib.text.TextWithDash - :parts: 1 - :private-bases: +********************* +``matplotlib.artist`` +********************* +.. automodule:: matplotlib.artist + :no-members: + :no-undoc-members: +Inheritance Diagrams +==================== -.. automodule:: matplotlib.artist +.. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.CircleCollection matplotlib.collections.Collection matplotlib.collections.EllipseCollection matplotlib.collections.EventCollection matplotlib.collections.LineCollection matplotlib.collections.PatchCollection matplotlib.collections.PathCollection matplotlib.collections.PolyCollection matplotlib.collections.QuadMesh matplotlib.collections.RegularPolyCollection matplotlib.collections.StarPolygonCollection matplotlib.collections.TriMesh matplotlib.collections._CollectionWithSizes matplotlib.contour.ContourSet matplotlib.contour.QuadContourSet matplotlib.figure.FigureBase matplotlib.figure.Figure matplotlib.figure.SubFigure matplotlib.image.AxesImage matplotlib.image.BboxImage matplotlib.image.FigureImage matplotlib.image.NonUniformImage matplotlib.image.PcolorImage matplotlib.image._ImageBase matplotlib.legend.Legend matplotlib.lines.Line2D matplotlib.offsetbox.AnchoredOffsetbox matplotlib.offsetbox.AnchoredText matplotlib.offsetbox.AnnotationBbox matplotlib.offsetbox.AuxTransformBox matplotlib.offsetbox.DrawingArea matplotlib.offsetbox.HPacker matplotlib.offsetbox.OffsetBox matplotlib.offsetbox.OffsetImage matplotlib.offsetbox.PackerBase matplotlib.offsetbox.PaddedBox matplotlib.offsetbox.TextArea matplotlib.offsetbox.VPacker matplotlib.patches.Annulus matplotlib.patches.Arc matplotlib.patches.Arrow matplotlib.patches.Circle matplotlib.patches.CirclePolygon matplotlib.patches.ConnectionPatch matplotlib.patches.Ellipse matplotlib.patches.FancyArrow matplotlib.patches.FancyArrowPatch matplotlib.patches.FancyBboxPatch matplotlib.patches.Patch matplotlib.patches.PathPatch matplotlib.patches.Polygon matplotlib.patches.Rectangle matplotlib.patches.RegularPolygon matplotlib.patches.Shadow matplotlib.patches.StepPatch matplotlib.patches.Wedge matplotlib.projections.geo.AitoffAxes matplotlib.projections.geo.GeoAxes matplotlib.projections.geo.HammerAxes matplotlib.projections.geo.LambertAxes matplotlib.projections.geo.MollweideAxes matplotlib.projections.polar.PolarAxes matplotlib.projections.polar.RadialAxis matplotlib.projections.polar.RadialTick matplotlib.projections.polar.ThetaAxis matplotlib.projections.polar.ThetaTick matplotlib.quiver.Barbs matplotlib.quiver.Quiver matplotlib.quiver.QuiverKey matplotlib.spines.Spine matplotlib.table.Cell matplotlib.table.Table matplotlib.text.Annotation matplotlib.text.Text matplotlib.tri.TriContourSet + :parts: 1 + :private-bases: ``Artist`` class ================ +.. autoclass:: Artist + :no-members: + :no-undoc-members: Interactive ----------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: Artist.add_callback - Artist.format_cursor_data - Artist.get_contains + Artist.remove_callback + Artist.pchanged Artist.get_cursor_data - Artist.get_picker - Artist.hitlist + Artist.format_cursor_data + Artist.set_mouseover + Artist.get_mouseover Artist.mouseover - Artist.pchanged + Artist.contains Artist.pick Artist.pickable - Artist.remove_callback - Artist.set_contains Artist.set_picker - Artist.contains - - -Margins and Autoscaling ------------------------ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - Artist.get_bottom_margin - Artist.get_left_margin - Artist.get_margins - Artist.get_top_margin - Artist.margins - Artist.left_margin - Artist.get_right_margin - Artist.bottom_margin - Artist.right_margin - Artist.set_bottom_margin - Artist.set_left_margin - Artist.set_margins - Artist.set_right_margin - Artist.set_top_margin - Artist.top_margin + Artist.get_picker Clipping -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: - Artist.get_clip_box + Artist.set_clip_on Artist.get_clip_on - Artist.get_clip_path Artist.set_clip_box - Artist.set_clip_on + Artist.get_clip_box Artist.set_clip_path + Artist.get_clip_path Bulk Properties --------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -89,74 +73,73 @@ Bulk Properties Artist.properties Artist.set - Drawing ------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: Artist.draw - Artist.get_animated Artist.set_animated + Artist.get_animated - Artist.get_agg_filter - + Artist.set_alpha Artist.get_alpha + Artist.set_snap Artist.get_snap + Artist.set_visible Artist.get_visible + Artist.zorder + Artist.set_zorder Artist.get_zorder Artist.set_agg_filter - Artist.set_alpha Artist.set_sketch_params - Artist.set_snap - Artist.get_rasterized Artist.get_sketch_params - Artist.set_path_effects Artist.set_rasterized - Artist.zorder - Artist.set_visible - Artist.set_zorder - Artist.get_window_extent + Artist.get_rasterized + Artist.set_path_effects Artist.get_path_effects - Artist.get_transformed_clip_path_and_affine - + Artist.get_agg_filter + Artist.get_window_extent + Artist.get_tightbbox + Artist.get_transformed_clip_path_and_affine Figure and Axes --------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: Artist.remove Artist.axes - Artist.get_axes - Artist.set_axes Artist.set_figure Artist.get_figure - Artist.is_figure_set + Artist.figure Children -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: Artist.get_children Artist.findobj - Transform --------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -164,12 +147,11 @@ Transform Artist.get_transform Artist.is_transform_set - - Units ----- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: @@ -181,30 +163,35 @@ Metadata -------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: - Artist.get_gid - Artist.get_label Artist.set_gid + Artist.get_gid Artist.set_label - Artist.get_url + Artist.get_label Artist.set_url - Artist.aname + Artist.get_url -Stale ------ +Miscellaneous +------------- .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: + Artist.sticky_edges + Artist.set_in_layout + Artist.get_in_layout Artist.stale Functions ========= .. autosummary:: + :template: autosummary.rst :toctree: _as_gen :nosignatures: diff --git a/doc/api/axes_api.rst b/doc/api/axes_api.rst index ef535514b11f..1868794dd921 100644 --- a/doc/api/axes_api.rst +++ b/doc/api/axes_api.rst @@ -1,15 +1,44 @@ -================ - ``Axes`` class -================ -.. currentmodule:: matplotlib.axes +******************* +``matplotlib.axes`` +******************* + +The `~.axes.Axes` class represents one (sub-)plot in a figure. It contains the +plotted data, axis ticks, labels, title, legend, etc. Its methods are the main +interface for manipulating the plot. -.. autoclass:: Axes +.. currentmodule:: matplotlib.axes .. contents:: Table of Contents :depth: 2 :local: :backlinks: entry + :class: multicol-toc + +.. automodule:: matplotlib.axes + :no-members: + :no-undoc-members: + +The Axes class +============== + +.. autosummary:: + :toctree: _as_gen + :template: autosummary_class_only.rst + :nosignatures: + + Axes +Attributes +---------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Axes.viewLim + Axes.dataLim + Axes.spines Plotting ======== @@ -19,13 +48,13 @@ Basic .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.plot Axes.errorbar Axes.scatter - Axes.plot_date Axes.step Axes.loglog @@ -37,11 +66,14 @@ Basic Axes.bar Axes.barh + Axes.bar_label + Axes.grouped_bar Axes.stem Axes.eventplot Axes.pie + Axes.pie_label Axes.stackplot @@ -56,18 +88,21 @@ Spans .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.axhline Axes.axhspan Axes.axvline Axes.axvspan + Axes.axline Spectral -------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.acorr @@ -85,41 +120,47 @@ Statistics .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: + Axes.ecdf Axes.boxplot Axes.violinplot - Axes.violin Axes.bxp + Axes.violin Binned ------ .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.hexbin Axes.hist Axes.hist2d + Axes.stairs Contours -------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.clabel Axes.contour Axes.contourf -Array ------ +2D arrays +--------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.imshow @@ -129,11 +170,12 @@ Array Axes.pcolormesh Axes.spy -Unstructured Triangles +Unstructured triangles ---------------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.tripcolor @@ -142,24 +184,31 @@ Unstructured Triangles Axes.tricontourf -Text and Annotations +Text and annotations -------------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.annotate Axes.text Axes.table Axes.arrow + Axes.inset_axes + Axes.indicate_inset + Axes.indicate_inset_zoom + Axes.secondary_xaxis + Axes.secondary_yaxis -Fields ------- +Vector fields +------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.barbs @@ -173,18 +222,19 @@ Clearing .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.cla Axes.clear - Appearance ========== .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: @@ -200,14 +250,8 @@ Appearance Axes.grid - Axes.get_axis_bgcolor Axes.get_facecolor - Axes.get_fc - Axes.set_facecolor - Axes.set_fc - Axes.set_axis_bgcolor - Property cycle @@ -215,63 +259,74 @@ Property cycle .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.set_prop_cycle - Axes.set_color_cycle +.. _axes-api-axis: Axis / limits ============= +.. For families of methods of the form {get,set}_{x,y}foo, try to list them in + the order set_xfoo, get_xfoo, set_yfoo, get_yfoo + +Axis access +----------- + .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - - Axes.get_yaxis + Axes.xaxis + Axes.yaxis Axes.get_xaxis + Axes.get_yaxis - - -Axis Limits and direction +Axis limits and direction ------------------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: + Axes.set_xinverted + Axes.get_xinverted Axes.invert_xaxis - Axes.invert_yaxis Axes.xaxis_inverted + Axes.set_yinverted + Axes.get_yinverted + Axes.invert_yaxis Axes.yaxis_inverted Axes.set_xlim + Axes.get_xlim Axes.set_ylim Axes.get_ylim - Axes.get_xlim Axes.update_datalim - Axes.update_datalim_bounds - Axes.update_datalim_numerix - Axes.set_ybound Axes.set_xbound - Axes.get_ybound Axes.get_xbound + Axes.set_ybound + Axes.get_ybound -Axis Labels, title, and legend +Axis labels, title, and legend ------------------------------ .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - Axes.get_xlabel - Axes.get_ylabel - Axes.set_xlabel + Axes.get_xlabel Axes.set_ylabel + Axes.get_ylabel + Axes.label_outer Axes.set_title Axes.get_title @@ -279,121 +334,103 @@ Axis Labels, title, and legend Axes.get_legend Axes.get_legend_handles_labels - Axis scales ----------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.set_xscale Axes.get_xscale - - Axes.get_yscale Axes.set_yscale + Axes.get_yscale - - -Autoscaling ------------ +Autoscaling and margins +----------------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: + Axes.use_sticky_edges + + Axes.margins + Axes.get_xmargin + Axes.get_ymargin + Axes.set_xmargin + Axes.set_ymargin + Axes.relim Axes.autoscale Axes.autoscale_view - Axes.get_autoscale_on Axes.set_autoscale_on + Axes.get_autoscale_on - Axes.get_autoscalex_on Axes.set_autoscalex_on + Axes.get_autoscalex_on - Axes.get_autoscaley_on Axes.set_autoscaley_on - - - -Margins -~~~~~~~ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - Axes.margins - Axes.set_margins - Axes.get_margins - Axes.bottom_margin - Axes.get_bottom_margin - Axes.get_left_margin - Axes.get_right_margin - Axes.get_top_margin - Axes.left_margin - Axes.right_margin - Axes.set_bottom_margin - Axes.set_left_margin - Axes.set_top_margin - Axes.set_right_margin - Axes.set_xmargin - Axes.top_margin - + Axes.get_autoscaley_on Aspect ratio ------------ .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.apply_aspect - Axes.get_aspect Axes.set_aspect + Axes.get_aspect - Axes.get_adjustable - Axes.set_adjustable + Axes.set_box_aspect + Axes.get_box_aspect + Axes.set_adjustable + Axes.get_adjustable Ticks and tick labels --------------------- - .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - Axes.xaxis_date - Axes.yaxis_date + Axes.set_xticks + Axes.get_xticks + Axes.set_xticklabels + Axes.get_xticklabels Axes.get_xmajorticklabels Axes.get_xminorticklabels - Axes.get_xticklabels + + Axes.get_xgridlines Axes.get_xticklines - Axes.get_xticks - Axes.get_ymajorticklabels - Axes.get_yminorticklabels - Axes.get_yticklabels - Axes.get_yticklines - Axes.get_yticks + Axes.xaxis_date - Axes.minorticks_off - Axes.minorticks_on + Axes.set_yticks + Axes.get_yticks - Axes.set_xticklabels - Axes.set_xticks - Axes.set_ymargin Axes.set_yticklabels - Axes.set_yticks + Axes.get_yticklabels + Axes.get_ymajorticklabels + Axes.get_yminorticklabels - Axes.get_xgridlines Axes.get_ygridlines + Axes.get_yticklines + Axes.yaxis_date + + Axes.minorticks_off + Axes.minorticks_on Axes.ticklabel_format Axes.tick_params @@ -401,12 +438,12 @@ Ticks and tick labels Axes.locator_params - Units ===== .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.convert_xunits @@ -414,14 +451,16 @@ Units Axes.have_units -Adding Artists +Adding artists ============== .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.add_artist + Axes.add_child_axes Axes.add_collection Axes.add_container Axes.add_image @@ -430,24 +469,29 @@ Adding Artists Axes.add_table -Twinning -======== +Twinning and sharing +==================== .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.twinx Axes.twiny + Axes.sharex + Axes.sharey + Axes.get_shared_x_axes Axes.get_shared_y_axes -Axes Position +Axes position ============= .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.get_anchor @@ -456,17 +500,21 @@ Axes Position Axes.get_axes_locator Axes.set_axes_locator + Axes.get_subplotspec + Axes.set_subplotspec + Axes.reset_position Axes.get_position Axes.set_position -Async/Event based +Async/event based ================= .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.stale @@ -480,6 +528,7 @@ Interactive .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: @@ -491,6 +540,9 @@ Interactive Axes.get_navigate_mode Axes.set_navigate_mode + Axes.get_forward_navigation_events + Axes.set_forward_navigation_events + Axes.start_pan Axes.drag_pan Axes.end_pan @@ -500,30 +552,20 @@ Interactive Axes.format_xdata Axes.format_ydata - Axes.hitlist Axes.mouseover Axes.in_axes - Axes.pick - Axes.pickable - Axes.get_picker - Axes.set_picker - - Axes.set_contains - Axes.get_contains - Axes.contains Axes.contains_point Axes.get_cursor_data - Axes.get_cursor_props - Axes.set_cursor_props Children ======== .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.get_children @@ -537,12 +579,12 @@ Drawing .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.draw Axes.draw_artist Axes.redraw_in_frame - Axes.get_renderer_cache Axes.get_rasterization_zorder Axes.set_rasterization_zorder @@ -551,79 +593,6 @@ Drawing Axes.get_tightbbox -Bulk property manipulation -========================== - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - Axes.set - Axes.update - Axes.properties - Axes.update_from - - -General Artist Properties -========================= - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - Axes.set_alpha - Axes.set_animated - Axes.set_clip_box - Axes.set_clip_on - Axes.set_clip_path - Axes.set_gid - Axes.set_label - Axes.set_url - Axes.set_visible - Axes.set_zorder - Axes.set_rasterized - Axes.set_sketch_params - Axes.set_agg_filter - Axes.set_snap - Axes.set_transform - Axes.set_path_effects - - Axes.get_agg_filter - Axes.get_sketch_params - Axes.get_alpha - Axes.get_animated - Axes.get_clip_box - Axes.get_clip_on - Axes.get_clip_path - Axes.get_gid - Axes.get_label - Axes.get_url - Axes.get_visible - Axes.get_zorder - Axes.get_rasterized - Axes.get_transform - Axes.get_snap - Axes.get_path_effects - - - Axes.axes - Axes.get_axes - Axes.set_axes - Axes.set_figure - Axes.get_figure - -Artist Methods -============== - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - Axes.is_figure_set - Axes.remove - Axes.is_transform_set - - Projection ========== @@ -632,13 +601,13 @@ non-rectilinear Axes. .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.name Axes.get_xaxis_transform Axes.get_yaxis_transform Axes.get_data_ratio - Axes.get_data_ratio_log Axes.get_xaxis_text1_transform Axes.get_xaxis_text2_transform @@ -651,18 +620,16 @@ Other .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axes.zorder - Axes.aname Axes.get_default_bbox_extra_artists Axes.get_transformed_clip_path_and_affine Axes.has_data - Axes.hold - Axes.ishold - + Axes.set + Axes.get_figure + Axes.figure + Axes.remove -Inheritance -=========== -.. inheritance-diagram:: matplotlib.axes.Axes - :private-bases: +.. autoclass:: matplotlib.axes.Axes.ArtistList diff --git a/doc/api/axis_api.rst b/doc/api/axis_api.rst index 287fa94fa015..85ba990ffece 100644 --- a/doc/api/axis_api.rst +++ b/doc/api/axis_api.rst @@ -1,6 +1,6 @@ -=================== - axis and tick API -=================== +******************* +``matplotlib.axis`` +******************* .. contents:: Table of Contents :depth: 3 @@ -8,6 +8,8 @@ :backlinks: entry .. automodule:: matplotlib.axis + :no-members: + :no-undoc-members: Inheritance =========== @@ -20,16 +22,25 @@ Inheritance ================ .. autoclass:: Axis + :no-members: + :no-undoc-members: .. autoclass:: XAxis + :no-members: + :no-undoc-members: .. autoclass:: YAxis + :no-members: + :no-undoc-members: .. autoclass:: Ticker + :no-members: + :no-undoc-members: .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - Axis.cla + Axis.clear Axis.get_scale @@ -38,6 +49,7 @@ Formatters and Locators .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.get_major_formatter @@ -49,14 +61,19 @@ Formatters and Locators Axis.set_minor_formatter Axis.set_minor_locator + Axis.remove_overlapping_locs + Axis.get_remove_overlapping_locs + Axis.set_remove_overlapping_locs Axis Label ---------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: + Axis.label Axis.set_label_coords Axis.set_label_position Axis.set_label_text @@ -68,6 +85,7 @@ Ticks, tick labels and Offset text .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.get_major_ticks @@ -82,6 +100,7 @@ Ticks, tick labels and Offset text Axis.get_offset_text Axis.get_tick_padding + Axis.get_tick_params Axis.get_ticklabels Axis.get_ticklines Axis.get_ticklocs @@ -89,34 +108,39 @@ Ticks, tick labels and Offset text Axis.get_gridlines Axis.grid - Axis.iter_ticks Axis.set_tick_params Axis.axis_date + Axis.minorticks_off + Axis.minorticks_on -Data and view internvals ------------------------- + +Data and view intervals +----------------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.get_data_interval Axis.get_view_interval + Axis.get_inverted Axis.set_data_interval Axis.set_view_interval + Axis.set_inverted Rendering helpers ----------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.get_minpos Axis.get_tick_space - Axis.get_ticklabel_extents Axis.get_tightbbox @@ -125,9 +149,11 @@ Interactive .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - + Axis.contains + Axis.pickradius Axis.get_pickradius Axis.set_pickradius @@ -137,84 +163,84 @@ Units .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.convert_units Axis.set_units Axis.get_units + Axis.set_converter + Axis.get_converter Axis.update_units -Incremental navigation ----------------------- +XAxis Specific +-------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - Axis.pan - Axis.zoom - + XAxis.axis_name + XAxis.get_ticks_position + XAxis.set_ticks_position + XAxis.set_label_position + XAxis.tick_bottom + XAxis.tick_top YAxis Specific -------------- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: YAxis.axis_name - YAxis.get_text_widths YAxis.get_ticks_position YAxis.set_offset_position YAxis.set_ticks_position + YAxis.set_label_position YAxis.tick_left YAxis.tick_right - -XAxis Specific --------------- -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - XAxis.axis_name - XAxis.get_text_heights - XAxis.get_ticks_position - XAxis.set_ticks_position - XAxis.tick_bottom - XAxis.tick_top - - Other ----- .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: Axis.OFFSETTEXTPAD + Axis.axes Axis.limit_range_for_scale Axis.reset_ticks + Axis.set_clip_path Axis.set_default_intervals - Axis.get_smart_bounds - Axis.set_smart_bounds Discouraged ----------- -These methods implicitly use `~matplotlib.ticker.FixedLocator` and -`~matplotlib.ticker.FixedFormatter`. They can be convenient, but if -not used together may de-couple your tick labels from your data. +These methods should be used together with care, calling ``set_ticks`` +to specify the desired tick locations **before** calling ``set_ticklabels`` to +specify a matching series of labels. Calling ``set_ticks`` makes a +`~matplotlib.ticker.FixedLocator`; it's list of locations is then used by +``set_ticklabels`` to make an appropriate +`~matplotlib.ticker.FuncFormatter`. .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - Axis.set_ticklabels + Axis.get_label + Axis.set_label Axis.set_ticks + Axis.set_ticklabels @@ -222,733 +248,27 @@ not used together may de-couple your tick labels from your data. ================ .. autoclass:: Tick + :no-members: + :no-undoc-members: .. autoclass:: XTick + :no-members: + :no-undoc-members: .. autoclass:: YTick + :no-members: + :no-undoc-members: .. autosummary:: :toctree: _as_gen + :template: autosummary.rst :nosignatures: - - Tick.apply_tickdir Tick.get_loc Tick.get_pad - Tick.get_pad_pixels Tick.get_tick_padding Tick.get_tickdir Tick.get_view_interval - Tick.set_label1 - Tick.set_label2 - Tick.set_pad - Tick.update_position - - -Common and inherited methods -============================ - -``XTick`` ---------- - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - XTick.apply_tickdir - XTick.get_loc - XTick.get_pad - XTick.get_pad_pixels - XTick.get_tick_padding - XTick.get_tickdir - XTick.get_view_interval - XTick.set_label1 - XTick.set_label2 - XTick.set_pad - XTick.update_position - - - -YTick ------ - - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - YTick.apply_tickdir - YTick.get_loc - YTick.get_pad - YTick.get_pad_pixels - YTick.get_tick_padding - YTick.get_tickdir - YTick.get_view_interval - YTick.set_label1 - YTick.set_label2 - YTick.set_pad - YTick.update_position - -YAxis ------ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - - YAxis.OFFSETTEXTPAD - YAxis.axis_date - YAxis.cla - YAxis.convert_units - YAxis.get_data_interval - YAxis.get_gridlines - YAxis.get_label_position - YAxis.get_label_text - YAxis.get_major_formatter - YAxis.get_major_locator - YAxis.get_major_ticks - YAxis.get_majorticklabels - YAxis.get_majorticklines - YAxis.get_majorticklocs - YAxis.get_minor_formatter - YAxis.get_minor_locator - YAxis.get_minor_ticks - YAxis.get_minorticklabels - YAxis.get_minorticklines - YAxis.get_minorticklocs - YAxis.get_minpos - YAxis.get_offset_text - YAxis.get_pickradius - YAxis.get_scale - YAxis.get_smart_bounds - YAxis.get_tick_padding - YAxis.get_tick_space - YAxis.get_ticklabel_extents - YAxis.get_ticklabels - YAxis.get_ticklines - YAxis.get_ticklocs - YAxis.get_tightbbox - YAxis.get_units - YAxis.get_view_interval - YAxis.grid - YAxis.iter_ticks - YAxis.limit_range_for_scale - YAxis.pan - YAxis.reset_ticks - YAxis.set_data_interval - YAxis.set_default_intervals - YAxis.set_label_coords - YAxis.set_label_position - YAxis.set_label_text - YAxis.set_major_formatter - YAxis.set_major_locator - YAxis.set_minor_formatter - YAxis.set_minor_locator - YAxis.set_pickradius - YAxis.set_smart_bounds - YAxis.set_tick_params - YAxis.set_ticklabels - YAxis.set_ticks - YAxis.set_units - YAxis.set_view_interval - YAxis.update_units - YAxis.zoom - -``YAxis`` ---------- - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - XAxis.OFFSETTEXTPAD - XAxis.axis_date - XAxis.cla - XAxis.convert_units - XAxis.get_data_interval - XAxis.get_gridlines - XAxis.get_label_position - XAxis.get_label_text - XAxis.get_major_formatter - XAxis.get_major_locator - XAxis.get_major_ticks - XAxis.get_majorticklabels - XAxis.get_majorticklines - XAxis.get_majorticklocs - XAxis.get_minor_formatter - XAxis.get_minor_locator - XAxis.get_minor_ticks - XAxis.get_minorticklabels - XAxis.get_minorticklines - XAxis.get_minorticklocs - XAxis.get_minpos - XAxis.get_offset_text - XAxis.get_pickradius - XAxis.get_scale - XAxis.get_smart_bounds - XAxis.get_tick_padding - XAxis.get_tick_space - XAxis.get_ticklabel_extents - XAxis.get_ticklabels - XAxis.get_ticklines - XAxis.get_ticklocs - XAxis.get_tightbbox - XAxis.get_units - XAxis.get_view_interval - XAxis.grid - XAxis.iter_ticks - XAxis.limit_range_for_scale - XAxis.pan - XAxis.reset_ticks - XAxis.set_data_interval - XAxis.set_default_intervals - XAxis.set_label_coords - XAxis.set_label_position - XAxis.set_label_text - XAxis.set_major_formatter - XAxis.set_major_locator - XAxis.set_minor_formatter - XAxis.set_minor_locator - XAxis.set_pickradius - XAxis.set_smart_bounds - XAxis.set_tick_params - XAxis.set_ticklabels - XAxis.set_ticks - XAxis.set_units - XAxis.set_view_interval - XAxis.update_units - XAxis.zoom - - - -Inherited from artist ---------------------- - -Ticks -~~~~~ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - Tick.add_callback - Tick.aname - Tick.axes - Tick.bottom_margin - Tick.contains - Tick.convert_xunits - Tick.convert_yunits - Tick.draw - Tick.findobj - Tick.format_cursor_data - Tick.get_agg_filter - Tick.get_alpha - Tick.get_animated - Tick.get_axes - Tick.get_bottom_margin - Tick.get_children - Tick.get_clip_box - Tick.get_clip_on - Tick.get_clip_path - Tick.get_contains - Tick.get_cursor_data - Tick.get_figure - Tick.get_gid - Tick.get_label - Tick.get_left_margin - Tick.get_margins - Tick.get_path_effects - Tick.get_picker - Tick.get_rasterized - Tick.get_right_margin - Tick.get_sketch_params - Tick.get_snap - Tick.get_top_margin - Tick.get_transform - Tick.get_transformed_clip_path_and_affine - Tick.get_url - Tick.get_visible - Tick.get_window_extent - Tick.get_zorder - Tick.have_units - Tick.hitlist - Tick.is_figure_set - Tick.is_transform_set - Tick.left_margin - Tick.margins - Tick.mouseover - Tick.pchanged - Tick.pick - Tick.pickable - Tick.properties - Tick.remove - Tick.remove_callback - Tick.right_margin - Tick.set - Tick.set_agg_filter - Tick.set_alpha - Tick.set_animated - Tick.set_axes - Tick.set_bottom_margin - Tick.set_clip_box - Tick.set_clip_on Tick.set_clip_path - Tick.set_contains - Tick.set_figure - Tick.set_gid - Tick.set_label - Tick.set_left_margin - Tick.set_margins - Tick.set_path_effects - Tick.set_picker - Tick.set_rasterized - Tick.set_right_margin - Tick.set_sketch_params - Tick.set_snap - Tick.set_top_margin - Tick.set_transform + Tick.set_pad Tick.set_url - Tick.set_visible - Tick.set_zorder - Tick.stale - Tick.top_margin - Tick.update - Tick.update_from - Tick.zorder - - XTick.add_callback - XTick.aname - XTick.axes - XTick.bottom_margin - XTick.contains - XTick.convert_xunits - XTick.convert_yunits - XTick.draw - XTick.findobj - XTick.format_cursor_data - XTick.get_agg_filter - XTick.get_alpha - XTick.get_animated - XTick.get_axes - XTick.get_bottom_margin - XTick.get_children - XTick.get_clip_box - XTick.get_clip_on - XTick.get_clip_path - XTick.get_contains - XTick.get_cursor_data - XTick.get_figure - XTick.get_gid - XTick.get_label - XTick.get_left_margin - XTick.get_margins - XTick.get_path_effects - XTick.get_picker - XTick.get_rasterized - XTick.get_right_margin - XTick.get_sketch_params - XTick.get_snap - XTick.get_top_margin - XTick.get_transform - XTick.get_transformed_clip_path_and_affine - XTick.get_url - XTick.get_visible - XTick.get_window_extent - XTick.get_zorder - XTick.have_units - XTick.hitlist - XTick.is_figure_set - XTick.is_transform_set - XTick.left_margin - XTick.margins - XTick.mouseover - XTick.pchanged - XTick.pick - XTick.pickable - XTick.properties - XTick.remove - XTick.remove_callback - XTick.right_margin - XTick.set - XTick.set_agg_filter - XTick.set_alpha - XTick.set_animated - XTick.set_axes - XTick.set_bottom_margin - XTick.set_clip_box - XTick.set_clip_on - XTick.set_clip_path - XTick.set_contains - XTick.set_figure - XTick.set_gid - XTick.set_label - XTick.set_left_margin - XTick.set_margins - XTick.set_path_effects - XTick.set_picker - XTick.set_rasterized - XTick.set_right_margin - XTick.set_sketch_params - XTick.set_snap - XTick.set_top_margin - XTick.set_transform - XTick.set_url - XTick.set_visible - XTick.set_zorder - XTick.stale - XTick.top_margin - XTick.update - XTick.update_from - XTick.zorder - - YTick.add_callback - YTick.aname - YTick.axes - YTick.bottom_margin - YTick.contains - YTick.convert_xunits - YTick.convert_yunits - YTick.draw - YTick.findobj - YTick.format_cursor_data - YTick.get_agg_filter - YTick.get_alpha - YTick.get_animated - YTick.get_axes - YTick.get_bottom_margin - YTick.get_children - YTick.get_clip_box - YTick.get_clip_on - YTick.get_clip_path - YTick.get_contains - YTick.get_cursor_data - YTick.get_figure - YTick.get_gid - YTick.get_label - YTick.get_left_margin - YTick.get_margins - YTick.get_path_effects - YTick.get_picker - YTick.get_rasterized - YTick.get_right_margin - YTick.get_sketch_params - YTick.get_snap - YTick.get_top_margin - YTick.get_transform - YTick.get_transformed_clip_path_and_affine - YTick.get_url - YTick.get_visible - YTick.get_window_extent - YTick.get_zorder - YTick.have_units - YTick.hitlist - YTick.is_figure_set - YTick.is_transform_set - YTick.left_margin - YTick.margins - YTick.mouseover - YTick.pchanged - YTick.pick - YTick.pickable - YTick.properties - YTick.remove - YTick.remove_callback - YTick.right_margin - YTick.set - YTick.set_agg_filter - YTick.set_alpha - YTick.set_animated - YTick.set_axes - YTick.set_bottom_margin - YTick.set_clip_box - YTick.set_clip_on - YTick.set_clip_path - YTick.set_contains - YTick.set_figure - YTick.set_gid - YTick.set_label - YTick.set_left_margin - YTick.set_margins - YTick.set_path_effects - YTick.set_picker - YTick.set_rasterized - YTick.set_right_margin - YTick.set_sketch_params - YTick.set_snap - YTick.set_top_margin - YTick.set_transform - YTick.set_url - YTick.set_visible - YTick.set_zorder - YTick.stale - YTick.top_margin - YTick.update - YTick.update_from - YTick.zorder - - -Axis -~~~~ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - Axis.add_callback - Axis.aname - Axis.axes - Axis.bottom_margin - Axis.contains - Axis.convert_xunits - Axis.convert_yunits - Axis.draw - Axis.findobj - Axis.format_cursor_data - Axis.get_agg_filter - Axis.get_alpha - Axis.get_animated - Axis.get_axes - Axis.get_bottom_margin - Axis.get_children - Axis.get_clip_box - Axis.get_clip_on - Axis.get_clip_path - Axis.get_contains - Axis.get_cursor_data - Axis.get_figure - Axis.get_gid - Axis.get_label - Axis.get_left_margin - Axis.get_margins - Axis.get_path_effects - Axis.get_picker - Axis.get_rasterized - Axis.get_right_margin - Axis.get_sketch_params - Axis.get_snap - Axis.get_top_margin - Axis.get_transform - Axis.get_transformed_clip_path_and_affine - Axis.get_url - Axis.get_visible - Axis.get_window_extent - Axis.get_zorder - Axis.have_units - Axis.hitlist - Axis.is_figure_set - Axis.is_transform_set - Axis.left_margin - Axis.margins - Axis.mouseover - Axis.pchanged - Axis.pick - Axis.pickable - Axis.properties - Axis.remove - Axis.remove_callback - Axis.right_margin - Axis.set - Axis.set_agg_filter - Axis.set_alpha - Axis.set_animated - Axis.set_axes - Axis.set_bottom_margin - Axis.set_clip_box - Axis.set_clip_on - Axis.set_clip_path - Axis.set_contains - Axis.set_figure - Axis.set_gid - Axis.set_label - Axis.set_left_margin - Axis.set_margins - Axis.set_path_effects - Axis.set_picker - Axis.set_rasterized - Axis.set_right_margin - Axis.set_sketch_params - Axis.set_snap - Axis.set_top_margin - Axis.set_transform - Axis.set_url - Axis.set_visible - Axis.set_zorder - Axis.stale - Axis.top_margin - Axis.update - Axis.update_from - Axis.zorder - - XAxis.add_callback - XAxis.aname - XAxis.axes - XAxis.bottom_margin - XAxis.contains - XAxis.convert_xunits - XAxis.convert_yunits - XAxis.draw - XAxis.findobj - XAxis.format_cursor_data - XAxis.get_agg_filter - XAxis.get_alpha - XAxis.get_animated - XAxis.get_axes - XAxis.get_bottom_margin - XAxis.get_children - XAxis.get_clip_box - XAxis.get_clip_on - XAxis.get_clip_path - XAxis.get_contains - XAxis.get_cursor_data - XAxis.get_figure - XAxis.get_gid - XAxis.get_label - XAxis.get_left_margin - XAxis.get_margins - XAxis.get_path_effects - XAxis.get_picker - XAxis.get_rasterized - XAxis.get_right_margin - XAxis.get_sketch_params - XAxis.get_snap - XAxis.get_top_margin - XAxis.get_transform - XAxis.get_transformed_clip_path_and_affine - XAxis.get_url - XAxis.get_visible - XAxis.get_window_extent - XAxis.get_zorder - XAxis.have_units - XAxis.hitlist - XAxis.is_figure_set - XAxis.is_transform_set - XAxis.left_margin - XAxis.margins - XAxis.mouseover - XAxis.pchanged - XAxis.pick - XAxis.pickable - XAxis.properties - XAxis.remove - XAxis.remove_callback - XAxis.right_margin - XAxis.set - XAxis.set_agg_filter - XAxis.set_alpha - XAxis.set_animated - XAxis.set_axes - XAxis.set_bottom_margin - XAxis.set_clip_box - XAxis.set_clip_on - XAxis.set_clip_path - XAxis.set_contains - XAxis.set_figure - XAxis.set_gid - XAxis.set_label - XAxis.set_left_margin - XAxis.set_margins - XAxis.set_path_effects - XAxis.set_picker - XAxis.set_rasterized - XAxis.set_right_margin - XAxis.set_sketch_params - XAxis.set_snap - XAxis.set_top_margin - XAxis.set_transform - XAxis.set_url - XAxis.set_visible - XAxis.set_zorder - XAxis.stale - XAxis.top_margin - XAxis.update - XAxis.update_from - XAxis.zorder - - YAxis.add_callback - YAxis.aname - YAxis.axes - YAxis.bottom_margin - YAxis.contains - YAxis.convert_xunits - YAxis.convert_yunits - YAxis.draw - YAxis.findobj - YAxis.format_cursor_data - YAxis.get_agg_filter - YAxis.get_alpha - YAxis.get_animated - YAxis.get_axes - YAxis.get_bottom_margin - YAxis.get_children - YAxis.get_clip_box - YAxis.get_clip_on - YAxis.get_clip_path - YAxis.get_contains - YAxis.get_cursor_data - YAxis.get_figure - YAxis.get_gid - YAxis.get_label - YAxis.get_left_margin - YAxis.get_margins - YAxis.get_path_effects - YAxis.get_picker - YAxis.get_rasterized - YAxis.get_right_margin - YAxis.get_sketch_params - YAxis.get_snap - YAxis.get_top_margin - YAxis.get_transform - YAxis.get_transformed_clip_path_and_affine - YAxis.get_url - YAxis.get_visible - YAxis.get_window_extent - YAxis.get_zorder - YAxis.have_units - YAxis.hitlist - YAxis.is_figure_set - YAxis.is_transform_set - YAxis.left_margin - YAxis.margins - YAxis.mouseover - YAxis.pchanged - YAxis.pick - YAxis.pickable - YAxis.properties - YAxis.remove - YAxis.remove_callback - YAxis.right_margin - YAxis.set - YAxis.set_agg_filter - YAxis.set_alpha - YAxis.set_animated - YAxis.set_axes - YAxis.set_bottom_margin - YAxis.set_clip_box - YAxis.set_clip_on - YAxis.set_clip_path - YAxis.set_contains - YAxis.set_figure - YAxis.set_gid - YAxis.set_label - YAxis.set_left_margin - YAxis.set_margins - YAxis.set_path_effects - YAxis.set_picker - YAxis.set_rasterized - YAxis.set_right_margin - YAxis.set_sketch_params - YAxis.set_snap - YAxis.set_top_margin - YAxis.set_transform - YAxis.set_url - YAxis.set_visible - YAxis.set_zorder - YAxis.stale - YAxis.top_margin - YAxis.update - YAxis.update_from - YAxis.zorder + Tick.update_position diff --git a/doc/api/backend_agg_api.rst b/doc/api/backend_agg_api.rst new file mode 100644 index 000000000000..752f348f8747 --- /dev/null +++ b/doc/api/backend_agg_api.rst @@ -0,0 +1,8 @@ +*********************************** +``matplotlib.backends.backend_agg`` +*********************************** + +.. automodule:: matplotlib.backends.backend_agg + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_bases_api.rst b/doc/api/backend_bases_api.rst index 990a1a091f81..c98a6af3e05e 100644 --- a/doc/api/backend_bases_api.rst +++ b/doc/api/backend_bases_api.rst @@ -1,6 +1,6 @@ - -:mod:`matplotlib.backend_bases` -================================ +**************************** +``matplotlib.backend_bases`` +**************************** .. automodule:: matplotlib.backend_bases :members: diff --git a/doc/api/backend_cairo_api.rst b/doc/api/backend_cairo_api.rst new file mode 100644 index 000000000000..66371ec6895c --- /dev/null +++ b/doc/api/backend_cairo_api.rst @@ -0,0 +1,8 @@ +************************************* +``matplotlib.backends.backend_cairo`` +************************************* + +.. automodule:: matplotlib.backends.backend_cairo + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_gtk3_api.rst b/doc/api/backend_gtk3_api.rst new file mode 100644 index 000000000000..bd6d71d6ccb2 --- /dev/null +++ b/doc/api/backend_gtk3_api.rst @@ -0,0 +1,13 @@ +********************************************************************************** +``matplotlib.backends.backend_gtk3agg``, ``matplotlib.backends.backend_gtk3cairo`` +********************************************************************************** + +**NOTE** These :ref:`backends` are not documented here, to avoid adding a +dependency to building the docs. + +.. redirect-from:: /api/backend_gtk3agg_api +.. redirect-from:: /api/backend_gtk3cairo_api + +.. module:: matplotlib.backends.backend_gtk3 +.. module:: matplotlib.backends.backend_gtk3agg +.. module:: matplotlib.backends.backend_gtk3cairo diff --git a/doc/api/backend_gtk4_api.rst b/doc/api/backend_gtk4_api.rst new file mode 100644 index 000000000000..278daa392b13 --- /dev/null +++ b/doc/api/backend_gtk4_api.rst @@ -0,0 +1,13 @@ +********************************************************************************** +``matplotlib.backends.backend_gtk4agg``, ``matplotlib.backends.backend_gtk4cairo`` +********************************************************************************** + +**NOTE** These :ref:`backends` are not documented here, to avoid adding a +dependency to building the docs. + +.. redirect-from:: /api/backend_gtk4agg_api +.. redirect-from:: /api/backend_gtk4cairo_api + +.. module:: matplotlib.backends.backend_gtk4 +.. module:: matplotlib.backends.backend_gtk4agg +.. module:: matplotlib.backends.backend_gtk4cairo diff --git a/doc/api/backend_gtkagg_api.rst b/doc/api/backend_gtkagg_api.rst deleted file mode 100644 index f5a37bf4d345..000000000000 --- a/doc/api/backend_gtkagg_api.rst +++ /dev/null @@ -1,11 +0,0 @@ - -:mod:`matplotlib.backends.backend_gtkagg` -========================================= - -**TODO** We'll add this later, importing the gtk backends requires an active -X-session, which is not compatible with cron jobs. - -.. .. automodule:: matplotlib.backends.backend_gtkagg -.. :members: -.. :undoc-members: -.. :show-inheritance: diff --git a/doc/api/backend_managers_api.rst b/doc/api/backend_managers_api.rst index 86d1c383b966..3e77e89dbbce 100644 --- a/doc/api/backend_managers_api.rst +++ b/doc/api/backend_managers_api.rst @@ -1,6 +1,6 @@ - -:mod:`matplotlib.backend_managers` -=================================== +******************************* +``matplotlib.backend_managers`` +******************************* .. automodule:: matplotlib.backend_managers :members: diff --git a/doc/api/backend_mixed_api.rst b/doc/api/backend_mixed_api.rst new file mode 100644 index 000000000000..61d770e56ccf --- /dev/null +++ b/doc/api/backend_mixed_api.rst @@ -0,0 +1,8 @@ +************************************* +``matplotlib.backends.backend_mixed`` +************************************* + +.. automodule:: matplotlib.backends.backend_mixed + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_nbagg_api.rst b/doc/api/backend_nbagg_api.rst new file mode 100644 index 000000000000..6596f461bbf0 --- /dev/null +++ b/doc/api/backend_nbagg_api.rst @@ -0,0 +1,8 @@ +************************************* +``matplotlib.backends.backend_nbagg`` +************************************* + +.. automodule:: matplotlib.backends.backend_nbagg + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_pdf_api.rst b/doc/api/backend_pdf_api.rst index 115863d61875..014c3e6e5017 100644 --- a/doc/api/backend_pdf_api.rst +++ b/doc/api/backend_pdf_api.rst @@ -1,7 +1,8 @@ - -:mod:`matplotlib.backends.backend_pdf` -====================================== +*********************************** +``matplotlib.backends.backend_pdf`` +*********************************** .. automodule:: matplotlib.backends.backend_pdf :members: + :undoc-members: :show-inheritance: diff --git a/doc/api/backend_pgf_api.rst b/doc/api/backend_pgf_api.rst new file mode 100644 index 000000000000..9f90beb72a1b --- /dev/null +++ b/doc/api/backend_pgf_api.rst @@ -0,0 +1,8 @@ +*********************************** +``matplotlib.backends.backend_pgf`` +*********************************** + +.. automodule:: matplotlib.backends.backend_pgf + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_ps_api.rst b/doc/api/backend_ps_api.rst new file mode 100644 index 000000000000..d9b07d961b4b --- /dev/null +++ b/doc/api/backend_ps_api.rst @@ -0,0 +1,8 @@ +********************************** +``matplotlib.backends.backend_ps`` +********************************** + +.. automodule:: matplotlib.backends.backend_ps + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_qt4agg_api.rst b/doc/api/backend_qt4agg_api.rst deleted file mode 100644 index 2e2e852612c7..000000000000 --- a/doc/api/backend_qt4agg_api.rst +++ /dev/null @@ -1,9 +0,0 @@ - -:mod:`matplotlib.backends.backend_qt4agg` -========================================= - -.. automodule:: matplotlib.backends.backend_qt4agg - :members: - :undoc-members: - :show-inheritance: - diff --git a/doc/api/backend_qt5agg_api.rst b/doc/api/backend_qt5agg_api.rst deleted file mode 100644 index 58e5353a32a9..000000000000 --- a/doc/api/backend_qt5agg_api.rst +++ /dev/null @@ -1,9 +0,0 @@ - -:mod:`matplotlib.backends.backend_qt5agg` -========================================= - -.. automodule:: matplotlib.backends.backend_qt5agg - :members: - :undoc-members: - :show-inheritance: - diff --git a/doc/api/backend_qt_api.rst b/doc/api/backend_qt_api.rst new file mode 100644 index 000000000000..ebfeedceb6e1 --- /dev/null +++ b/doc/api/backend_qt_api.rst @@ -0,0 +1,71 @@ +****************************************************************************** +``matplotlib.backends.backend_qtagg``, ``matplotlib.backends.backend_qtcairo`` +****************************************************************************** + +**NOTE** These :ref:`backends` are not (auto) documented here, to avoid adding +a dependency to building the docs. + +.. redirect-from:: /api/backend_qt4agg_api +.. redirect-from:: /api/backend_qt4cairo_api +.. redirect-from:: /api/backend_qt5agg_api +.. redirect-from:: /api/backend_qt5cairo_api + +.. module:: matplotlib.backends.qt_compat +.. module:: matplotlib.backends.backend_qt +.. module:: matplotlib.backends.backend_qtagg +.. module:: matplotlib.backends.backend_qtcairo +.. module:: matplotlib.backends.backend_qt5agg +.. module:: matplotlib.backends.backend_qt5cairo + +.. _QT_bindings: + +Qt Bindings +----------- + +There are currently 2 actively supported Qt versions, Qt5 and Qt6, and two +supported Python bindings per version -- `PyQt5 +`_ and `PySide2 +`_ for Qt5 and `PyQt6 +`_ and `PySide6 +`_ for Qt6 [#]_. Matplotlib's +qtagg and qtcairo backends (``matplotlib.backends.backend_qtagg`` and +``matplotlib.backend.backend_qtcairo``) support all these bindings, with common +parts factored out in the ``matplotlib.backends.backend_qt`` module. + +At runtime, these backends select the actual binding used as follows: + +1. If a binding's ``QtCore`` subpackage is already imported, that binding is + selected (the order for the check is ``PyQt6``, ``PySide6``, ``PyQt5``, + ``PySide2``). +2. If the :envvar:`QT_API` environment variable is set to one of "PyQt6", + "PySide6", "PyQt5", "PySide2" (case-insensitive), that binding is selected. + (See also the documentation on :ref:`environment-variables`.) +3. Otherwise, the first available backend in the order ``PyQt6``, ``PySide6``, + ``PyQt5``, ``PySide2`` is selected. + +In the past, Matplotlib used to have separate backends for each version of Qt +(e.g. qt4agg/``matplotlib.backends.backend_qt4agg`` and +qt5agg/``matplotlib.backends.backend_qt5agg``). This scheme was dropped when +support for Qt6 was added. For back-compatibility, qt5agg/``backend_qt5agg`` +and qt5cairo/``backend_qt5cairo`` remain available; selecting one of these +backends forces the use of a Qt5 binding. Their use is discouraged and +``backend_qtagg`` or ``backend_qtcairo`` should be preferred instead. However, +these modules will not be deprecated until we drop support for Qt5. + +While both PyQt +and Qt for Python (aka PySide) closely mirror the underlying C++ API they are +wrapping, they are not drop-in replacements for each other [#]_. To account +for this, Matplotlib has an internal API compatibility layer in +`matplotlib.backends.qt_compat` which covers our needs. Despite being a public +module, we do not consider this to be a stable user-facing API and it may +change without warning [#]_. + +.. [#] There is also `PyQt4 + `_ and `PySide + `_ for Qt4 but these are no + longer supported by Matplotlib and upstream support for Qt4 ended + in 2015. +.. [#] Despite the slight API differences, the more important distinction + between the PyQt and Qt for Python series of bindings is licensing. +.. [#] If you are looking for a general purpose compatibility library please + see `qtpy `_. diff --git a/doc/api/backend_registry_api.rst b/doc/api/backend_registry_api.rst new file mode 100644 index 000000000000..ca184c67d0a2 --- /dev/null +++ b/doc/api/backend_registry_api.rst @@ -0,0 +1,8 @@ +******************************** +``matplotlib.backends.registry`` +******************************** + +.. automodule:: matplotlib.backends.registry + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_svg_api.rst b/doc/api/backend_svg_api.rst index 399042482ea8..2e7c1c9f5db1 100644 --- a/doc/api/backend_svg_api.rst +++ b/doc/api/backend_svg_api.rst @@ -1,7 +1,8 @@ - -:mod:`matplotlib.backends.backend_svg` -====================================== +*********************************** +``matplotlib.backends.backend_svg`` +*********************************** .. automodule:: matplotlib.backends.backend_svg :members: + :undoc-members: :show-inheritance: diff --git a/doc/api/backend_template_api.rst b/doc/api/backend_template_api.rst new file mode 100644 index 000000000000..8198eeae121e --- /dev/null +++ b/doc/api/backend_template_api.rst @@ -0,0 +1,8 @@ +**************************************** +``matplotlib.backends.backend_template`` +**************************************** + +.. automodule:: matplotlib.backends.backend_template + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_tk_api.rst b/doc/api/backend_tk_api.rst new file mode 100644 index 000000000000..08abf603fd91 --- /dev/null +++ b/doc/api/backend_tk_api.rst @@ -0,0 +1,13 @@ +****************************************************************************** +``matplotlib.backends.backend_tkagg``, ``matplotlib.backends.backend_tkcairo`` +****************************************************************************** + +.. automodule:: matplotlib.backends.backend_tkagg + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: matplotlib.backends.backend_tkcairo + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_tools_api.rst b/doc/api/backend_tools_api.rst index 32babd5844b0..994f32ac854e 100644 --- a/doc/api/backend_tools_api.rst +++ b/doc/api/backend_tools_api.rst @@ -1,6 +1,6 @@ - -:mod:`matplotlib.backend_tools` -================================ +**************************** +``matplotlib.backend_tools`` +**************************** .. automodule:: matplotlib.backend_tools :members: diff --git a/doc/api/backend_webagg_api.rst b/doc/api/backend_webagg_api.rst new file mode 100644 index 000000000000..ced3533da249 --- /dev/null +++ b/doc/api/backend_webagg_api.rst @@ -0,0 +1,8 @@ +************************************** +``matplotlib.backends.backend_webagg`` +************************************** + +.. automodule:: matplotlib.backends.backend_webagg + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_webagg_core_api.rst b/doc/api/backend_webagg_core_api.rst new file mode 100644 index 000000000000..0d1e58dd8f9f --- /dev/null +++ b/doc/api/backend_webagg_core_api.rst @@ -0,0 +1,8 @@ +******************************************* +``matplotlib.backends.backend_webagg_core`` +******************************************* + +.. automodule:: matplotlib.backends.backend_webagg_core + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/backend_wx_api.rst b/doc/api/backend_wx_api.rst new file mode 100644 index 000000000000..bec832da0c13 --- /dev/null +++ b/doc/api/backend_wx_api.rst @@ -0,0 +1,12 @@ +****************************************************************************** +``matplotlib.backends.backend_wxagg``, ``matplotlib.backends.backend_wxcairo`` +****************************************************************************** + +**NOTE** These :ref:`backends` are not documented here, to avoid adding a +dependency to building the docs. + +.. redirect-from:: /api/backend_wxagg_api + +.. module:: matplotlib.backends.backend_wx +.. module:: matplotlib.backends.backend_wxagg +.. module:: matplotlib.backends.backend_wxcairo diff --git a/doc/api/backend_wxagg_api.rst b/doc/api/backend_wxagg_api.rst deleted file mode 100644 index 67c5a00be546..000000000000 --- a/doc/api/backend_wxagg_api.rst +++ /dev/null @@ -1,8 +0,0 @@ - -:mod:`matplotlib.backends.backend_wxagg` -======================================== - -.. automodule:: matplotlib.backends.backend_wxagg - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/api/bezier_api.rst b/doc/api/bezier_api.rst new file mode 100644 index 000000000000..45019153fa63 --- /dev/null +++ b/doc/api/bezier_api.rst @@ -0,0 +1,9 @@ +********************* +``matplotlib.bezier`` +********************* + +.. automodule:: matplotlib.bezier + :members: + :undoc-members: + :special-members: __call__ + :show-inheritance: diff --git a/doc/api/category_api.rst b/doc/api/category_api.rst new file mode 100644 index 000000000000..895f7c961141 --- /dev/null +++ b/doc/api/category_api.rst @@ -0,0 +1,8 @@ +*********************** +``matplotlib.category`` +*********************** + +.. automodule:: matplotlib.category + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/cbook_api.rst b/doc/api/cbook_api.rst index 7c133ce8fdd1..4c8ef9cc50fa 100644 --- a/doc/api/cbook_api.rst +++ b/doc/api/cbook_api.rst @@ -1,10 +1,6 @@ -***** -cbook -***** - - -:mod:`matplotlib.cbook` -======================= +******************** +``matplotlib.cbook`` +******************** .. automodule:: matplotlib.cbook :members: diff --git a/doc/api/cm_api.rst b/doc/api/cm_api.rst index 6cf4a262a62d..c9509389a2bb 100644 --- a/doc/api/cm_api.rst +++ b/doc/api/cm_api.rst @@ -1,11 +1,10 @@ -************* -cm (colormap) -************* - -:mod:`matplotlib.cm` -==================== +***************** +``matplotlib.cm`` +***************** .. automodule:: matplotlib.cm :members: :undoc-members: :show-inheritance: + +.. include:: scalarmappable.gen_rst diff --git a/doc/api/collections_api.rst b/doc/api/collections_api.rst index 166dc3690c08..f2d9cb5226b2 100644 --- a/doc/api/collections_api.rst +++ b/doc/api/collections_api.rst @@ -1,16 +1,23 @@ -*********** -collections -*********** +************************** +``matplotlib.collections`` +************************** .. inheritance-diagram:: matplotlib.collections :parts: 2 :private-bases: -:mod:`matplotlib.collections` -============================= - .. automodule:: matplotlib.collections :members: :undoc-members: :show-inheritance: :inherited-members: + +.. autoclass:: _CollectionWithSizes + :no-members: + :members: get_sizes, set_sizes + :class-doc-from: class + +.. autoclass:: _MeshData + :no-members: + :members: set_array + :class-doc-from: class diff --git a/doc/api/colorbar_api.rst b/doc/api/colorbar_api.rst index 26714dcebd38..745589a39fa4 100644 --- a/doc/api/colorbar_api.rst +++ b/doc/api/colorbar_api.rst @@ -1,10 +1,6 @@ -******** -colorbar -******** - - -:mod:`matplotlib.colorbar` -========================== +*********************** +``matplotlib.colorbar`` +*********************** .. automodule:: matplotlib.colorbar :members: diff --git a/doc/api/colorizer_api.rst b/doc/api/colorizer_api.rst new file mode 100644 index 000000000000..e72da5cfb030 --- /dev/null +++ b/doc/api/colorizer_api.rst @@ -0,0 +1,9 @@ +************************ +``matplotlib.colorizer`` +************************ + +.. automodule:: matplotlib.colorizer + :members: + :undoc-members: + :show-inheritance: + :private-members: _ColorizerInterface, _ScalarMappable diff --git a/doc/api/colors_api.rst b/doc/api/colors_api.rst index bd577af02ed4..18e7c43932a9 100644 --- a/doc/api/colors_api.rst +++ b/doc/api/colors_api.rst @@ -1,15 +1,100 @@ -****** -colors -****** +********************* +``matplotlib.colors`` +********************* -For a visual representation of the matplotlib colormaps, see the -"Color" section in the gallery. +.. note:: + The Color :ref:`tutorials ` and :ref:`examples + ` demonstrate how to set colors and colormaps. You may want + to read those instead. -:mod:`matplotlib.colors` -======================== +.. currentmodule:: matplotlib.colors .. automodule:: matplotlib.colors - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-inherited-members: + +Color norms +----------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Norm + Normalize + NoNorm + AsinhNorm + BoundaryNorm + CenteredNorm + FuncNorm + LogNorm + PowerNorm + SymLogNorm + TwoSlopeNorm + MultiNorm + +Univariate Colormaps +-------------------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Colormap + LinearSegmentedColormap + ListedColormap + +Multivariate Colormaps +---------------------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + BivarColormap + SegmentedBivarColormap + BivarColormapFromImage + +Other classes +------------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + ColorSequenceRegistry + LightSource + +Functions +--------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + from_levels_and_colors + hsv_to_rgb + rgb_to_hsv + to_hex + to_rgb + to_rgba + to_rgba_array + is_color_like + same_color + get_named_colors_mapping + make_norm_from_scale + +Exported colors +--------------- + +The data used to populate the :doc:`/gallery/color/named_colors` are exposed +as dictionaries that map color names to hex strings. + +.. py:data:: BASE_COLORS + +.. py:data:: TABLEAU_COLORS + +.. py:data:: CSS4_COLORS + +.. py:data:: XKCD_COLORS diff --git a/doc/api/container_api.rst b/doc/api/container_api.rst new file mode 100644 index 000000000000..4bc05067fd26 --- /dev/null +++ b/doc/api/container_api.rst @@ -0,0 +1,8 @@ +************************ +``matplotlib.container`` +************************ + +.. automodule:: matplotlib.container + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/contour_api.rst b/doc/api/contour_api.rst new file mode 100644 index 000000000000..7fe159efd9eb --- /dev/null +++ b/doc/api/contour_api.rst @@ -0,0 +1,8 @@ +********************** +``matplotlib.contour`` +********************** + +.. automodule:: matplotlib.contour + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/dates_api.rst b/doc/api/dates_api.rst index 6d9d8dd529b6..7a3e3bcf4a95 100644 --- a/doc/api/dates_api.rst +++ b/doc/api/dates_api.rst @@ -1,14 +1,13 @@ -***** -dates -***** +******************** +``matplotlib.dates`` +******************** .. inheritance-diagram:: matplotlib.dates :parts: 1 - -:mod:`matplotlib.dates` -======================= + :top-classes: matplotlib.ticker.Formatter, matplotlib.ticker.Locator .. automodule:: matplotlib.dates :members: :undoc-members: + :exclude-members: rrule :show-inheritance: diff --git a/doc/api/dviread.rst b/doc/api/dviread.rst index 99549ce02f59..9d07407a1753 100644 --- a/doc/api/dviread.rst +++ b/doc/api/dviread.rst @@ -1,11 +1,9 @@ -**************** -dviread -**************** - -:mod:`matplotlib.dviread` -========================= +********************** +``matplotlib.dviread`` +********************** .. automodule:: matplotlib.dviread :members: :undoc-members: + :exclude-members: Page, Text, Box :show-inheritance: diff --git a/doc/api/figure_api.rst b/doc/api/figure_api.rst index 27e86a1fee14..5dd3adbfec9f 100644 --- a/doc/api/figure_api.rst +++ b/doc/api/figure_api.rst @@ -1,12 +1,318 @@ -****** -figure -****** +********************* +``matplotlib.figure`` +********************* - -:mod:`matplotlib.figure` -======================== +.. currentmodule:: matplotlib.figure .. automodule:: matplotlib.figure - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-undoc-members: + +Figure +====== + +Figure class +------------ +.. autosummary:: + :toctree: _as_gen + :template: autosummary_class_only.rst + :nosignatures: + + Figure + + +Adding Axes and SubFigures +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.add_axes + Figure.add_subplot + Figure.subplots + Figure.subplot_mosaic + Figure.add_gridspec + Figure.get_axes + Figure.axes + Figure.delaxes + Figure.subfigures + Figure.add_subfigure + +Saving +------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.savefig + + +Annotating +---------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.colorbar + Figure.legend + Figure.text + Figure.suptitle + Figure.get_suptitle + Figure.supxlabel + Figure.get_supxlabel + Figure.supylabel + Figure.get_supylabel + Figure.align_labels + Figure.align_xlabels + Figure.align_ylabels + Figure.align_titles + Figure.autofmt_xdate + + +Figure geometry +--------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.set_size_inches + Figure.get_size_inches + Figure.set_figheight + Figure.get_figheight + Figure.set_figwidth + Figure.get_figwidth + Figure.dpi + Figure.set_dpi + Figure.get_dpi + +Subplot layout +-------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.subplots_adjust + Figure.set_layout_engine + Figure.get_layout_engine + +Discouraged or deprecated +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.tight_layout + Figure.set_tight_layout + Figure.get_tight_layout + Figure.set_constrained_layout + Figure.get_constrained_layout + Figure.set_constrained_layout_pads + Figure.get_constrained_layout_pads + +Interactive +----------- + +.. seealso:: + + - :ref:`event-handling` + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.ginput + Figure.add_axobserver + Figure.waitforbuttonpress + Figure.pick + +Modifying appearance +-------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.set_frameon + Figure.get_frameon + Figure.set_linewidth + Figure.get_linewidth + Figure.set_facecolor + Figure.get_facecolor + Figure.set_edgecolor + Figure.get_edgecolor + +Adding and getting Artists +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.add_artist + Figure.get_children + Figure.figimage + +Getting and modifying state +--------------------------- + +.. seealso:: + + - :ref:`interactive_figures` + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Figure.clear + Figure.gca + Figure.sca + Figure.get_tightbbox + Figure.get_window_extent + Figure.show + Figure.set_canvas + Figure.draw + Figure.draw_without_rendering + Figure.draw_artist + +.. _figure-api-subfigure: + +SubFigure +========= + +Matplotlib has the concept of a `~.SubFigure`, which is a logical figure inside +a parent `~.Figure`. It has many of the same methods as the parent. See +:ref:`nested_axes_layouts`. + +.. plot:: + + fig = plt.figure(layout='constrained', figsize=(4, 2.5), facecolor='lightgoldenrodyellow') + + # Make two subfigures, left ones more narrow than right ones: + sfigs = fig.subfigures(1, 2, width_ratios=[0.8, 1]) + sfigs[0].set_facecolor('khaki') + sfigs[1].set_facecolor('lightsalmon') + + # Add subplots to left subfigure: + lax = sfigs[0].subplots(2, 1) + sfigs[0].suptitle('Left subfigure') + + # Add subplots to right subfigure: + rax = sfigs[1].subplots(1, 2) + sfigs[1].suptitle('Right subfigure') + + # suptitle for the main figure: + fig.suptitle('Figure') + +SubFigure class +--------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary_class_only.rst + :nosignatures: + + SubFigure + +Adding Axes and SubFigures +-------------------------- +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.add_axes + SubFigure.add_subplot + SubFigure.subplots + SubFigure.subplot_mosaic + SubFigure.add_gridspec + SubFigure.delaxes + SubFigure.add_subfigure + SubFigure.subfigures + +Annotating +---------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.colorbar + SubFigure.legend + SubFigure.text + SubFigure.suptitle + SubFigure.get_suptitle + SubFigure.supxlabel + SubFigure.get_supxlabel + SubFigure.supylabel + SubFigure.get_supylabel + SubFigure.align_labels + SubFigure.align_xlabels + SubFigure.align_ylabels + SubFigure.align_titles + +Adding and getting Artists +-------------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.add_artist + SubFigure.get_children + +Modifying appearance +-------------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.set_frameon + SubFigure.get_frameon + SubFigure.set_linewidth + SubFigure.get_linewidth + SubFigure.set_facecolor + SubFigure.get_facecolor + SubFigure.set_edgecolor + SubFigure.get_edgecolor + +Passthroughs +------------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + SubFigure.set_dpi + SubFigure.get_dpi + + +FigureBase parent class +======================= + +.. autoclass:: FigureBase + +Helper functions +================ + +.. autofunction:: figaspect diff --git a/doc/api/finance_api.rst b/doc/api/finance_api.rst deleted file mode 100644 index 9b2c5021c675..000000000000 --- a/doc/api/finance_api.rst +++ /dev/null @@ -1,12 +0,0 @@ -******* -finance -******* - - -:mod:`matplotlib.finance` -========================= - -.. automodule:: matplotlib.finance - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/api/font_manager_api.rst b/doc/api/font_manager_api.rst index 88d5fb38e5f9..1a1b06da1fa9 100644 --- a/doc/api/font_manager_api.rst +++ b/doc/api/font_manager_api.rst @@ -1,21 +1,16 @@ -************ -font_manager -************ - -:mod:`matplotlib.font_manager` -============================== +*************************** +``matplotlib.font_manager`` +*************************** .. automodule:: matplotlib.font_manager :members: + :exclude-members: FontEntry :undoc-members: :show-inheritance: -:mod:`matplotlib.fontconfig_pattern` -==================================== - -.. automodule:: matplotlib.fontconfig_pattern - :members: - :undoc-members: - :show-inheritance: +.. data:: fontManager + The global instance of `FontManager`. +.. autoclass:: FontEntry + :no-undoc-members: diff --git a/doc/api/ft2font.rst b/doc/api/ft2font.rst new file mode 100644 index 000000000000..a1f984abdda5 --- /dev/null +++ b/doc/api/ft2font.rst @@ -0,0 +1,8 @@ +********************** +``matplotlib.ft2font`` +********************** + +.. automodule:: matplotlib.ft2font + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/gridspec_api.rst b/doc/api/gridspec_api.rst index d4771f02ebdd..fe1137d94113 100644 --- a/doc/api/gridspec_api.rst +++ b/doc/api/gridspec_api.rst @@ -1,12 +1,22 @@ -******** -gridspec -******** +*********************** +``matplotlib.gridspec`` +*********************** - -:mod:`matplotlib.gridspec` -========================== +.. currentmodule:: matplotlib.gridspec .. automodule:: matplotlib.gridspec - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-inherited-members: + +Classes +------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + GridSpec + SubplotSpec + GridSpecBase + GridSpecFromSubplotSpec + SubplotParams diff --git a/doc/api/hatch_api.rst b/doc/api/hatch_api.rst new file mode 100644 index 000000000000..b706be379a15 --- /dev/null +++ b/doc/api/hatch_api.rst @@ -0,0 +1,8 @@ +******************** +``matplotlib.hatch`` +******************** + +.. automodule:: matplotlib.hatch + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/image_api.rst b/doc/api/image_api.rst index 15afe2e8a1b6..df3177395eef 100644 --- a/doc/api/image_api.rst +++ b/doc/api/image_api.rst @@ -1,10 +1,6 @@ -***** -image -***** - - -:mod:`matplotlib.image` -======================= +******************** +``matplotlib.image`` +******************** .. automodule:: matplotlib.image :members: diff --git a/doc/api/index.rst b/doc/api/index.rst index 8d8892834c73..04c0e279a4fe 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -1,39 +1,114 @@ -.. _api-index: +API Reference +============= + +Matplotlib interfaces +--------------------- + +Matplotlib has two interfaces. See :ref:`api_interfaces` for a more detailed +description of both and their recommended use cases. + +.. grid:: 1 1 2 2 + :padding: 0 + :gutter: 2 + + .. grid-item-card:: + :shadow: none + :class-footer: api-interface-footer + + **Axes interface** (object-based, explicit) + + create a `.Figure` and one or more `~.axes.Axes` objects, then *explicitly* use + methods on these objects to add data, configure limits, set labels etc. + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + API: + + - `~.pyplot.subplots`: create Figure and Axes + - :mod:`~matplotlib.axes`: add data, limits, labels etc. + - `.Figure`: for figure-level methods + + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Example: + + .. code-block:: python + :class: api-interface-example + + fig, ax = plt.subplots() + ax.plot(x, y) + ax.set_title("Sample plot") + plt.show() + -#################### - The Matplotlib API -#################### + .. grid-item-card:: + :shadow: none + :class-footer: api-interface-footer -.. htmlonly:: + **pyplot interface** (function-based, implicit) - :Release: |version| - :Date: |today| + consists of functions in the `.pyplot` module. Figure and Axes are manipulated + through these functions and are only *implicitly* present in the background. + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + API: + + - `matplotlib.pyplot` + + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Example: + + .. code-block:: python + :class: api-interface-example + + plt.plot(x, y) + plt.title("Sample plot") + plt.show() + + +.. _api-index: + +Modules +------- + +Alphabetical list of modules: .. toctree:: :maxdepth: 1 - pyplot_summary.rst - api_changes.rst matplotlib_configuration_api.rst - afm_api.rst animation_api.rst artist_api.rst axes_api.rst axis_api.rst + backend_bases_api.rst + backend_managers_api.rst + backend_tools_api.rst index_backend_api.rst + bezier_api.rst + category_api.rst cbook_api.rst cm_api.rst collections_api.rst colorbar_api.rst + colorizer_api.rst colors_api.rst + container_api.rst + contour_api.rst dates_api.rst dviread.rst figure_api.rst - finance_api.rst font_manager_api.rst + ft2font.rst gridspec_api.rst + hatch_api.rst image_api.rst + inset_api.rst + layout_engine_api.rst legend_api.rst + legend_handler_api.rst lines_api.rst markers_api.rst mathtext_api.rst @@ -42,16 +117,36 @@ patches_api.rst path_api.rst patheffects_api.rst + pyplot_summary.rst projections_api.rst - pyplot_api.rst + quiver_api.rst + rcsetup_api.rst sankey_api.rst scale_api.rst + sphinxext_mathmpl_api.rst + sphinxext_plot_directive_api.rst + sphinxext_figmpl_directive_api.rst + sphinxext_roles.rst spines_api.rst style_api.rst + table_api.rst + testing_api.rst text_api.rst + texmanager_api.rst ticker_api.rst - tight_layout_api.rst + transformations.rst tri_api.rst - type1font.rst + typing_api.rst units_api.rst widgets_api.rst + _afm_api.rst + _api_api.rst + _docstring_api.rst + _enums_api.rst + _type1font.rst + _tight_bbox_api.rst + _tight_layout_api.rst + toolkits/mplot3d.rst + toolkits/axes_grid1.rst + toolkits/axisartist.rst + pylab.rst diff --git a/doc/api/index_backend_api.rst b/doc/api/index_backend_api.rst index 7153193529e4..66009d86825d 100644 --- a/doc/api/index_backend_api.rst +++ b/doc/api/index_backend_api.rst @@ -1,18 +1,26 @@ -******** -backends -******** +*********************** +``matplotlib.backends`` +*********************** + +.. module:: matplotlib.backends .. toctree:: + :maxdepth: 1 - backend_bases_api.rst - backend_managers_api.rst - backend_tools_api.rst - backend_gtkagg_api.rst - backend_qt4agg_api.rst - backend_qt5agg_api.rst - backend_wxagg_api.rst + backend_mixed_api.rst + backend_template_api.rst + backend_agg_api.rst + backend_cairo_api.rst + backend_gtk3_api.rst + backend_gtk4_api.rst + backend_nbagg_api.rst backend_pdf_api.rst + backend_pgf_api.rst + backend_ps_api.rst + backend_registry_api.rst + backend_qt_api.rst backend_svg_api.rst -.. backend_webagg.rst - dviread.rst - type1font.rst + backend_tk_api.rst + backend_webagg_core_api.rst + backend_webagg_api.rst + backend_wx_api.rst diff --git a/doc/api/inset_api.rst b/doc/api/inset_api.rst new file mode 100644 index 000000000000..d8b89a106a7a --- /dev/null +++ b/doc/api/inset_api.rst @@ -0,0 +1,8 @@ +******************** +``matplotlib.inset`` +******************** + +.. automodule:: matplotlib.inset + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/layout_engine_api.rst b/doc/api/layout_engine_api.rst new file mode 100644 index 000000000000..8890061e0979 --- /dev/null +++ b/doc/api/layout_engine_api.rst @@ -0,0 +1,9 @@ +**************************** +``matplotlib.layout_engine`` +**************************** + +.. currentmodule:: matplotlib.layout_engine + +.. automodule:: matplotlib.layout_engine + :members: + :inherited-members: diff --git a/doc/api/legend_api.rst b/doc/api/legend_api.rst index 9893ae31dc78..c6808b15665d 100644 --- a/doc/api/legend_api.rst +++ b/doc/api/legend_api.rst @@ -1,18 +1,8 @@ -************************* -legend and legend_handler -************************* - - -:mod:`matplotlib.legend` -========================= +********************* +``matplotlib.legend`` +********************* .. automodule:: matplotlib.legend :members: :undoc-members: :show-inheritance: - -:mod:`matplotlib.legend_handler` -================================ -.. automodule:: matplotlib.legend_handler - :members: - :undoc-members: diff --git a/doc/api/legend_handler_api.rst b/doc/api/legend_handler_api.rst new file mode 100644 index 000000000000..dfdf04730ce2 --- /dev/null +++ b/doc/api/legend_handler_api.rst @@ -0,0 +1,7 @@ +***************************** +``matplotlib.legend_handler`` +***************************** + +.. automodule:: matplotlib.legend_handler + :members: + :undoc-members: diff --git a/doc/api/lines_api.rst b/doc/api/lines_api.rst index 46b7a3bfe58d..bf7ec73493a3 100644 --- a/doc/api/lines_api.rst +++ b/doc/api/lines_api.rst @@ -1,12 +1,29 @@ -***** -lines -***** +******************** +``matplotlib.lines`` +******************** - -:mod:`matplotlib.lines` -======================= +.. currentmodule:: matplotlib.lines .. automodule:: matplotlib.lines - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-inherited-members: + +Classes +------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Line2D + VertexSelector + AxLine + +Functions +--------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + segment_hits diff --git a/doc/api/markers_api.rst b/doc/api/markers_api.rst index 291cca259734..5193d2dd90c9 100644 --- a/doc/api/markers_api.rst +++ b/doc/api/markers_api.rst @@ -1,12 +1,18 @@ -******* -markers -******* +********************** +``matplotlib.markers`` +********************** - -:mod:`matplotlib.markers` -========================= +.. currentmodule:: matplotlib.markers .. automodule:: matplotlib.markers - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-inherited-members: + +Classes +------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + MarkerStyle diff --git a/doc/api/mathtext_api.rst b/doc/api/mathtext_api.rst index 689c0ade3aa5..295ed0382c61 100644 --- a/doc/api/mathtext_api.rst +++ b/doc/api/mathtext_api.rst @@ -1,13 +1,10 @@ -******** -mathtext -******** +*********************** +``matplotlib.mathtext`` +*********************** .. inheritance-diagram:: matplotlib.mathtext :parts: 1 -:mod:`matplotlib.mathtext` -============================= - .. automodule:: matplotlib.mathtext :members: :undoc-members: diff --git a/doc/api/matplotlib_configuration_api.rst b/doc/api/matplotlib_configuration_api.rst index 9f0ad3678ffb..b2fafc08e5fc 100644 --- a/doc/api/matplotlib_configuration_api.rst +++ b/doc/api/matplotlib_configuration_api.rst @@ -1,37 +1,91 @@ -The top level :mod:`matplotlib` module -====================================== - +************** +``matplotlib`` +************** .. py:currentmodule:: matplotlib +.. automodule:: matplotlib + :no-members: + :no-undoc-members: + :noindex: + +Backend management +================== + .. autofunction:: use .. autofunction:: get_backend -.. py:data:: rcParams +.. autofunction:: interactive - An instance of :class:`RcParams` for handling default matplotlib values. +.. autofunction:: is_interactive -.. autofunction:: rc +Default values and styling +========================== -.. autofunction::rcdefaults +.. py:data:: rcParams + :type: RcParams -.. autofunction::rc_file + The global configuration settings for Matplotlib. -.. autofunction::rc_context + This is a dictionary-like variable that stores the current configuration + settings. Many of the values control styling, but others control + various aspects of Matplotlib's behavior. -.. autofunction:: matplotlib_fname + See :doc:`/users/explain/configuration` for a full list of config + parameters. -.. autofunction::rc_file_defaults + See :ref:`customizing` for usage information. -.. autofunction::interactive + Notes + ----- + This object is also available as ``plt.rcParams`` via the + `matplotlib.pyplot` module (which by convention is imported as ``plt``). -.. autofunction::is_interactive .. autoclass:: RcParams + :no-members: + + .. automethod:: find_all + .. automethod:: copy + +.. autofunction:: rc_context + +.. autofunction:: rc + +.. autofunction:: rcdefaults + +.. autofunction:: rc_file_defaults + +.. autofunction:: rc_file .. autofunction:: rc_params .. autofunction:: rc_params_from_file -.. autoclass:: rc_context +.. autofunction:: get_configdir + +.. autofunction:: matplotlib_fname + +.. autofunction:: get_data_path + +Logging +======= + +.. autofunction:: set_loglevel + +Colormaps and color sequences +============================= + +.. autodata:: colormaps + :no-value: + +.. autodata:: color_sequences + :no-value: + +Miscellaneous +============= + +.. autoclass:: MatplotlibDeprecationWarning + +.. autofunction:: get_cachedir diff --git a/doc/api/mlab_api.rst b/doc/api/mlab_api.rst index 4e326b353a50..3e80b04cad27 100644 --- a/doc/api/mlab_api.rst +++ b/doc/api/mlab_api.rst @@ -1,10 +1,6 @@ -**** -mlab -**** - - -:mod:`matplotlib.mlab` -======================= +******************* +``matplotlib.mlab`` +******************* .. automodule:: matplotlib.mlab :members: diff --git a/doc/api/next_api_changes.rst b/doc/api/next_api_changes.rst new file mode 100644 index 000000000000..9c0630697763 --- /dev/null +++ b/doc/api/next_api_changes.rst @@ -0,0 +1,44 @@ + +================ +Next API changes +================ + +.. ifconfig:: releaselevel == 'dev' + + This page lists API changes for the next release. + + Behavior changes + ---------------- + + .. toctree:: + :glob: + :maxdepth: 1 + + next_api_changes/behavior/* + + Deprecations + ------------ + + .. toctree:: + :glob: + :maxdepth: 1 + + next_api_changes/deprecations/* + + Removals + -------- + + .. toctree:: + :glob: + :maxdepth: 1 + + next_api_changes/removals/* + + Development changes + ------------------- + + .. toctree:: + :glob: + :maxdepth: 1 + + next_api_changes/development/* diff --git a/doc/api/next_api_changes/README.rst b/doc/api/next_api_changes/README.rst new file mode 100644 index 000000000000..1f40122aa318 --- /dev/null +++ b/doc/api/next_api_changes/README.rst @@ -0,0 +1,45 @@ +:orphan: + +.. NOTE TO EDITORS OF THIS FILE + This file serves as the README directly available in the file system next to the + next_api_changes entries. The content between the ``api-change-guide-*`` markers is + additionally included in the documentation page ``doc/devel/api_changes.rst``. Please + check that the page builds correctly after changing this file. + +Adding API change notes +======================= + +.. api-change-guide-start + +API change notes for future releases are collected in :file:`doc/api/next_api_changes/`. +They are divided into four subdirectories: + +- **Deprecations**: Announcements of future changes. Typically, these will + raise a deprecation warning and users of this API should change their code + to stay compatible with future releases of Matplotlib. If possible, state + what should be used instead. +- **Removals**: Parts of the API that got removed. If possible, state what + should be used instead. +- **Behaviour changes**: API that stays valid but will yield a different + result. +- **Development changes**: Changes to the build process, dependencies, etc. + +Please place new entries in these directories with a new file named +``99999-ABC.rst``, where ``99999`` would be the PR number, and ``ABC`` the +author's initials. Typically, each change will get its own file, but you may +also amend existing files when suitable. The overall goal is a comprehensible +documentation of the changes. + +A typical entry could look like this:: + + Locators + ~~~~~~~~ + The unused `Locator.autoscale()` method is deprecated (pass the axis + limits to `Locator.view_limits()` instead). + +Please avoid using references in section titles, as it causes links to be +confusing in the table of contents. Instead, ensure that a reference is +included in the descriptive text. Use inline literals (double backticks) +to denote code objects in the title. + +.. api-change-guide-end diff --git a/doc/api/next_api_changes/behavior/00001-ABC.rst b/doc/api/next_api_changes/behavior/00001-ABC.rst new file mode 100644 index 000000000000..f6d8c1d8b351 --- /dev/null +++ b/doc/api/next_api_changes/behavior/00001-ABC.rst @@ -0,0 +1,7 @@ +Behavior change template +~~~~~~~~~~~~~~~~~~~~~~~~ + +Enter description here.... + +Please rename file with PR number and your initials i.e. "99999-ABC.rst" +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/behavior/28437-CH.rst b/doc/api/next_api_changes/behavior/28437-CH.rst new file mode 100644 index 000000000000..bb303bbe9d3b --- /dev/null +++ b/doc/api/next_api_changes/behavior/28437-CH.rst @@ -0,0 +1,8 @@ +*alpha* parameter handling on images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When passing and array to ``imshow(..., alpha=...)``, the parameter was silently ignored +if the image data was an RGB or RBGA image or if :rc:`image.interpolation_stage` +resolved to "rbga". + +This is now fixed, and the alpha array overwrites any previous transparency information. diff --git a/doc/api/next_api_changes/behavior/29054-AL.rst b/doc/api/next_api_changes/behavior/29054-AL.rst new file mode 100644 index 000000000000..3c65e9ae9b78 --- /dev/null +++ b/doc/api/next_api_changes/behavior/29054-AL.rst @@ -0,0 +1,11 @@ +Minor log tick labels are set depending on number of major log ticks, not on number of decades spanned +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, by default, on a log-scaled axis, the minor ticks would be +unlabeled if the axis limits spanned more than one decade. The meaning of the +``minor_thresholds`` parameter to `.LogFormatter` has been altered so that the +decision of whether to label the minor ticks is now based on the number of +major ticks drawn within the axis limits. + +For example, for an axis spanning from 4 to 60 (with thus a single major log +tick, at 10), minor ticks are now labeled, even though the axis spans more than +one decade. diff --git a/doc/api/next_api_changes/behavior/29256_IMT.rst b/doc/api/next_api_changes/behavior/29256_IMT.rst new file mode 100644 index 000000000000..57ec8f68d85c --- /dev/null +++ b/doc/api/next_api_changes/behavior/29256_IMT.rst @@ -0,0 +1,6 @@ +Setting titles of figures using webagg backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously when using the ``webagg`` backend the title of a figure was set using +``figure.set_label``. Now it is set using ``figure.canvas.manager.set_window_title`` +which is more consistent with other backends. diff --git a/doc/api/next_api_changes/behavior/29827-ES.rst b/doc/api/next_api_changes/behavior/29827-ES.rst new file mode 100644 index 000000000000..d25dfa0c6574 --- /dev/null +++ b/doc/api/next_api_changes/behavior/29827-ES.rst @@ -0,0 +1,6 @@ +``matplotlib.testing.check_figures_equal`` defaults to PNG only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In most cases, checking that figures are equal with `.check_figures_equal` does not +depend on the file format. Consequently, the *extensions* parameter now defaults to +``['png']`` instead of ``['png', 'pdf', 'svg']``, reducing default test requirements. diff --git a/doc/api/next_api_changes/behavior/29832-REC.rst b/doc/api/next_api_changes/behavior/29832-REC.rst new file mode 100644 index 000000000000..a23b043a823b --- /dev/null +++ b/doc/api/next_api_changes/behavior/29832-REC.rst @@ -0,0 +1,11 @@ +Mixing positional and keyword arguments for ``legend`` handles and labels... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is no longer valid. If passing *handles* and *labels* to ``legend``, they must +now be passed either both positionally or both as keywords. + +Legend labels for ``plot`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. Now, if the sequence length is not one an error is raised. To keep the old +behavior, cast the sequence to string before passing. diff --git a/doc/api/next_api_changes/behavior/29958-TH.rst b/doc/api/next_api_changes/behavior/29958-TH.rst new file mode 100644 index 000000000000..cacaf2bac612 --- /dev/null +++ b/doc/api/next_api_changes/behavior/29958-TH.rst @@ -0,0 +1,8 @@ +``Axes.add_collection(..., autolim=True)`` updates view limits +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Axes.add_collection(..., autolim=True)`` has so far only updated the data limits. +Users needed to additionally call `.Axes.autoscale_view` to update the view limits. +View limits are now updated as well if ``autolim=True``, using a lazy internal +update mechanism, so that the costs only apply once also if you add multiple +collections. diff --git a/doc/api/next_api_changes/behavior/30272-ES.rst b/doc/api/next_api_changes/behavior/30272-ES.rst new file mode 100644 index 000000000000..5a03f9bc7972 --- /dev/null +++ b/doc/api/next_api_changes/behavior/30272-ES.rst @@ -0,0 +1,2 @@ +``font_manager.findfont`` logs if selected font weight does not match requested +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/next_api_changes/behavior/30532-TH.rst b/doc/api/next_api_changes/behavior/30532-TH.rst new file mode 100644 index 000000000000..3d368c566039 --- /dev/null +++ b/doc/api/next_api_changes/behavior/30532-TH.rst @@ -0,0 +1,4 @@ +Default name of ``ListedColormap`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default name of `.ListedColormap` has changed from "from_list" to "unnamed". diff --git a/doc/api/next_api_changes/behavior/30634-AL.rst b/doc/api/next_api_changes/behavior/30634-AL.rst new file mode 100644 index 000000000000..585de1ea14eb --- /dev/null +++ b/doc/api/next_api_changes/behavior/30634-AL.rst @@ -0,0 +1,6 @@ +hist2d no longer forces axes limits +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, `.Axes.hist2d` would force the axes x and y limits to the extents +of the histogrammed data, ignoring any other artists. `.Axes.hist2d` now +behaves similarly to `.Axes.imshow`: axes limits are updated to fit the data, +but autoscaling is not otherwise disabled. diff --git a/doc/api/next_api_changes/behavior/30824-AYS.rst b/doc/api/next_api_changes/behavior/30824-AYS.rst new file mode 100644 index 000000000000..a190bd537126 --- /dev/null +++ b/doc/api/next_api_changes/behavior/30824-AYS.rst @@ -0,0 +1,6 @@ +Bivariate colormaps now fully span the intended range of colors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Bivariate colormaps generated by ``SegmentedBivarColormap`` (e.g., ``BiOrangeBlue``) +from a set of input colors now fully span that range of colors. There had been a bug +with the numerical interpolation such that the colormap did not actually include the +first or last colors. diff --git a/doc/api/next_api_changes/deprecations/00001-ABC.rst b/doc/api/next_api_changes/deprecations/00001-ABC.rst new file mode 100644 index 000000000000..541047ed1d8d --- /dev/null +++ b/doc/api/next_api_changes/deprecations/00001-ABC.rst @@ -0,0 +1,7 @@ +Template for deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Add description here... + +Please rename file with PR number and your initials i.e. "99999-ABC.rst" +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/deprecations/27551-AL.rst b/doc/api/next_api_changes/deprecations/27551-AL.rst new file mode 100644 index 000000000000..4811f542bd5f --- /dev/null +++ b/doc/api/next_api_changes/deprecations/27551-AL.rst @@ -0,0 +1,4 @@ +``GridFinder.transform_xy`` and ``GridFinder.inv_transform_xy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated. Directly use the standard transform returned by +`.GridFinder.get_transform` instead. diff --git a/doc/api/next_api_changes/deprecations/27972-AL.rst b/doc/api/next_api_changes/deprecations/27972-AL.rst new file mode 100644 index 000000000000..cf14b24345b5 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/27972-AL.rst @@ -0,0 +1,8 @@ +``axes_grid.Grid.ngrids`` +~~~~~~~~~~~~~~~~~~~~~~~~~ +This attribute has been deprecated and renamed ``n_axes``, consistently with +the new name of the `~.axes_grid.Grid` constructor parameter that allows +setting the actual number of axes in the grid (the old parameter, ``ngrids``, +did not actually work since Matplotlib 3.3). + +The same change has been made in ``axes_grid.ImageGrid``. diff --git a/doc/api/next_api_changes/deprecations/27998-TS.rst b/doc/api/next_api_changes/deprecations/27998-TS.rst new file mode 100644 index 000000000000..ab69b87f0989 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/27998-TS.rst @@ -0,0 +1,7 @@ +``violinplot`` and ``violin`` *vert* parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter *vert: bool* has been deprecated on `~.Axes.violinplot` and +`~.Axes.violin`. +It will be replaced by *orientation: {"vertical", "horizontal"}* for API +consistency. diff --git a/doc/api/next_api_changes/deprecations/28074-TS.rst b/doc/api/next_api_changes/deprecations/28074-TS.rst new file mode 100644 index 000000000000..6a8b5d4b21b8 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/28074-TS.rst @@ -0,0 +1,9 @@ +``boxplot`` and ``bxp`` *vert* parameter, and ``rcParams["boxplot.vertical"]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter *vert: bool* has been deprecated on `~.Axes.boxplot` and +`~.Axes.bxp`. It is replaced by *orientation: {"vertical", "horizontal"}* +for API consistency. + +``rcParams["boxplot.vertical"]``, which controlled the orientation of ``boxplot``, +is deprecated without replacement. diff --git a/doc/api/next_api_changes/deprecations/29135-TH.rst b/doc/api/next_api_changes/deprecations/29135-TH.rst new file mode 100644 index 000000000000..e2289a248076 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29135-TH.rst @@ -0,0 +1,5 @@ +Parameter ``ListedColormap(..., N=...)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing the parameter *N* to `.ListedColormap` is deprecated. +Please preprocess the list colors yourself if needed. diff --git a/doc/api/next_api_changes/deprecations/29358-TH.rst b/doc/api/next_api_changes/deprecations/29358-TH.rst new file mode 100644 index 000000000000..1b7a50456afc --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29358-TH.rst @@ -0,0 +1,17 @@ +3rd party scales do not need to have an *axis* parameter anymore +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since matplotlib 3.1 `PR 12831 `_ +scale objects should be reusable and therefore independent of any particular Axis. +Therefore, the use of the *axis* parameter in the ``__init__`` had been discouraged. +However, having that parameter in the signature was still necessary for API +backwards-compatibility. This is no longer the case. + +`.register_scale` now accepts scale classes with or without this parameter. + +The *axis* parameter is pending-deprecated. It will be deprecated in matplotlib 3.13, +and removed in matplotlib 3.15. + +3rd-party scales are recommended to remove the *axis* parameter now if they can +afford to restrict compatibility to matplotlib >= 3.11 already. Otherwise, they may +keep the *axis* parameter and remove it in time for matplotlib 3.13. diff --git a/doc/api/next_api_changes/deprecations/29529-TH.rst b/doc/api/next_api_changes/deprecations/29529-TH.rst new file mode 100644 index 000000000000..e396e68c4aa1 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29529-TH.rst @@ -0,0 +1,7 @@ +Capitalization of None in matplotlibrc +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In :file:`matplotlibrc` config files every capitalization of None was +accepted for denoting the Python constant `None`. This is deprecated. The +only accepted capitalization is now None, i.e. starting with a capital letter +and all other letters in lowercase. diff --git a/doc/api/next_api_changes/deprecations/29817-AL.rst b/doc/api/next_api_changes/deprecations/29817-AL.rst new file mode 100644 index 000000000000..f3b339ed7c10 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29817-AL.rst @@ -0,0 +1,7 @@ +``DviFont.widths`` +~~~~~~~~~~~~~~~~~~ +... is deprecated with no replacement. + +Direct access to ``Tfm``'s ``widths``, ``heights``, ``depths`` dicts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated; access a glyph's metrics with `.Tfm.get_metrics` instead. diff --git a/doc/api/next_api_changes/deprecations/29904-tac.rst b/doc/api/next_api_changes/deprecations/29904-tac.rst new file mode 100644 index 000000000000..8e4f986ffa77 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29904-tac.rst @@ -0,0 +1,16 @@ + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.11, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+----------------+ +| Dependency | min in mpl3.10 | min in mpl3.11 | ++============+=================+================+ +| Python | 3.10 | 3.11 | +| NumPy | 1.23 | 1.25 | ++------------+-----------------+----------------+ + +This is consistent with our :ref:`min_deps_policy` and `SPEC0 +`__ diff --git a/doc/api/next_api_changes/deprecations/29993-AL.rst b/doc/api/next_api_changes/deprecations/29993-AL.rst new file mode 100644 index 000000000000..9104fd669325 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/29993-AL.rst @@ -0,0 +1,4 @@ +``testing.widgets.mock_event`` and ``testing.widgets.do_event`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated. Directly construct Event objects (typically `.MouseEvent` +or `.KeyEvent`) and pass them to ``canvas.callbacks.process()`` instead. diff --git a/doc/api/next_api_changes/deprecations/30027-AL.rst b/doc/api/next_api_changes/deprecations/30027-AL.rst new file mode 100644 index 000000000000..ed65d9391371 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30027-AL.rst @@ -0,0 +1,3 @@ +``PdfFile.fontNames``, ``PdfFile.dviFontInfo``, ``PdfFile.type1Descriptors`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated with no replacement. diff --git a/doc/api/next_api_changes/deprecations/30044-AL.rst b/doc/api/next_api_changes/deprecations/30044-AL.rst new file mode 100644 index 000000000000..e004d5f2730f --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30044-AL.rst @@ -0,0 +1,12 @@ +``FT2Image`` +~~~~~~~~~~~~ +... is deprecated. Use 2D uint8 ndarrays instead. In particular: + +- The ``FT2Image`` constructor took ``width, height`` as separate parameters + but the ndarray constructor takes ``(height, width)`` as single tuple + parameter. +- `.FT2Font.draw_glyph_to_bitmap` now (also) takes 2D uint8 arrays as input. +- ``FT2Image.draw_rect_filled`` should be replaced by directly setting pixel + values to black. +- The ``image`` attribute of the object returned by ``MathTextParser("agg").parse`` + is now a 2D uint8 array. diff --git a/doc/api/next_api_changes/deprecations/30070-OG.rst b/doc/api/next_api_changes/deprecations/30070-OG.rst new file mode 100644 index 000000000000..98786bcfa1d2 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30070-OG.rst @@ -0,0 +1,4 @@ +``BezierSegment.point_at_t`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. Instead, it is possible to call the BezierSegment with an argument. diff --git a/doc/api/next_api_changes/deprecations/30088-AL.rst b/doc/api/next_api_changes/deprecations/30088-AL.rst new file mode 100644 index 000000000000..ae1338da7f85 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30088-AL.rst @@ -0,0 +1,4 @@ +*fontfile* parameter of ``PdfFile.createType1Descriptor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This parameter is deprecated; all relevant pieces of information are now +directly extracted from the *t1font* argument. diff --git a/doc/api/next_api_changes/deprecations/30163-AL.rst b/doc/api/next_api_changes/deprecations/30163-AL.rst new file mode 100644 index 000000000000..15d0077375f2 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30163-AL.rst @@ -0,0 +1,9 @@ +``matplotlib.style.core`` +~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``matplotlib.style.core`` module is deprecated. All APIs intended for +public use are now available in `matplotlib.style` directly (including +``USER_LIBRARY_PATHS``, which was previously not reexported). + +The following APIs of ``matplotlib.style.core`` have been deprecated with no +replacement: ``BASE_LIBRARY_PATH``, ``STYLE_EXTENSION``, ``STYLE_BLACKLIST``, +``update_user_library``, ``read_style_directory``, ``update_nested_dict``. diff --git a/doc/api/next_api_changes/deprecations/30349-AL.rst b/doc/api/next_api_changes/deprecations/30349-AL.rst new file mode 100644 index 000000000000..78e26f41889f --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30349-AL.rst @@ -0,0 +1,3 @@ +``Axes.set_navigate_mode`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... with no replacement. diff --git a/doc/api/next_api_changes/deprecations/30364-AS.rst b/doc/api/next_api_changes/deprecations/30364-AS.rst new file mode 100644 index 000000000000..4f5493b8b706 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30364-AS.rst @@ -0,0 +1,4 @@ +Parameters ``Axes3D.set_aspect(..., anchor=..., share=...)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The parameters *anchor* and *share* of `.Axes3D.set_aspect` are deprecated. +They had no effect on 3D axes and will be removed in a future version. diff --git a/doc/api/next_api_changes/deprecations/30368-AL.rst b/doc/api/next_api_changes/deprecations/30368-AL.rst new file mode 100644 index 000000000000..efd3c8360ef3 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30368-AL.rst @@ -0,0 +1,3 @@ +``GridFinder.get_grid_info`` now takes a single bbox as parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing ``x1, y1, x2, y2`` as separate parameters is deprecated. diff --git a/doc/api/next_api_changes/deprecations/30469-AL.rst b/doc/api/next_api_changes/deprecations/30469-AL.rst new file mode 100644 index 000000000000..ef3f042843c2 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30469-AL.rst @@ -0,0 +1,4 @@ +The *axes* parameter of ``RadialLocator`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. `~.polar.RadialLocator` now fetches the relevant information +from the Axis' parent Axes. diff --git a/doc/api/next_api_changes/deprecations/30531-TH.rst b/doc/api/next_api_changes/deprecations/30531-TH.rst new file mode 100644 index 000000000000..19d51fd2fb6c --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30531-TH.rst @@ -0,0 +1,16 @@ +In-place modifications of colormaps +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Colormaps are planned to become immutable in the long term. + +As a first step, in-place modifications of colormaps are now pending-deprecated. +This affects the following methods of `.Colormap`: + +- `.Colormap.set_bad` - use ``cmap.with_extremes(bad=...)`` instead +- `.Colormap.set_under` - use ``cmap.with_extremes(under=...)`` instead +- `.Colormap.set_over` - use ``cmap.with_extremes(over=...)`` instead +- `.Colormap.set_extremes` - use ``cmap.with_extremes(...)`` instead + +Use the respective `.Colormap.with_extremes` and appropriate keyword arguments +instead which returns a copy of the colormap (available since matplotlib 3.4). +Alternatively, if you create the colormap yourself, you can also pass the +respective arguments to the constructor (available since matplotlib 3.11). diff --git a/doc/api/next_api_changes/deprecations/30737-TH.rst b/doc/api/next_api_changes/deprecations/30737-TH.rst new file mode 100644 index 000000000000..8feef85912b4 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30737-TH.rst @@ -0,0 +1,8 @@ +The *canvas* parameter to ``MultiCursor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. It has been unused for a while already. + +Please remove the parameter and change the call from +``MultiCursor(canvas, axes)`` to ``MultiCursor(axes)``. Both calls are +valid throughout the deprecation period. diff --git a/doc/api/next_api_changes/deprecations/30889-TH.rst b/doc/api/next_api_changes/deprecations/30889-TH.rst new file mode 100644 index 000000000000..d221ae30d4fb --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30889-TH.rst @@ -0,0 +1,10 @@ +Transforms helper functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following functions in the `.transforms` module are deprecated, +because they are considerer internal functionality and should not be used +by end users: + +- ``matplotlib.transforms.nonsingular`` +- ``matplotlib.transforms.interval_contains`` +- ``matplotlib.transforms.interval_contains_open`` diff --git a/doc/api/next_api_changes/development/00001-ABC.rst b/doc/api/next_api_changes/development/00001-ABC.rst new file mode 100644 index 000000000000..6db90a13e44c --- /dev/null +++ b/doc/api/next_api_changes/development/00001-ABC.rst @@ -0,0 +1,7 @@ +Development change template +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Enter description here.... + +Please rename file with PR number and your initials i.e. "99999-ABC.rst" +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/development/29745-DS.rst b/doc/api/next_api_changes/development/29745-DS.rst new file mode 100644 index 000000000000..7d9b1c2b143b --- /dev/null +++ b/doc/api/next_api_changes/development/29745-DS.rst @@ -0,0 +1,4 @@ +New minimum version of pyparsing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The minimum required version of ``pyparsing`` has been updated from 2.3.1 to 3.0.0. diff --git a/doc/api/next_api_changes/removals/00001-ABC.rst b/doc/api/next_api_changes/removals/00001-ABC.rst new file mode 100644 index 000000000000..3cc5b6344f7f --- /dev/null +++ b/doc/api/next_api_changes/removals/00001-ABC.rst @@ -0,0 +1,7 @@ +Removal change template +~~~~~~~~~~~~~~~~~~~~~~~ + +Enter description of methods/classes removed here.... + +Please rename file with PR number and your initials i.e. "99999-ABC.rst" +and ``git add`` the new file. diff --git a/doc/api/next_api_changes/removals/29697-REC.rst b/doc/api/next_api_changes/removals/29697-REC.rst new file mode 100644 index 000000000000..0155578f0c21 --- /dev/null +++ b/doc/api/next_api_changes/removals/29697-REC.rst @@ -0,0 +1,10 @@ +``plot_date`` +~~~~~~~~~~~~~ + +Use of ``plot_date`` has been discouraged since Matplotlib 3.5 and deprecated +since 3.9. The ``plot_date`` function has now been removed. + +- ``datetime``-like data should directly be plotted using `~.Axes.plot`. +- If you need to plot plain numeric data as :ref:`date-format` or need to set + a timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` before + `~.Axes.plot`. See `.Axis.axis_date`. diff --git a/doc/api/next_api_changes/removals/30004-DS.rst b/doc/api/next_api_changes/removals/30004-DS.rst new file mode 100644 index 000000000000..f5fdf214366c --- /dev/null +++ b/doc/api/next_api_changes/removals/30004-DS.rst @@ -0,0 +1,10 @@ +``apply_theta_transforms`` option in ``PolarTransform`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Applying theta transforms in `~matplotlib.projections.polar.PolarTransform` and +`~matplotlib.projections.polar.InvertedPolarTransform` has been removed, and +the ``apply_theta_transforms`` keyword argument removed from both classes. + +If you need to retain the behaviour where theta values +are transformed, chain the ``PolarTransform`` with a `~matplotlib.transforms.Affine2D` +transform that performs the theta shift and/or sign shift. diff --git a/doc/api/next_api_changes/removals/30005-DS.rst b/doc/api/next_api_changes/removals/30005-DS.rst new file mode 100644 index 000000000000..a5ba482c848f --- /dev/null +++ b/doc/api/next_api_changes/removals/30005-DS.rst @@ -0,0 +1,11 @@ +``matplotlib.cm.get_cmap`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Colormaps are now available through the `.ColormapRegistry` accessible via +`matplotlib.colormaps` or `matplotlib.pyplot.colormaps`. + +If you have the name of a colormap as a string, you can use a direct lookup, +``matplotlib.colormaps[name]`` or ``matplotlib.pyplot.colormaps[name]`` . Alternatively, ``matplotlib.colormaps.get_cmap`` will +maintain the existing behavior of additionally passing through `.Colormap` instances +and converting ``None`` to the default colormap. `matplotlib.pyplot.get_cmap` will stay as a +shortcut to ``matplotlib.colormaps.get_cmap``. diff --git a/doc/api/next_api_changes/removals/30014-DS.rst b/doc/api/next_api_changes/removals/30014-DS.rst new file mode 100644 index 000000000000..d13737f17e40 --- /dev/null +++ b/doc/api/next_api_changes/removals/30014-DS.rst @@ -0,0 +1,4 @@ +``GridHelperCurveLinear.get_tick_iterator`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed with no replacement. diff --git a/doc/api/next_api_changes/removals/30015-DS.rst b/doc/api/next_api_changes/removals/30015-DS.rst new file mode 100644 index 000000000000..e5f17518a9f3 --- /dev/null +++ b/doc/api/next_api_changes/removals/30015-DS.rst @@ -0,0 +1,9 @@ +*nth_coord* parameter to axisartist helpers for fixed axis +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Helper APIs in `.axisartist` for generating a "fixed" axis on rectilinear axes +(`.FixedAxisArtistHelperRectilinear`) no longer take a *nth_coord* parameter. +That parameter is entirely inferred from the (required) *loc* parameter. + +For curvilinear axes, the *nth_coord* parameter remains supported (it affects +the *ticks*, not the axis position itself), but it is now keyword-only. diff --git a/doc/api/next_api_changes/removals/30067-OG.rst b/doc/api/next_api_changes/removals/30067-OG.rst new file mode 100644 index 000000000000..1a8d8bc5c2c5 --- /dev/null +++ b/doc/api/next_api_changes/removals/30067-OG.rst @@ -0,0 +1,23 @@ +``TransformNode.is_bbox`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is removed. Instead check the object using ``isinstance(..., BboxBase)``. + +``rcsetup.interactive_bk``, ``rcsetup.non_interactive_bk`` and ``rcsetup.all_backends`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... are removed and replaced by ``matplotlib.backends.backend_registry.list_builtin`` +with the following arguments + +- ``matplotlib.backends.BackendFilter.INTERACTIVE`` +- ``matplotlib.backends.BackendFilter.NON_INTERACTIVE`` +- ``None`` + +``BboxTransformToMaxOnly`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is removed. It can be replaced by ``BboxTransformTo(LockableBbox(bbox, x0=0, y0=0))``. + +*interval* parameter of ``TimerBase.start`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The timer interval parameter can no longer be set while starting it. The interval can be specified instead in the timer constructor, or by setting the timer.interval attribute. diff --git a/doc/api/offsetbox_api.rst b/doc/api/offsetbox_api.rst index 1ed7e55504db..667444fdf55f 100644 --- a/doc/api/offsetbox_api.rst +++ b/doc/api/offsetbox_api.rst @@ -1,10 +1,9 @@ -********* -offsetbox -********* +************************ +``matplotlib.offsetbox`` +************************ - -:mod:`matplotlib.offsetbox` -=========================== +.. inheritance-diagram:: matplotlib.offsetbox + :parts: 1 .. automodule:: matplotlib.offsetbox :members: diff --git a/doc/api/patches_api.rst b/doc/api/patches_api.rst index fabd77545650..5b1eefa91971 100644 --- a/doc/api/patches_api.rst +++ b/doc/api/patches_api.rst @@ -1,12 +1,50 @@ -******* -patches -******* +********************** +``matplotlib.patches`` +********************** -:mod:`matplotlib.patches` -========================= +.. inheritance-diagram:: matplotlib.patches + :parts: 1 .. automodule:: matplotlib.patches - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-inherited-members: + +Classes +------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Annulus + Arc + Arrow + ArrowStyle + BoxStyle + Circle + CirclePolygon + ConnectionPatch + ConnectionStyle + Ellipse + FancyArrow + FancyArrowPatch + FancyBboxPatch + Patch + PathPatch + StepPatch + Polygon + Rectangle + RegularPolygon + Shadow + Wedge + +Functions +--------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + bbox_artist + draw_bbox diff --git a/doc/api/path_api.rst b/doc/api/path_api.rst index d67bbb2536e4..4ee7200b1921 100644 --- a/doc/api/path_api.rst +++ b/doc/api/path_api.rst @@ -1,10 +1,6 @@ -**** -path -**** - - -:mod:`matplotlib.path` -======================= +******************* +``matplotlib.path`` +******************* .. automodule:: matplotlib.path :members: diff --git a/doc/api/patheffects_api.rst b/doc/api/patheffects_api.rst index 0efcc14a114a..1f124b051d13 100644 --- a/doc/api/patheffects_api.rst +++ b/doc/api/patheffects_api.rst @@ -1,10 +1,6 @@ -*********** -patheffects -*********** - - -:mod:`matplotlib.patheffects` -============================= +************************** +``matplotlib.patheffects`` +************************** .. automodule:: matplotlib.patheffects :members: diff --git a/doc/api/prev_api_changes/api_changes_0.40.rst b/doc/api/prev_api_changes/api_changes_0.40.rst new file mode 100644 index 000000000000..83815ff43157 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.40.rst @@ -0,0 +1,67 @@ + +Changes for 0.40 +================ + +.. code-block:: text + + - Artist + * __init__ takes a DPI instance and a Bound2D instance which is + the bounding box of the artist in display coords + * get_window_extent returns a Bound2D instance + * set_size is removed; replaced by bbox and dpi + * the clip_gc method is removed. Artists now clip themselves with + their box + * added _clipOn boolean attribute. If True, gc clip to bbox. + + - AxisTextBase + * Initialized with a transx, transy which are Transform instances + * set_drawing_area removed + * get_left_right and get_top_bottom are replaced by get_window_extent + + - Line2D Patches now take transx, transy + * Initialized with a transx, transy which are Transform instances + + - Patches + * Initialized with a transx, transy which are Transform instances + + - FigureBase attributes dpi is a DPI instance rather than scalar and + new attribute bbox is a Bound2D in display coords, and I got rid + of the left, width, height, etc... attributes. These are now + accessible as, for example, bbox.x.min is left, bbox.x.interval() + is width, bbox.y.max is top, etc... + + - GcfBase attribute pagesize renamed to figsize + + - Axes + * removed figbg attribute + * added fig instance to __init__ + * resizing is handled by figure call to resize. + + - Subplot + * added fig instance to __init__ + + - Renderer methods for patches now take gcEdge and gcFace instances. + gcFace=None takes the place of filled=False + + - True and False symbols provided by cbook in a python2.3 compatible + way + + - new module transforms supplies Bound1D, Bound2D and Transform + instances and more + + - Changes to the MATLAB helpers API + + * _matlab_helpers.GcfBase is renamed by Gcf. Backends no longer + need to derive from this class. Instead, they provide a factory + function new_figure_manager(num, figsize, dpi). The destroy + method of the GcfDerived from the backends is moved to the derived + FigureManager. + + * FigureManagerBase moved to backend_bases + + * Gcf.get_all_figwins renamed to Gcf.get_all_fig_managers + + Jeremy: + + Make sure to self._reset = False in AxisTextWX._set_font. This was + something missing in my backend code. diff --git a/doc/api/prev_api_changes/api_changes_0.42.rst b/doc/api/prev_api_changes/api_changes_0.42.rst new file mode 100644 index 000000000000..e90d2af0ab2c --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.42.rst @@ -0,0 +1,37 @@ +Changes for 0.42 +================ + +.. code-block:: text + + * Refactoring AxisText to be backend independent. Text drawing and + get_window_extent functionality will be moved to the Renderer. + + * backend_bases.AxisTextBase is now text.Text module + + * All the erase and reset functionality removed from AxisText - not + needed with double buffered drawing. Ditto with state change. + Text instances have a get_prop_tup method that returns a hashable + tuple of text properties which you can use to see if text props + have changed, e.g., by caching a font or layout instance in a dict + with the prop tup as a key -- see RendererGTK.get_pango_layout in + backend_gtk for an example. + + * Text._get_xy_display renamed Text.get_xy_display + + * Artist set_renderer and wash_brushes methods removed + + * Moved Legend class from matplotlib.axes into matplotlib.legend + + * Moved Tick, XTick, YTick, Axis, XAxis, YAxis from matplotlib.axes + to matplotlib.axis + + * moved process_text_args to matplotlib.text + + * After getting Text handled in a backend independent fashion, the + import process is much cleaner since there are no longer cyclic + dependencies + + * matplotlib.matlab._get_current_fig_manager renamed to + matplotlib.matlab.get_current_fig_manager to allow user access to + the GUI window attribute, e.g., figManager.window for GTK and + figManager.frame for wx diff --git a/doc/api/prev_api_changes/api_changes_0.50.rst b/doc/api/prev_api_changes/api_changes_0.50.rst new file mode 100644 index 000000000000..bf1b608d2b63 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.50.rst @@ -0,0 +1,86 @@ + +Changes for 0.50 +================ + +.. code-block:: text + + * refactored Figure class so it is no longer backend dependent. + FigureCanvasBackend takes over the backend specific duties of the + Figure. matplotlib.backend_bases.FigureBase moved to + matplotlib.figure.Figure. + + * backends must implement FigureCanvasBackend (the thing that + controls the figure and handles the events if any) and + FigureManagerBackend (wraps the canvas and the window for MATLAB + interface). FigureCanvasBase implements a backend switching + mechanism + + * Figure is now an Artist (like everything else in the figure) and + is totally backend independent + + * GDFONTPATH renamed to TTFPATH + + * backend faceColor argument changed to rgbFace + + * colormap stuff moved to colors.py + + * arg_to_rgb in backend_bases moved to class ColorConverter in + colors.py + + * GD users must upgrade to gd-2.0.22 and gdmodule-0.52 since new gd + features (clipping, antialiased lines) are now used. + + * Renderer must implement points_to_pixels + + Migrating code: + + MATLAB interface: + + The only API change for those using the MATLAB interface is in how + you call figure redraws for dynamically updating figures. In the + old API, you did + + fig.draw() + + In the new API, you do + + manager = get_current_fig_manager() + manager.canvas.draw() + + See the examples system_monitor.py, dynamic_demo.py, and anim.py + + API + + There is one important API change for application developers. + Figure instances used subclass GUI widgets that enabled them to be + placed directly into figures. e.g., FigureGTK subclassed + gtk.DrawingArea. Now the Figure class is independent of the + backend, and FigureCanvas takes over the functionality formerly + handled by Figure. In order to include figures into your apps, + you now need to do, for example + + # gtk example + fig = Figure(figsize=(5,4), dpi=100) + canvas = FigureCanvasGTK(fig) # a gtk.DrawingArea + canvas.show() + vbox.pack_start(canvas) + + If you use the NavigationToolbar, this in now initialized with a + FigureCanvas, not a Figure. The examples embedding_in_gtk.py, + embedding_in_gtk2.py, and mpl_with_glade.py all reflect the new + API so use these as a guide. + + All prior calls to + + figure.draw() and + figure.print_figure(args) + + should now be + + canvas.draw() and + canvas.print_figure(args) + + Apologies for the inconvenience. This refactorization brings + significant more freedom in developing matplotlib and should bring + better plotting capabilities, so I hope the inconvenience is worth + it. diff --git a/doc/api/prev_api_changes/api_changes_0.54.3.rst b/doc/api/prev_api_changes/api_changes_0.54.3.rst new file mode 100644 index 000000000000..0747a0372927 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.54.3.rst @@ -0,0 +1,10 @@ + +Changes for 0.54.3 +================== + +.. code-block:: text + + removed the set_default_font / get_default_font scheme from the + font_manager to unify customization of font defaults with the rest of + the rc scheme. See examples/font_properties_demo.py and help(rc) in + matplotlib.matlab. diff --git a/doc/api/prev_api_changes/api_changes_0.54.rst b/doc/api/prev_api_changes/api_changes_0.54.rst new file mode 100644 index 000000000000..059c9322157f --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.54.rst @@ -0,0 +1,211 @@ + +Changes for 0.54 +================ + +MATLAB interface +---------------- + +dpi +~~~ + +Several of the backends used a PIXELS_PER_INCH hack that I added to +try and make images render consistently across backends. This just +complicated matters. So you may find that some font sizes and line +widths appear different than before. Apologies for the +inconvenience. You should set the dpi to an accurate value for your +screen to get true sizes. + + +pcolor and scatter +~~~~~~~~~~~~~~~~~~ + +There are two changes to the MATLAB interface API, both involving the +patch drawing commands. For efficiency, pcolor and scatter have been +rewritten to use polygon collections, which are a new set of objects +from matplotlib.collections designed to enable efficient handling of +large collections of objects. These new collections make it possible +to build large scatter plots or pcolor plots with no loops at the +python level, and are significantly faster than their predecessors. +The original pcolor and scatter functions are retained as +pcolor_classic and scatter_classic. + +The return value from pcolor is a PolyCollection. Most of the +properties that are available on rectangles or other patches are also +available on PolyCollections, e.g., you can say:: + + c = scatter(blah, blah) + c.set_linewidth(1.0) + c.set_facecolor('r') + c.set_alpha(0.5) + +or:: + + c = scatter(blah, blah) + set(c, 'linewidth', 1.0, 'facecolor', 'r', 'alpha', 0.5) + + +Because the collection is a single object, you no longer need to loop +over the return value of scatter or pcolor to set properties for the +entire list. + +If you want the different elements of a collection to vary on a +property, e.g., to have different line widths, see matplotlib.collections +for a discussion on how to set the properties as a sequence. + +For scatter, the size argument is now in points^2 (the area of the +symbol in points) as in MATLAB and is not in data coords as before. +Using sizes in data coords caused several problems. So you will need +to adjust your size arguments accordingly or use scatter_classic. + +mathtext spacing +~~~~~~~~~~~~~~~~ + +For reasons not clear to me (and which I'll eventually fix) spacing no +longer works in font groups. However, I added three new spacing +commands which compensate for this '\ ' (regular space), '\/' (small +space) and '\hspace{frac}' where frac is a fraction of fontsize in +points. You will need to quote spaces in font strings, is:: + + title(r'$\rm{Histogram\ of\ IQ:}\ \mu=100,\ \sigma=15$') + + + +Object interface - Application programmers +------------------------------------------ + +Autoscaling +~~~~~~~~~~~ + +The x and y axis instances no longer have autoscale view. These are +handled by axes.autoscale_view + +Axes creation +~~~~~~~~~~~~~ + +You should not instantiate your own Axes any more using the OO API. +Rather, create a Figure as before and in place of:: + + f = Figure(figsize=(5,4), dpi=100) + a = Subplot(f, 111) + f.add_axis(a) + +use:: + + f = Figure(figsize=(5,4), dpi=100) + a = f.add_subplot(111) + +That is, add_axis no longer exists and is replaced by:: + + add_axes(rect, axisbg=defaultcolor, frameon=True) + add_subplot(num, axisbg=defaultcolor, frameon=True) + +Artist methods +~~~~~~~~~~~~~~ + +If you define your own Artists, you need to rename the _draw method +to draw + +Bounding boxes +~~~~~~~~~~~~~~ + +matplotlib.transforms.Bound2D is replaced by +matplotlib.transforms.Bbox. If you want to construct a bbox from +left, bottom, width, height (the signature for Bound2D), use +matplotlib.transforms.lbwh_to_bbox, as in:: + + bbox = clickBBox = lbwh_to_bbox(left, bottom, width, height) + +The Bbox has a different API than the Bound2D. e.g., if you want to +get the width and height of the bbox + +**OLD**:: + + width = fig.bbox.x.interval() + height = fig.bbox.y.interval() + +**NEW**:: + + width = fig.bbox.width() + height = fig.bbox.height() + + +Object constructors +~~~~~~~~~~~~~~~~~~~ + +You no longer pass the bbox, dpi, or transforms to the various +Artist constructors. The old way or creating lines and rectangles +was cumbersome because you had to pass so many attributes to the +Line2D and Rectangle classes not related directly to the geometry +and properties of the object. Now default values are added to the +object when you call axes.add_line or axes.add_patch, so they are +hidden from the user. + +If you want to define a custom transformation on these objects, call +o.set_transform(trans) where trans is a Transformation instance. + +In prior versions of you wanted to add a custom line in data coords, +you would have to do:: + + l = Line2D(dpi, bbox, x, y, + color = color, + transx = transx, + transy = transy, + ) + +now all you need is:: + + l = Line2D(x, y, color=color) + +and the axes will set the transformation for you (unless you have +set your own already, in which case it will eave it unchanged) + +Transformations +~~~~~~~~~~~~~~~ + +The entire transformation architecture has been rewritten. +Previously the x and y transformations where stored in the xaxis and +yaxis instances. The problem with this approach is it only allows +for separable transforms (where the x and y transformations don't +depend on one another). But for cases like polar, they do. Now +transformations operate on x,y together. There is a new base class +matplotlib.transforms.Transformation and two concrete +implementations, matplotlib.transforms.SeparableTransformation and +matplotlib.transforms.Affine. The SeparableTransformation is +constructed with the bounding box of the input (this determines the +rectangular coordinate system of the input, i.e., the x and y view +limits), the bounding box of the display, and possibly nonlinear +transformations of x and y. The 2 most frequently used +transformations, data coordinates -> display and axes coordinates -> +display are available as ax.transData and ax.transAxes. See +alignment_demo.py which uses axes coords. + +Also, the transformations should be much faster now, for two reasons + +* they are written entirely in extension code + +* because they operate on x and y together, they can do the entire + transformation in one loop. Earlier I did something along the + lines of:: + + xt = sx*func(x) + tx + yt = sy*func(y) + ty + + Although this was done in numerix, it still involves 6 length(x) + for-loops (the multiply, add, and function evaluation each for x + and y). Now all of that is done in a single pass. + +If you are using transformations and bounding boxes to get the +cursor position in data coordinates, the method calls are a little +different now. See the updated examples/coords_demo.py which shows +you how to do this. + +Likewise, if you are using the artist bounding boxes to pick items +on the canvas with the GUI, the bbox methods are somewhat +different. You will need to see the updated +examples/object_picker.py. + +See unit/transforms_unit.py for many examples using the new +transformations. + + +.. highlight:: none diff --git a/doc/api/prev_api_changes/api_changes_0.60.rst b/doc/api/prev_api_changes/api_changes_0.60.rst new file mode 100644 index 000000000000..d27c5ae1848b --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.60.rst @@ -0,0 +1,15 @@ +Changes for 0.60 +================ + +.. code-block:: text + + ColormapJet and Grayscale are deprecated. For backwards + compatibility, they can be obtained either by doing + + from matplotlib.cm import ColormapJet + + or + + from matplotlib.matlab import * + + They are replaced by cm.jet and cm.grey diff --git a/doc/api/prev_api_changes/api_changes_0.61.rst b/doc/api/prev_api_changes/api_changes_0.61.rst new file mode 100644 index 000000000000..570c8d1d22e4 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.61.rst @@ -0,0 +1,8 @@ +Changes for 0.61 +================ + +.. code-block:: text + + canvas.connect is now deprecated for event handling. use + mpl_connect and mpl_disconnect instead. The callback signature is + func(event) rather than func(widget, event) diff --git a/doc/api/prev_api_changes/api_changes_0.63.rst b/doc/api/prev_api_changes/api_changes_0.63.rst new file mode 100644 index 000000000000..bb896ad55207 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.63.rst @@ -0,0 +1,43 @@ +Changes for 0.63 +================ + +.. code-block:: text + + Dates are now represented internally as float days since 0001-01-01, + UTC. + + All date tickers and formatters are now in matplotlib.dates, rather + than matplotlib.tickers + + converters have been abolished from all functions and classes. + num2date and date2num are now the converter functions for all date + plots + + Most of the date tick locators have a different meaning in their + constructors. In the prior implementation, the first argument was a + base and multiples of the base were ticked. e.g., + + HourLocator(5) # old: tick every 5 minutes + + In the new implementation, the explicit points you want to tick are + provided as a number or sequence + + HourLocator(range(0,5,61)) # new: tick every 5 minutes + + This gives much greater flexibility. I have tried to make the + default constructors (no args) behave similarly, where possible. + + Note that YearLocator still works under the base/multiple scheme. + The difference between the YearLocator and the other locators is + that years are not recurrent. + + + Financial functions: + + matplotlib.finance.quotes_historical_yahoo(ticker, date1, date2) + + date1, date2 are now datetime instances. Return value is a list + of quotes where the quote time is a float - days since gregorian + start, as returned by date2num + + See examples/finance_demo.py for example usage of new API diff --git a/doc/api/prev_api_changes/api_changes_0.65.1.rst b/doc/api/prev_api_changes/api_changes_0.65.1.rst new file mode 100644 index 000000000000..fb75baaa6acb --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.65.1.rst @@ -0,0 +1,10 @@ +Changes for 0.65.1 +================== + +.. code-block:: text + + removed add_axes and add_subplot from backend_bases. Use + figure.add_axes and add_subplot instead. The figure now manages the + current axes with gca and sca for get and set current axes. If you + have code you are porting which called, e.g., figmanager.add_axes, you + can now simply do figmanager.canvas.figure.add_axes. diff --git a/doc/api/prev_api_changes/api_changes_0.65.rst b/doc/api/prev_api_changes/api_changes_0.65.rst new file mode 100644 index 000000000000..f9b9af732010 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.65.rst @@ -0,0 +1,12 @@ +Changes for 0.65 +================ + +.. code-block:: text + + + mpl_connect and mpl_disconnect in the MATLAB interface renamed to + connect and disconnect + + Did away with the text methods for angle since they were ambiguous. + fontangle could mean fontstyle (oblique, etc) or the rotation of the + text. Use style and rotation instead. diff --git a/doc/api/prev_api_changes/api_changes_0.70.rst b/doc/api/prev_api_changes/api_changes_0.70.rst new file mode 100644 index 000000000000..e30dfbb64954 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.70.rst @@ -0,0 +1,9 @@ +Changes for 0.70 +================ + +.. code-block:: text + + MplEvent factored into a base class Event and derived classes + MouseEvent and KeyEvent + + Removed defunct set_measurement in wx toolbar diff --git a/doc/api/prev_api_changes/api_changes_0.71.rst b/doc/api/prev_api_changes/api_changes_0.71.rst new file mode 100644 index 000000000000..d10a7439e672 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.71.rst @@ -0,0 +1,41 @@ +Changes for 0.71 +================ + +.. code-block:: text + + Significant numerix namespace changes, introduced to resolve + namespace clashes between python built-ins and mlab names. + Refactored numerix to maintain separate modules, rather than + folding all these names into a single namespace. See the following + mailing list threads for more information and background + + http://sourceforge.net/mailarchive/forum.php?thread_id=6398890&forum_id=36187 + http://sourceforge.net/mailarchive/forum.php?thread_id=6323208&forum_id=36187 + + + OLD usage + + from matplotlib.numerix import array, mean, fft + + NEW usage + + from matplotlib.numerix import array + from matplotlib.numerix.mlab import mean + from matplotlib.numerix.fft import fft + + numerix dir structure mirrors numarray (though it is an incomplete + implementation) + + numerix + numerix/mlab + numerix/linear_algebra + numerix/fft + numerix/random_array + + but of course you can use 'numerix : Numeric' and still get the + symbols. + + pylab still imports most of the symbols from Numerix, MLab, fft, + etc, but is more cautious. For names that clash with python names + (min, max, sum), pylab keeps the builtins and provides the numeric + versions with an a* prefix, e.g., (amin, amax, asum) diff --git a/doc/api/prev_api_changes/api_changes_0.72.rst b/doc/api/prev_api_changes/api_changes_0.72.rst new file mode 100644 index 000000000000..bfb6fc124658 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.72.rst @@ -0,0 +1,33 @@ +Changes for 0.72 +================ + +.. code-block:: text + + - Line2D, Text, and Patch copy_properties renamed update_from and + moved into artist base class + + - LineCollections.color renamed to LineCollections.set_color for + consistency with set/get introspection mechanism, + + - pylab figure now defaults to num=None, which creates a new figure + with a guaranteed unique number + + - contour method syntax changed - now it is MATLAB compatible + + unchanged: contour(Z) + old: contour(Z, x=Y, y=Y) + new: contour(X, Y, Z) + + see http://matplotlib.sf.net/matplotlib.pylab.html#-contour + + + - Increased the default resolution for save command. + + - Renamed the base attribute of the ticker classes to _base to avoid conflict + with the base method. Sitt for subs + + - subs=none now does autosubbing in the tick locator. + + - New subplots that overlap old will delete the old axes. If you + do not want this behavior, use fig.add_subplot or the axes + command diff --git a/doc/api/prev_api_changes/api_changes_0.73.rst b/doc/api/prev_api_changes/api_changes_0.73.rst new file mode 100644 index 000000000000..ec7c4e34c6ef --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.73.rst @@ -0,0 +1,10 @@ +Changes for 0.73 +================ + +.. code-block:: text + + - Removed deprecated ColormapJet and friends + + - Removed all error handling from the verbose object + + - figure num of zero is now allowed diff --git a/doc/api/prev_api_changes/api_changes_0.80.rst b/doc/api/prev_api_changes/api_changes_0.80.rst new file mode 100644 index 000000000000..1c118fd21aca --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.80.rst @@ -0,0 +1,9 @@ +Changes for 0.80 +================ + +.. code-block:: text + + - xlim/ylim/axis always return the new limits regardless of + arguments. They now take kwargs which allow you to selectively + change the upper or lower limits while leaving unnamed limits + unchanged. See help(xlim) for example diff --git a/doc/api/prev_api_changes/api_changes_0.81.rst b/doc/api/prev_api_changes/api_changes_0.81.rst new file mode 100644 index 000000000000..f571d5dbae2c --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.81.rst @@ -0,0 +1,49 @@ +Changes for 0.81 +================ + +.. code-block:: text + + - pylab and artist "set" functions renamed to setp to avoid clash + with python2.4 built-in set. Current version will issue a + deprecation warning which will be removed in future versions + + - imshow interpolation arguments changes for advanced interpolation + schemes. See help imshow, particularly the interpolation, + filternorm and filterrad kwargs + + - Support for masked arrays has been added to the plot command and + to the Line2D object. Only the valid points are plotted. A + "valid_only" kwarg was added to the get_xdata() and get_ydata() + methods of Line2D; by default it is False, so that the original + data arrays are returned. Setting it to True returns the plottable + points. + + - contour changes: + + Masked arrays: contour and contourf now accept masked arrays as + the variable to be contoured. Masking works correctly for + contour, but a bug remains to be fixed before it will work for + contourf. The "badmask" kwarg has been removed from both + functions. + + Level argument changes: + + Old version: a list of levels as one of the positional + arguments specified the lower bound of each filled region; the + upper bound of the last region was taken as a very large + number. Hence, it was not possible to specify that z values + between 0 and 1, for example, be filled, and that values + outside that range remain unfilled. + + New version: a list of N levels is taken as specifying the + boundaries of N-1 z ranges. Now the user has more control over + what is colored and what is not. Repeated calls to contourf + (with different colormaps or color specifications, for example) + can be used to color different ranges of z. Values of z + outside an expected range are left uncolored. + + Example: + Old: contourf(z, [0, 1, 2]) would yield 3 regions: 0-1, 1-2, and >2. + New: it would yield 2 regions: 0-1, 1-2. If the same 3 regions were + desired, the equivalent list of levels would be [0, 1, 2, + 1e38]. diff --git a/doc/api/prev_api_changes/api_changes_0.82.rst b/doc/api/prev_api_changes/api_changes_0.82.rst new file mode 100644 index 000000000000..31a90fca52d4 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.82.rst @@ -0,0 +1,52 @@ +Changes for 0.82 +================ + +.. code-block:: text + + - toolbar import change in GTKAgg, GTKCairo and WXAgg + + - Added subplot config tool to GTK* backends -- note you must now + import the NavigationToolbar2 from your backend of choice rather + than from backend_gtk because it needs to know about the backend + specific canvas -- see examples/embedding_in_gtk2.py. Ditto for + wx backend -- see examples/embedding_in_wxagg.py + + + - hist bin change + + Sean Richards notes there was a problem in the way we created + the binning for histogram, which made the last bin + underrepresented. From his post: + + I see that hist uses the linspace function to create the bins + and then uses searchsorted to put the values in their correct + bin. That's all good but I am confused over the use of linspace + for the bin creation. I wouldn't have thought that it does + what is needed, to quote the docstring it creates a "Linear + spaced array from min to max". For it to work correctly + shouldn't the values in the bins array be the same bound for + each bin? (i.e. each value should be the lower bound of a + bin). To provide the correct bins for hist would it not be + something like + + def bins(xmin, xmax, N): + if N==1: return xmax + dx = (xmax-xmin)/N # instead of N-1 + return xmin + dx*arange(N) + + + This suggestion is implemented in 0.81. My test script with these + changes does not reveal any bias in the binning + + from matplotlib.numerix.mlab import randn, rand, zeros, Float + from matplotlib.mlab import hist, mean + + Nbins = 50 + Ntests = 200 + results = zeros((Ntests,Nbins), typecode=Float) + for i in range(Ntests): + print 'computing', i + x = rand(10000) + n, bins = hist(x, Nbins) + results[i] = n + print mean(results) diff --git a/doc/api/prev_api_changes/api_changes_0.83.rst b/doc/api/prev_api_changes/api_changes_0.83.rst new file mode 100644 index 000000000000..267951c18aee --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.83.rst @@ -0,0 +1,24 @@ +Changes for 0.83 +================ + +.. code-block:: text + + - Made HOME/.matplotlib the new config dir where the matplotlibrc + file, the ttf.cache, and the tex.cache live. The new default + filenames in .matplotlib have no leading dot and are not hidden. + e.g., the new names are matplotlibrc, tex.cache, and ttffont.cache. + This is how ipython does it so it must be right. + + If old files are found, a warning is issued and they are moved to + the new location. + + - backends/__init__.py no longer imports new_figure_manager, + draw_if_interactive and show from the default backend, but puts + these imports into a call to pylab_setup. Also, the Toolbar is no + longer imported from WX/WXAgg. New usage: + + from backends import pylab_setup + new_figure_manager, draw_if_interactive, show = pylab_setup() + + - Moved Figure.get_width_height() to FigureCanvasBase. It now + returns int instead of float. diff --git a/doc/api/prev_api_changes/api_changes_0.84.rst b/doc/api/prev_api_changes/api_changes_0.84.rst new file mode 100644 index 000000000000..7dabe214e3cc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.84.rst @@ -0,0 +1,18 @@ +Changes for 0.84 +================ + +.. code-block:: text + + Unified argument handling between hlines and vlines. Both now + take optionally a fmt argument (as in plot) and a keyword args + that can be passed onto Line2D. + + Removed all references to "data clipping" in rc and lines.py since + these were not used and not optimized. I'm sure they'll be + resurrected later with a better implementation when needed. + + 'set' removed - no more deprecation warnings. Use 'setp' instead. + + Backend developers: Added flipud method to image and removed it + from to_str. Removed origin kwarg from backend.draw_image. + origin is handled entirely by the frontend now. diff --git a/doc/api/prev_api_changes/api_changes_0.85.rst b/doc/api/prev_api_changes/api_changes_0.85.rst new file mode 100644 index 000000000000..29f646e0e9d8 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.85.rst @@ -0,0 +1,43 @@ +Changes for 0.85 +================ + +.. code-block:: text + + Made xtick and ytick separate props in rc + + made pos=None the default for tick formatters rather than 0 to + indicate "not supplied" + + Removed "feature" of minor ticks which prevents them from + overlapping major ticks. Often you want major and minor ticks at + the same place, and can offset the major ticks with the pad. This + could be made configurable + + Changed the internal structure of contour.py to a more OO style. + Calls to contour or contourf in axes.py or pylab.py now return + a ContourSet object which contains references to the + LineCollections or PolyCollections created by the call, + as well as the configuration variables that were used. + The ContourSet object is a "mappable" if a colormap was used. + + Added a clip_ends kwarg to contourf. From the docstring: + * clip_ends = True + If False, the limits for color scaling are set to the + minimum and maximum contour levels. + True (default) clips the scaling limits. Example: + if the contour boundaries are V = [-100, 2, 1, 0, 1, 2, 100], + then the scaling limits will be [-100, 100] if clip_ends + is False, and [-3, 3] if clip_ends is True. + Added kwargs linewidths, antialiased, and nchunk to contourf. These + are experimental; see the docstring. + + Changed Figure.colorbar(): + kw argument order changed; + if mappable arg is a non-filled ContourSet, colorbar() shows + lines instead hof polygons. + if mappable arg is a filled ContourSet with clip_ends=True, + the endpoints are not labelled, so as to give the + correct impression of open-endedness. + + Changed LineCollection.get_linewidths to get_linewidth, for + consistency. diff --git a/doc/api/prev_api_changes/api_changes_0.86.rst b/doc/api/prev_api_changes/api_changes_0.86.rst new file mode 100644 index 000000000000..5e0c813347b2 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.86.rst @@ -0,0 +1,28 @@ +Changes for 0.86 +================ + +.. code-block:: text + + Matplotlib data is installed into the matplotlib module. + This is similar to package_data. This should get rid of + having to check for many possibilities in _get_data_path(). + The MATPLOTLIBDATA env key is still checked first to allow + for flexibility. + + 1) Separated the color table data from cm.py out into + a new file, _cm.py, to make it easier to find the actual + code in cm.py and to add new colormaps. Everything + from _cm.py is imported by cm.py, so the split should be + transparent. + 2) Enabled automatic generation of a colormap from + a list of colors in contour; see modified + examples/contour_demo.py. + 3) Support for imshow of a masked array, with the + ability to specify colors (or no color at all) for + masked regions, and for regions that are above or + below the normally mapped region. See + examples/image_masked.py. + 4) In support of the above, added two new classes, + ListedColormap, and no_norm, to colors.py, and modified + the Colormap class to include common functionality. Added + a clip kwarg to the normalize class. diff --git a/doc/api/prev_api_changes/api_changes_0.87.7.rst b/doc/api/prev_api_changes/api_changes_0.87.7.rst new file mode 100644 index 000000000000..c3538c6f7432 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.87.7.rst @@ -0,0 +1,89 @@ + + +Changes for 0.87.7 +================== + +.. code-block:: text + + Completely reworked the annotations API because I found the old + API cumbersome. The new design is much more legible and easy to + read. See matplotlib.text.Annotation and + examples/annotation_demo.py + + markeredgecolor and markerfacecolor cannot be configured in + matplotlibrc any more. Instead, markers are generally colored + automatically based on the color of the line, unless marker colors + are explicitly set as kwargs - NN + + Changed default comment character for load to '#' - JDH + + math_parse_s_ft2font_svg from mathtext.py & mathtext2.py now returns + width, height, svg_elements. svg_elements is an instance of Bunch ( + cmbook.py) and has the attributes svg_glyphs and svg_lines, which are both + lists. + + Renderer.draw_arc now takes an additional parameter, rotation. + It specifies to draw the artist rotated in degrees anti- + clockwise. It was added for rotated ellipses. + + Renamed Figure.set_figsize_inches to Figure.set_size_inches to + better match the get method, Figure.get_size_inches. + + Removed the copy_bbox_transform from transforms.py; added + shallowcopy methods to all transforms. All transforms already + had deepcopy methods. + + FigureManager.resize(width, height): resize the window + specified in pixels + + barh: x and y args have been renamed to width and bottom + respectively, and their order has been swapped to maintain + a (position, value) order. + + bar and barh: now accept kwarg 'edgecolor'. + + bar and barh: The left, height, width and bottom args can + now all be scalars or sequences; see docstring. + + barh: now defaults to edge aligned instead of center + aligned bars + + bar, barh and hist: Added a keyword arg 'align' that + controls between edge or center bar alignment. + + Collections: PolyCollection and LineCollection now accept + vertices or segments either in the original form [(x,y), + (x,y), ...] or as a 2D numerix array, with X as the first column + and Y as the second. Contour and quiver output the numerix + form. The transforms methods Bbox.update() and + Transformation.seq_xy_tups() now accept either form. + + Collections: LineCollection is now a ScalarMappable like + PolyCollection, etc. + + Specifying a grayscale color as a float is deprecated; use + a string instead, e.g., 0.75 -> '0.75'. + + Collections: initializers now accept any mpl color arg, or + sequence of such args; previously only a sequence of rgba + tuples was accepted. + + Colorbar: completely new version and api; see docstring. The + original version is still accessible as colorbar_classic, but + is deprecated. + + Contourf: "extend" kwarg replaces "clip_ends"; see docstring. + Masked array support added to pcolormesh. + + Modified aspect-ratio handling: + Removed aspect kwarg from imshow + Axes methods: + set_aspect(self, aspect, adjustable=None, anchor=None) + set_adjustable(self, adjustable) + set_anchor(self, anchor) + Pylab interface: + axis('image') + + Backend developers: ft2font's load_char now takes a flags + argument, which you can OR together from the LOAD_XXX + constants. diff --git a/doc/api/prev_api_changes/api_changes_0.90.0.rst b/doc/api/prev_api_changes/api_changes_0.90.0.rst new file mode 100644 index 000000000000..7bbdfc06c760 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.90.0.rst @@ -0,0 +1,40 @@ +Changes for 0.90.0 +================== + +.. code-block:: text + + All artists now implement a "pick" method which users should not + call. Rather, set the "picker" property of any artist you want to + pick on (the epsilon distance in points for a hit test) and + register with the "pick_event" callback. See + examples/pick_event_demo.py for details + + Bar, barh, and hist have "log" binary kwarg: log=True + sets the ordinate to a log scale. + + Boxplot can handle a list of vectors instead of just + an array, so vectors can have different lengths. + + Plot can handle 2-D x and/or y; it plots the columns. + + Added linewidth kwarg to bar and barh. + + Made the default Artist._transform None (rather than invoking + identity_transform for each artist only to have it overridden + later). Use artist.get_transform() rather than artist._transform, + even in derived classes, so that the default transform will be + created lazily as needed + + New LogNorm subclass of Normalize added to colors.py. + All Normalize subclasses have new inverse() method, and + the __call__() method has a new clip kwarg. + + Changed class names in colors.py to match convention: + normalize -> Normalize, no_norm -> NoNorm. Old names + are still available for now. + + Removed obsolete pcolor_classic command and method. + + Removed lineprops and markerprops from the Annotation code and + replaced them with an arrow configurable with kwarg arrowprops. + See examples/annotation_demo.py - JDH diff --git a/doc/api/prev_api_changes/api_changes_0.90.1.rst b/doc/api/prev_api_changes/api_changes_0.90.1.rst new file mode 100644 index 000000000000..8caef5e35ced --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.90.1.rst @@ -0,0 +1,65 @@ + +Changes for 0.90.1 +================== + +.. code-block:: text + + The file dviread.py has a (very limited and fragile) dvi reader + for usetex support. The API might change in the future so don't + depend on it yet. + + Removed deprecated support for a float value as a gray-scale; + now it must be a string, like '0.5'. Added alpha kwarg to + ColorConverter.to_rgba_list. + + New method set_bounds(vmin, vmax) for formatters, locators sets + the viewInterval and dataInterval from floats. + + Removed deprecated colorbar_classic. + + Line2D.get_xdata and get_ydata valid_only=False kwarg is replaced + by orig=True. When True, it returns the original data, otherwise + the processed data (masked, converted) + + Some modifications to the units interface. + units.ConversionInterface.tickers renamed to + units.ConversionInterface.axisinfo and it now returns a + units.AxisInfo object rather than a tuple. This will make it + easier to add axis info functionality (e.g., I added a default label + on this iteration) w/o having to change the tuple length and hence + the API of the client code every time new functionality is added. + Also, units.ConversionInterface.convert_to_value is now simply + named units.ConversionInterface.convert. + + Axes.errorbar uses Axes.vlines and Axes.hlines to draw its error + limits in the vertical and horizontal direction. As you'll see + in the changes below, these functions now return a LineCollection + rather than a list of lines. The new return signature for + errorbar is ylins, caplines, errorcollections where + errorcollections is a xerrcollection, yerrcollection + + Axes.vlines and Axes.hlines now create and returns a LineCollection, not a list + of lines. This is much faster. The kwarg signature has changed, + so consult the docs + + MaxNLocator accepts a new Boolean kwarg ('integer') to force + ticks to integer locations. + + Commands that pass an argument to the Text constructor or to + Text.set_text() now accept any object that can be converted + with '%s'. This affects xlabel(), title(), etc. + + Barh now takes a **kwargs dict instead of most of the old + arguments. This helps ensure that bar and barh are kept in sync, + but as a side effect you can no longer pass e.g., color as a + positional argument. + + ft2font.get_charmap() now returns a dict that maps character codes + to glyph indices (until now it was reversed) + + Moved data files into lib/matplotlib so that setuptools' develop + mode works. Re-organized the mpl-data layout so that this source + structure is maintained in the installation. (i.e., the 'fonts' and + 'images' sub-directories are maintained in site-packages.). + Suggest removing site-packages/matplotlib/mpl-data and + ~/.matplotlib/ttffont.cache before installing diff --git a/doc/api/prev_api_changes/api_changes_0.91.0.rst b/doc/api/prev_api_changes/api_changes_0.91.0.rst new file mode 100644 index 000000000000..32760554522a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.91.0.rst @@ -0,0 +1,69 @@ + +Changes for 0.91.0 +================== + +* Changed ``cbook.is_file_like`` to ``cbook.is_writable_file_like`` and + corrected behavior. + +* Added *ax* keyword argument to :func:`.pyplot.colorbar` and + :meth:`.Figure.colorbar` so that one can specify the axes object from + which space for the colorbar is to be taken, if one does not want to + make the colorbar axes manually. + +* Changed ``cbook.reversed`` so it yields a tuple rather than a (index, tuple). + This agrees with the Python reversed builtin, and cbook only defines reversed + if Python doesn't provide the builtin. + +* Made skiprows=1 the default on ``csv2rec`` + +* The gd and paint backends have been deleted. + +* The errorbar method and function now accept additional kwargs + so that upper and lower limits can be indicated by capping the + bar with a caret instead of a straight line segment. + +* The :mod:`matplotlib.dviread` file now has a parser for files like + psfonts.map and pdftex.map, to map TeX font names to external files. + +* The file ``matplotlib.type1font`` contains a new class for Type 1 + fonts. Currently it simply reads pfa and pfb format files and + stores the data in a way that is suitable for embedding in pdf + files. In the future the class might actually parse the font to + allow e.g., subsetting. + +* ``matplotlib.ft2font`` now supports ``FT_Attach_File``. In + practice this can be used to read an afm file in addition to a + pfa/pfb file, to get metrics and kerning information for a Type 1 + font. + +* The ``AFM`` class now supports querying CapHeight and stem + widths. The get_name_char method now has an isord kwarg like + get_width_char. + +* Changed :func:`.pcolor` default to ``shading='flat'``; but as noted now in + the docstring, it is preferable to simply use the *edgecolor* keyword + argument. + +* The mathtext font commands (``\cal``, ``\rm``, ``\it``, ``\tt``) now + behave as TeX does: they are in effect until the next font change + command or the end of the grouping. Therefore uses of ``$\cal{R}$`` + should be changed to ``${\cal R}$``. Alternatively, you may use the + new LaTeX-style font commands (``\mathcal``, ``\mathrm``, + ``\mathit``, ``\mathtt``) which do affect the following group, + e.g., ``$\mathcal{R}$``. + +* Text creation commands have a new default linespacing and a new + ``linespacing`` kwarg, which is a multiple of the maximum vertical + extent of a line of ordinary text. The default is 1.2; + ``linespacing=2`` would be like ordinary double spacing, for example. + +* Changed default kwarg in `matplotlib.colors.Normalize` to ``clip=False``; + clipping silently defeats the purpose of the special over, under, + and bad values in the colormap, thereby leading to unexpected + behavior. The new default should reduce such surprises. + +* Made the emit property of :meth:`~matplotlib.axes.Axes.set_xlim` and + :meth:`~matplotlib.axes.Axes.set_ylim` ``True`` by default; removed + the Axes custom callback handling into a 'callbacks' attribute which + is a :class:`~matplotlib.cbook.CallbackRegistry` instance. This now + supports the 'xlim_changed' and 'ylim_changed' Axes events. diff --git a/doc/api/prev_api_changes/api_changes_0.91.2.rst b/doc/api/prev_api_changes/api_changes_0.91.2.rst new file mode 100644 index 000000000000..5abf2ec3be77 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.91.2.rst @@ -0,0 +1,16 @@ + +Changes for 0.91.2 +================== + +* For ``csv2rec``, checkrows=0 is the new default indicating all rows + will be checked for type inference + +* A warning is issued when an image is drawn on log-scaled axes, since + it will not log-scale the image data. + +* Moved ``rec2gtk`` to ``matplotlib.toolkits.gtktools`` + +* Moved ``rec2excel`` to ``matplotlib.toolkits.exceltools`` + +* Removed, dead/experimental ExampleInfo, Namespace and Importer + code from :mod:`matplotlib` diff --git a/doc/api/prev_api_changes/api_changes_0.98.0.rst b/doc/api/prev_api_changes/api_changes_0.98.0.rst new file mode 100644 index 000000000000..7a1ebf56fcde --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.98.0.rst @@ -0,0 +1,314 @@ + + +Changes for 0.98.0 +================== + +* :func:`matplotlib.image.imread` now no longer always returns RGBA data---if + the image is luminance or RGB, it will return a MxN or MxNx3 array + if possible. Also uint8 is no longer always forced to float. + +* Rewrote the :class:`matplotlib.cm.ScalarMappable` callback + infrastructure to use :class:`matplotlib.cbook.CallbackRegistry` + rather than custom callback handling. Any users of + ``matplotlib.cm.ScalarMappable.add_observer`` of the + :class:`~matplotlib.cm.ScalarMappable` should use the + ``matplotlib.cm.ScalarMappable.callbacksSM`` + :class:`~matplotlib.cbook.CallbackRegistry` instead. + +* New axes function and Axes method provide control over the plot + color cycle: ``matplotlib.axes.set_default_color_cycle`` and + ``matplotlib.axes.Axes.set_color_cycle``. + +* Matplotlib now requires Python 2.4, so :mod:`matplotlib.cbook` will + no longer provide :class:`set`, :func:`enumerate`, :func:`reversed` + or ``izip`` compatibility functions. + +* In Numpy 1.0, bins are specified by the left edges only. The axes + method :meth:`matplotlib.axes.Axes.hist` now uses future Numpy 1.3 + semantics for histograms. Providing ``binedges``, the last value gives + the upper-right edge now, which was implicitly set to +infinity in + Numpy 1.0. This also means that the last bin doesn't contain upper + outliers any more by default. + +* New axes method and pyplot function, + :func:`~matplotlib.pyplot.hexbin`, is an alternative to + :func:`~matplotlib.pyplot.scatter` for large datasets. It makes + something like a :func:`~matplotlib.pyplot.pcolor` of a 2-D + histogram, but uses hexagonal bins. + +* New kwarg, ``symmetric``, in :class:`matplotlib.ticker.MaxNLocator` + allows one require an axis to be centered around zero. + +* Toolkits must now be imported from ``mpl_toolkits`` (not ``matplotlib.toolkits``) + +Notes about the transforms refactoring +-------------------------------------- + +A major new feature of the 0.98 series is a more flexible and +extensible transformation infrastructure, written in Python/Numpy +rather than a custom C extension. + +The primary goal of this refactoring was to make it easier to +extend matplotlib to support new kinds of projections. This is +mostly an internal improvement, and the possible user-visible +changes it allows are yet to come. + +See :mod:`matplotlib.transforms` for a description of the design of +the new transformation framework. + +For efficiency, many of these functions return views into Numpy +arrays. This means that if you hold on to a reference to them, +their contents may change. If you want to store a snapshot of +their current values, use the Numpy array method copy(). + +The view intervals are now stored only in one place -- in the +:class:`matplotlib.axes.Axes` instance, not in the locator instances +as well. This means locators must get their limits from their +:class:`matplotlib.axis.Axis`, which in turn looks up its limits from +the :class:`~matplotlib.axes.Axes`. If a locator is used temporarily +and not assigned to an Axis or Axes, (e.g., in +:mod:`matplotlib.contour`), a dummy axis must be created to store its +bounds. Call :meth:`matplotlib.ticker.TickHelper.create_dummy_axis` to +do so. + +The functionality of ``Pbox`` has been merged with +:class:`~matplotlib.transforms.Bbox`. Its methods now all return +copies rather than modifying in place. + +The following lists many of the simple changes necessary to update +code from the old transformation framework to the new one. In +particular, methods that return a copy are named with a verb in the +past tense, whereas methods that alter an object in place are named +with a verb in the present tense. + +:mod:`matplotlib.transforms` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------------------------+------------------------------------------------------+ +| Old method | New method | ++============================================+======================================================+ +| ``Bbox.get_bounds`` | :attr:`.transforms.Bbox.bounds` | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.width`` | :attr:`transforms.Bbox.width | +| | <.transforms.BboxBase.width>` | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.height`` | :attr:`transforms.Bbox.height | +| | <.transforms.BboxBase.height>` | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.intervalx().get_bounds()`` | :attr:`.transforms.Bbox.intervalx` | +| ``Bbox.intervalx().set_bounds()`` | [It is now a property.] | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.intervaly().get_bounds()`` | :attr:`.transforms.Bbox.intervaly` | +| ``Bbox.intervaly().set_bounds()`` | [It is now a property.] | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.xmin`` | :attr:`.transforms.Bbox.x0` or | +| | :attr:`transforms.Bbox.xmin | +| | <.transforms.BboxBase.xmin>` [1]_ | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.ymin`` | :attr:`.transforms.Bbox.y0` or | +| | :attr:`transforms.Bbox.ymin | +| | <.transforms.BboxBase.ymin>` [1]_ | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.xmax`` | :attr:`.transforms.Bbox.x1` or | +| | :attr:`transforms.Bbox.xmax | +| | <.transforms.BboxBase.xmax>` [1]_ | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.ymax`` | :attr:`.transforms.Bbox.y1` or | +| | :attr:`transforms.Bbox.ymax | +| | <.transforms.BboxBase.ymax>` [1]_ | ++--------------------------------------------+------------------------------------------------------+ +| ``Bbox.overlaps(bboxes)`` | `Bbox.count_overlaps(bboxes) | +| | <.BboxBase.count_overlaps>` | ++--------------------------------------------+------------------------------------------------------+ +| ``bbox_all(bboxes)`` | `Bbox.union(bboxes) <.BboxBase.union>` | +| | [It is a staticmethod.] | ++--------------------------------------------+------------------------------------------------------+ +| ``lbwh_to_bbox(l, b, w, h)`` | `Bbox.from_bounds(x0, y0, w, h) <.Bbox.from_bounds>` | +| | [It is a staticmethod.] | ++--------------------------------------------+------------------------------------------------------+ +| ``inverse_transform_bbox(trans, bbox)`` | ``bbox.inverse_transformed(trans)`` | +| | | ++--------------------------------------------+------------------------------------------------------+ +| ``Interval.contains_open(v)`` | `interval_contains_open(tuple, v) | +| | <.interval_contains_open>` | ++--------------------------------------------+------------------------------------------------------+ +| ``Interval.contains(v)`` | `interval_contains(tuple, v) <.interval_contains>` | ++--------------------------------------------+------------------------------------------------------+ +| ``identity_transform()`` | :class:`.transforms.IdentityTransform` | ++--------------------------------------------+------------------------------------------------------+ +| ``blend_xy_sep_transform(xtrans, ytrans)`` | `blended_transform_factory(xtrans, ytrans) | +| | <.blended_transform_factory>` | ++--------------------------------------------+------------------------------------------------------+ +| ``scale_transform(xs, ys)`` | `Affine2D().scale(xs[, ys]) <.Affine2D.scale>` | ++--------------------------------------------+------------------------------------------------------+ +| ``get_bbox_transform(boxin, boxout)`` | `BboxTransform(boxin, boxout) <.BboxTransform>` or | +| | `BboxTransformFrom(boxin) <.BboxTransformFrom>` or | +| | `BboxTransformTo(boxout) <.BboxTransformTo>` | ++--------------------------------------------+------------------------------------------------------+ +| ``Transform.seq_xy_tup(points)`` | `Transform.transform(points) <.Transform.transform>` | ++--------------------------------------------+------------------------------------------------------+ +| ``Transform.inverse_xy_tup(points)`` | `Transform.inverted() | +| | <.Transform.inverted>`.transform(points) | ++--------------------------------------------+------------------------------------------------------+ + +.. [1] The :class:`~matplotlib.transforms.Bbox` is bound by the points + (x0, y0) to (x1, y1) and there is no defined order to these points, + that is, x0 is not necessarily the left edge of the box. To get + the left edge of the :class:`.Bbox`, use the read-only property + :attr:`xmin `. + +:mod:`matplotlib.axes` +~~~~~~~~~~~~~~~~~~~~~~ + +============================= ============================================== +Old method New method +============================= ============================================== +``Axes.get_position()`` :meth:`matplotlib.axes.Axes.get_position` [2]_ +----------------------------- ---------------------------------------------- +``Axes.set_position()`` :meth:`matplotlib.axes.Axes.set_position` [3]_ +----------------------------- ---------------------------------------------- +``Axes.toggle_log_lineary()`` :meth:`matplotlib.axes.Axes.set_yscale` [4]_ +----------------------------- ---------------------------------------------- +``Subplot`` class removed +============================= ============================================== + +The ``Polar`` class has moved to :mod:`matplotlib.projections.polar`. + +.. [2] :meth:`matplotlib.axes.Axes.get_position` used to return a list + of points, now it returns a :class:`matplotlib.transforms.Bbox` + instance. + +.. [3] :meth:`matplotlib.axes.Axes.set_position` now accepts either + four scalars or a :class:`matplotlib.transforms.Bbox` instance. + +.. [4] Since the refactoring allows for more than two scale types + ('log' or 'linear'), it no longer makes sense to have a toggle. + ``Axes.toggle_log_lineary()`` has been removed. + +:mod:`matplotlib.artist` +~~~~~~~~~~~~~~~~~~~~~~~~ + +============================== ============================================== +Old method New method +============================== ============================================== +``Artist.set_clip_path(path)`` ``Artist.set_clip_path(path, transform)`` [5]_ +============================== ============================================== + +.. [5] :meth:`matplotlib.artist.Artist.set_clip_path` now accepts a + :class:`matplotlib.path.Path` instance and a + :class:`matplotlib.transforms.Transform` that will be applied to + the path immediately before clipping. + +:mod:`matplotlib.collections` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +=========== ================= +Old method New method +=========== ================= +*linestyle* *linestyles* [6]_ +=========== ================= + +.. [6] Linestyles are now treated like all other collection + attributes, i.e. a single value or multiple values may be + provided. + +:mod:`matplotlib.colors` +~~~~~~~~~~~~~~~~~~~~~~~~ + +================================== ===================================================== +Old method New method +================================== ===================================================== +``ColorConvertor.to_rgba_list(c)`` ``colors.to_rgba_array(c)`` + [:meth:`matplotlib.colors.to_rgba_array` + returns an Nx4 NumPy array of RGBA color quadruples.] +================================== ===================================================== + +:mod:`matplotlib.contour` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +===================== =================================================== +Old method New method +===================== =================================================== +``Contour._segments`` ``matplotlib.contour.Contour.get_paths`` [Returns a + list of :class:`matplotlib.path.Path` instances.] +===================== =================================================== + +:mod:`matplotlib.figure` +~~~~~~~~~~~~~~~~~~~~~~~~ + ++----------------------+--------------------------------------+ +| Old method | New method | ++======================+======================================+ +| ``Figure.dpi.get()`` | :attr:`matplotlib.figure.Figure.dpi` | +| ``Figure.dpi.set()`` | *(a property)* | ++----------------------+--------------------------------------+ + +:mod:`matplotlib.patches` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +===================== ==================================================== +Old method New method +===================== ==================================================== +``Patch.get_verts()`` :meth:`matplotlib.patches.Patch.get_path` [Returns a + :class:`matplotlib.path.Path` instance] +===================== ==================================================== + +:mod:`matplotlib.backend_bases` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============================================= ========================================== +Old method New method +============================================= ========================================== +``GraphicsContext.set_clip_rectangle(tuple)`` `GraphicsContext.set_clip_rectangle(bbox) + <.GraphicsContextBase.set_clip_rectangle>` +--------------------------------------------- ------------------------------------------ +``GraphicsContext.get_clip_path()`` `GraphicsContext.get_clip_path() + <.GraphicsContextBase.get_clip_path>` [7]_ +--------------------------------------------- ------------------------------------------ +``GraphicsContext.set_clip_path()`` `GraphicsContext.set_clip_path() + <.GraphicsContextBase.set_clip_path>` [8]_ +============================================= ========================================== + +.. [7] :meth:`matplotlib.backend_bases.GraphicsContextBase.get_clip_path` + returns a tuple of the form (*path*, *affine_transform*), where *path* is a + :class:`matplotlib.path.Path` instance and *affine_transform* is a + :class:`matplotlib.transforms.Affine2D` instance. + +.. [8] :meth:`matplotlib.backend_bases.GraphicsContextBase.set_clip_path` now + only accepts a :class:`matplotlib.transforms.TransformedPath` instance. + +:class:`~matplotlib.backend_bases.RendererBase` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New methods: + +* :meth:`draw_path(self, gc, path, transform, rgbFace) + ` +* :meth:`draw_markers(self, gc, marker_path, marker_trans, path, + trans, rgbFace) + ` +* :meth:`draw_path_collection(self, master_transform, cliprect, + clippath, clippath_trans, paths, all_transforms, offsets, + offsetTrans, facecolors, edgecolors, linewidths, linestyles, + antialiaseds) + ` + *[optional]* + +Changed methods: + +* ``draw_image(self, x, y, im, bbox)`` is now + :meth:`draw_image(self, x, y, im, bbox, clippath, clippath_trans) + ` + +Removed methods: + +* ``draw_arc`` +* ``draw_line_collection`` +* ``draw_line`` +* ``draw_lines`` +* ``draw_point`` +* ``draw_quad_mesh`` +* ``draw_poly_collection`` +* ``draw_polygon`` +* ``draw_rectangle`` +* ``draw_regpoly_collection`` diff --git a/doc/api/prev_api_changes/api_changes_0.98.1.rst b/doc/api/prev_api_changes/api_changes_0.98.1.rst new file mode 100644 index 000000000000..7ec4c767abbc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.98.1.rst @@ -0,0 +1,5 @@ +Changes for 0.98.1 +================== + +* Removed broken ``matplotlib.axes3d`` support and replaced it with a + non-implemented error pointing to 0.91.x diff --git a/doc/api/prev_api_changes/api_changes_0.98.x.rst b/doc/api/prev_api_changes/api_changes_0.98.x.rst new file mode 100644 index 000000000000..d21e8d17092f --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.98.x.rst @@ -0,0 +1,109 @@ + +Changes for 0.98.x +================== +* ``psd()``, ``csd()``, and ``cohere()`` will now automatically wrap negative + frequency components to the beginning of the returned arrays. + This is much more sensible behavior and makes them consistent + with ``specgram()``. The previous behavior was more of an oversight + than a design decision. + +* Added new keyword parameters *nonposx*, *nonposy* to + :class:`matplotlib.axes.Axes` methods that set log scale + parameters. The default is still to mask out non-positive + values, but the kwargs accept 'clip', which causes non-positive + values to be replaced with a very small positive value. + +* Added new :func:`matplotlib.pyplot.fignum_exists` and + :func:`matplotlib.pyplot.get_fignums`; they merely expose + information that had been hidden in ``matplotlib._pylab_helpers``. + +* Deprecated numerix package. + +* Added new :func:`matplotlib.image.imsave` and exposed it to the + :mod:`matplotlib.pyplot` interface. + +* Remove support for pyExcelerator in exceltools -- use xlwt + instead + +* Changed the defaults of acorr and xcorr to use usevlines=True, + maxlags=10 and normed=True since these are the best defaults + +* Following keyword parameters for :class:`matplotlib.legend.Legend` are now + deprecated and new set of parameters are introduced. The new parameters + are given as a fraction of the font-size. Also, *scatteryoffsets*, + *fancybox* and *columnspacing* are added as keyword parameters. + + ================ ================ + Deprecated New + ================ ================ + pad borderpad + labelsep labelspacing + handlelen handlelength + handlestextsep handletextpad + axespad borderaxespad + ================ ================ + +* Removed the configobj and experimental traits rc support + +* Modified :func:`matplotlib.mlab.psd`, :func:`matplotlib.mlab.csd`, + :func:`matplotlib.mlab.cohere`, and :func:`matplotlib.mlab.specgram` + to scale one-sided densities by a factor of 2. Also, optionally + scale the densities by the sampling frequency, which gives true values + of densities that can be integrated by the returned frequency values. + This also gives better MATLAB compatibility. The corresponding + :class:`matplotlib.axes.Axes` methods and :mod:`matplotlib.pyplot` + functions were updated as well. + +* Font lookup now uses a nearest-neighbor approach rather than an + exact match. Some fonts may be different in plots, but should be + closer to what was requested. + +* :meth:`matplotlib.axes.Axes.set_xlim`, + :meth:`matplotlib.axes.Axes.set_ylim` now return a copy of the + ``viewlim`` array to avoid modify-in-place surprises. + +* ``matplotlib.afm.AFM.get_fullname`` and + ``matplotlib.afm.AFM.get_familyname`` no longer raise an + exception if the AFM file does not specify these optional + attributes, but returns a guess based on the required FontName + attribute. + +* Changed precision kwarg in :func:`matplotlib.pyplot.spy`; default is + 0, and the string value 'present' is used for sparse arrays only to + show filled locations. + +* :class:`matplotlib.collections.EllipseCollection` added. + +* Added ``angles`` kwarg to :func:`matplotlib.pyplot.quiver` for more + flexible specification of the arrow angles. + +* Deprecated (raise NotImplementedError) all the mlab2 functions from + :mod:`matplotlib.mlab` out of concern that some of them were not + clean room implementations. + +* Methods :meth:`matplotlib.collections.Collection.get_offsets` and + :meth:`matplotlib.collections.Collection.set_offsets` added to + :class:`~matplotlib.collections.Collection` base class. + +* ``matplotlib.figure.Figure.figurePatch`` renamed + ``matplotlib.figure.Figure.patch``; + ``matplotlib.axes.Axes.axesPatch`` renamed + ``matplotlib.axes.Axes.patch``; + ``matplotlib.axes.Axes.axesFrame`` renamed + ``matplotlib.axes.Axes.frame``. + ``matplotlib.axes.Axes.get_frame``, which returns + ``matplotlib.axes.Axes.patch``, is deprecated. + +* Changes in the :class:`matplotlib.contour.ContourLabeler` attributes + (:func:`matplotlib.pyplot.clabel` function) so that they all have a + form like ``.labelAttribute``. The three attributes that are most + likely to be used by end users, ``.cl``, ``.cl_xy`` and + ``.cl_cvalues`` have been maintained for the moment (in addition to + their renamed versions), but they are deprecated and will eventually + be removed. + +* Moved several functions in :mod:`matplotlib.mlab` and + :mod:`matplotlib.cbook` into a separate module + ``matplotlib.numerical_methods`` because they were unrelated to + the initial purpose of mlab or cbook and appeared more coherent + elsewhere. diff --git a/doc/api/prev_api_changes/api_changes_0.99.rst b/doc/api/prev_api_changes/api_changes_0.99.rst new file mode 100644 index 000000000000..e03face0d075 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.99.rst @@ -0,0 +1,27 @@ +Changes in 0.99 +=============== + +* pylab no longer provides a load and save function. These are + available in matplotlib.mlab, or you can use numpy.loadtxt and + numpy.savetxt for text files, or np.save and np.load for binary + NumPy arrays. + +* User-generated colormaps can now be added to the set recognized + by ``matplotlib.cm.get_cmap``. Colormaps can be made the + default and applied to the current image using + :func:`matplotlib.pyplot.set_cmap`. + +* changed use_mrecords default to False in mlab.csv2rec since this is + partially broken + +* Axes instances no longer have a "frame" attribute. Instead, use the + new "spines" attribute. Spines is a dictionary where the keys are + the names of the spines (e.g., 'left','right' and so on) and the + values are the artists that draw the spines. For normal + (rectilinear) axes, these artists are Line2D instances. For other + axes (such as polar axes), these artists may be Patch instances. + +* Polar plots no longer accept a resolution kwarg. Instead, each Path + must specify its own number of interpolation steps. This is + unlikely to be a user-visible change -- if interpolation of data is + required, that should be done before passing it to Matplotlib. diff --git a/doc/api/prev_api_changes/api_changes_0.99.x.rst b/doc/api/prev_api_changes/api_changes_0.99.x.rst new file mode 100644 index 000000000000..4736d066d43e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_0.99.x.rst @@ -0,0 +1,124 @@ +Changes beyond 0.99.x +===================== + +* The default behavior of :meth:`matplotlib.axes.Axes.set_xlim`, + :meth:`matplotlib.axes.Axes.set_ylim`, and + :meth:`matplotlib.axes.Axes.axis`, and their corresponding + pyplot functions, has been changed: when view limits are + set explicitly with one of these methods, autoscaling is turned + off for the matching axis. A new *auto* kwarg is available to + control this behavior. The limit kwargs have been renamed to + *left* and *right* instead of *xmin* and *xmax*, and *bottom* + and *top* instead of *ymin* and *ymax*. The old names may still + be used, however. + +* There are five new Axes methods with corresponding pyplot + functions to facilitate autoscaling, tick location, and tick + label formatting, and the general appearance of ticks and + tick labels: + + + :meth:`matplotlib.axes.Axes.autoscale` turns autoscaling + on or off, and applies it. + + + :meth:`matplotlib.axes.Axes.margins` sets margins used to + autoscale the ``matplotlib.axes.Axes.viewLim`` based on + the ``matplotlib.axes.Axes.dataLim``. + + + :meth:`matplotlib.axes.Axes.locator_params` allows one to + adjust axes locator parameters such as *nbins*. + + + :meth:`matplotlib.axes.Axes.ticklabel_format` is a convenience + method for controlling the :class:`matplotlib.ticker.ScalarFormatter` + that is used by default with linear axes. + + + :meth:`matplotlib.axes.Axes.tick_params` controls direction, size, + visibility, and color of ticks and their labels. + +* The :meth:`matplotlib.axes.Axes.bar` method accepts a *error_kw* + kwarg; it is a dictionary of kwargs to be passed to the + errorbar function. + +* The :meth:`matplotlib.axes.Axes.hist` *color* kwarg now accepts + a sequence of color specs to match a sequence of datasets. + +* The :class:`~matplotlib.collections.EllipseCollection` has been + changed in two ways: + + + There is a new *units* option, 'xy', that scales the ellipse with + the data units. This matches the :class:'~matplotlib.patches.Ellipse` + scaling. + + + The *height* and *width* kwargs have been changed to specify + the height and width, again for consistency with + :class:`~matplotlib.patches.Ellipse`, and to better match + their names; previously they specified the half-height and + half-width. + +* There is a new rc parameter ``axes.color_cycle``, and the color + cycle is now independent of the rc parameter ``lines.color``. + ``matplotlib.Axes.set_default_color_cycle`` is deprecated. + +* You can now print several figures to one pdf file and modify the + document information dictionary of a pdf file. See the docstrings + of the class :class:`matplotlib.backends.backend_pdf.PdfPages` for + more information. + +* Removed configobj_ and `enthought.traits`_ packages, which are only + required by the experimental traited config and are somewhat out of + date. If needed, install them independently. + +.. _configobj: http://www.voidspace.org.uk/python/configobj.html +.. _`enthought.traits`: http://code.enthought.com/pages/traits.html + +* The new rc parameter ``savefig.extension`` sets the filename extension + that is used by :meth:`matplotlib.figure.Figure.savefig` if its *fname* + argument lacks an extension. + +* In an effort to simplify the backend API, all clipping rectangles + and paths are now passed in using GraphicsContext objects, even + on collections and images. Therefore:: + + draw_path_collection(self, master_transform, cliprect, clippath, + clippath_trans, paths, all_transforms, offsets, + offsetTrans, facecolors, edgecolors, linewidths, + linestyles, antialiaseds, urls) + + # is now + + draw_path_collection(self, gc, master_transform, paths, all_transforms, + offsets, offsetTrans, facecolors, edgecolors, + linewidths, linestyles, antialiaseds, urls) + + + draw_quad_mesh(self, master_transform, cliprect, clippath, + clippath_trans, meshWidth, meshHeight, coordinates, + offsets, offsetTrans, facecolors, antialiased, + showedges) + + # is now + + draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, + coordinates, offsets, offsetTrans, facecolors, + antialiased, showedges) + + + draw_image(self, x, y, im, bbox, clippath=None, clippath_trans=None) + + # is now + + draw_image(self, gc, x, y, im) + +* There are four new Axes methods with corresponding pyplot + functions that deal with unstructured triangular grids: + + + :meth:`matplotlib.axes.Axes.tricontour` draws contour lines + on a triangular grid. + + + :meth:`matplotlib.axes.Axes.tricontourf` draws filled contours + on a triangular grid. + + + :meth:`matplotlib.axes.Axes.tripcolor` draws a pseudocolor + plot on a triangular grid. + + + :meth:`matplotlib.axes.Axes.triplot` draws a triangular grid + as lines and/or markers. diff --git a/doc/api/prev_api_changes/api_changes_1.1.x.rst b/doc/api/prev_api_changes/api_changes_1.1.x.rst new file mode 100644 index 000000000000..790b669081b7 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.1.x.rst @@ -0,0 +1,25 @@ + +API Changes in 1.1.x +==================== + +* Added new :class:`matplotlib.sankey.Sankey` for generating Sankey diagrams. + +* In :meth:`~matplotlib.pyplot.imshow`, setting *interpolation* to 'nearest' + will now always mean that the nearest-neighbor interpolation is performed. + If you want the no-op interpolation to be performed, choose 'none'. + +* There were errors in how the tri-functions were handling input parameters + that had to be fixed. If your tri-plots are not working correctly anymore, + or you were working around apparent mistakes, please see issue #203 in the + github tracker. When in doubt, use kwargs. + +* The 'symlog' scale had some bad behavior in previous versions. This has now + been fixed and users should now be able to use it without frustrations. + The fixes did result in some minor changes in appearance for some users who + may have been depending on the bad behavior. + +* There is now a common set of markers for all plotting functions. Previously, + some markers existed only for :meth:`~matplotlib.pyplot.scatter` or just for + :meth:`~matplotlib.pyplot.plot`. This is now no longer the case. This merge + did result in a conflict. The string 'd' now means "thin diamond" while + 'D' will mean "regular diamond". diff --git a/doc/api/prev_api_changes/api_changes_1.2.x.rst b/doc/api/prev_api_changes/api_changes_1.2.x.rst new file mode 100644 index 000000000000..45a2f35cf29e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.2.x.rst @@ -0,0 +1,145 @@ +API Changes in 1.2.x +==================== + +* The ``classic`` option of the rc parameter ``toolbar`` is deprecated + and will be removed in the next release. + +* The ``matplotlib.cbook.isvector`` method has been removed since it + is no longer functional. + +* The ``rasterization_zorder`` property on `~matplotlib.axes.Axes` sets a + zorder below which artists are rasterized. This has defaulted to + -30000.0, but it now defaults to *None*, meaning no artists will be + rasterized. In order to rasterize artists below a given zorder + value, `.set_rasterization_zorder` must be explicitly called. + +* In :meth:`~matplotlib.axes.Axes.scatter`, and `~.pyplot.scatter`, + when specifying a marker using a tuple, the angle is now specified + in degrees, not radians. + +* Using :meth:`~matplotlib.axes.Axes.twinx` or + :meth:`~matplotlib.axes.Axes.twiny` no longer overrides the current locaters + and formatters on the axes. + +* In :meth:`~matplotlib.axes.Axes.contourf`, the handling of the *extend* + kwarg has changed. Formerly, the extended ranges were mapped + after to 0, 1 after being normed, so that they always corresponded + to the extreme values of the colormap. Now they are mapped + outside this range so that they correspond to the special + colormap values determined by the + :meth:`~matplotlib.colors.Colormap.set_under` and + :meth:`~matplotlib.colors.Colormap.set_over` methods, which + default to the colormap end points. + +* The new rc parameter ``savefig.format`` replaces ``cairo.format`` and + ``savefig.extension``, and sets the default file format used by + :meth:`matplotlib.figure.Figure.savefig`. + +* In :func:`.pyplot.pie` and :meth:`.axes.Axes.pie`, one can now set the radius + of the pie; setting the *radius* to 'None' (the default value), will result + in a pie with a radius of 1 as before. + +* Use of ``matplotlib.projections.projection_factory`` is now deprecated + in favour of axes class identification using + ``matplotlib.projections.process_projection_requirements`` followed by + direct axes class invocation (at the time of writing, functions which do this + are: :meth:`~matplotlib.figure.Figure.add_axes`, + :meth:`~matplotlib.figure.Figure.add_subplot` and + :meth:`~matplotlib.figure.Figure.gca`). Therefore:: + + + key = figure._make_key(*args, **kwargs) + ispolar = kwargs.pop('polar', False) + projection = kwargs.pop('projection', None) + if ispolar: + if projection is not None and projection != 'polar': + raise ValueError('polar and projection args are inconsistent') + projection = 'polar' + ax = projection_factory(projection, self, rect, **kwargs) + key = self._make_key(*args, **kwargs) + + # is now + + projection_class, kwargs, key = \ + process_projection_requirements(self, *args, **kwargs) + ax = projection_class(self, rect, **kwargs) + + This change means that third party objects can expose themselves as + Matplotlib axes by providing a ``_as_mpl_axes`` method. See + :mod:`matplotlib.projections` for more detail. + +* A new keyword *extendfrac* in :meth:`~matplotlib.pyplot.colorbar` and + :class:`~matplotlib.colorbar.ColorbarBase` allows one to control the size of + the triangular minimum and maximum extensions on colorbars. + +* A new keyword *capthick* in :meth:`~matplotlib.pyplot.errorbar` has been + added as an intuitive alias to the *markeredgewidth* and *mew* keyword + arguments, which indirectly controlled the thickness of the caps on + the errorbars. For backwards compatibility, specifying either of the + original keyword arguments will override any value provided by + *capthick*. + +* Transform subclassing behaviour is now subtly changed. If your transform + implements a non-affine transformation, then it should override the + ``transform_non_affine`` method, rather than the generic ``transform`` method. + Previously transforms would define ``transform`` and then copy the + method into ``transform_non_affine``:: + + class MyTransform(mtrans.Transform): + def transform(self, xy): + ... + transform_non_affine = transform + + + This approach will no longer function correctly and should be changed to:: + + class MyTransform(mtrans.Transform): + def transform_non_affine(self, xy): + ... + + +* Artists no longer have ``x_isdata`` or ``y_isdata`` attributes; instead + any artist's transform can be interrogated with + ``artist_instance.get_transform().contains_branch(ax.transData)`` + +* Lines added to an axes now take into account their transform when updating the + data and view limits. This means transforms can now be used as a pre-transform. + For instance:: + + >>> import matplotlib.pyplot as plt + >>> import matplotlib.transforms as mtrans + >>> ax = plt.axes() + >>> ax.plot(range(10), transform=mtrans.Affine2D().scale(10) + ax.transData) + >>> print(ax.viewLim) + Bbox('array([[ 0., 0.],\n [ 90., 90.]])') + +* One can now easily get a transform which goes from one transform's coordinate + system to another, in an optimized way, using the new subtract method on a + transform. For instance, to go from data coordinates to axes coordinates:: + + >>> import matplotlib.pyplot as plt + >>> ax = plt.axes() + >>> data2ax = ax.transData - ax.transAxes + >>> print(ax.transData.depth, ax.transAxes.depth) + 3, 1 + >>> print(data2ax.depth) + 2 + + for versions before 1.2 this could only be achieved in a sub-optimal way, + using ``ax.transData + ax.transAxes.inverted()`` (depth is a new concept, + but had it existed it would return 4 for this example). + +* ``twinx`` and ``twiny`` now returns an instance of SubplotBase if + parent axes is an instance of SubplotBase. + +* All Qt3-based backends are now deprecated due to the lack of py3k bindings. + Qt and QtAgg backends will continue to work in v1.2.x for py2.6 + and py2.7. It is anticipated that the Qt3 support will be completely + removed for the next release. + +* ``matplotlib.colors.ColorConverter``, + :class:`~matplotlib.colors.Colormap` and + :class:`~matplotlib.colors.Normalize` now subclasses ``object`` + +* ContourSet instances no longer have a ``transform`` attribute. Instead, + access the transform with the ``get_transform`` method. diff --git a/doc/api/prev_api_changes/api_changes_1.3.x.rst b/doc/api/prev_api_changes/api_changes_1.3.x.rst new file mode 100644 index 000000000000..2601824ba7d1 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.3.x.rst @@ -0,0 +1,218 @@ +.. _changes_in_1_3: + + +API Changes in 1.3.x +==================== + +Changes in 1.3.1 +---------------- + +It is rare that we make an API change in a micro release, however, +for 1.3.1 since 1.3.0 the following change was made: + +- ``text.Text.cached`` (used to cache font objects) has been made into a + private variable. Among the obvious encapsulation benefit, this + removes this confusing-looking member from the documentation. + +- The method :meth:`~matplotlib.axes.Axes.hist` now always returns bin + occupancies as an array of type `float`. Previously, it was sometimes + an array of type `int`, depending on the call. + +Code removal +------------ + +* The following items that were deprecated in version 1.2 or earlier + have now been removed completely. + + - The Qt 3.x backends (``qt`` and ``qtagg``) have been removed in + favor of the Qt 4.x backends (``qt4`` and ``qt4agg``). + + - The FltkAgg and Emf backends have been removed. + + - The ``matplotlib.nxutils`` module has been removed. Use the + functionality on `matplotlib.path.Path.contains_point` and + friends instead. + + - Instead of ``axes.Axes.get_frame``, use ``axes.Axes.patch``. + + - The following keyword arguments to the `~.axes.Axes.legend` function have + been renamed: + + - *pad* -> *borderpad* + - *labelsep* -> *labelspacing* + - *handlelen* -> *handlelength* + - *handletextsep* -> *handletextpad* + - *axespad* -> *borderaxespad* + + Related to this, the following rcParams have been removed: + + - ``legend.pad``, + - ``legend.labelsep``, + - ``legend.handlelen``, + - ``legend.handletextsep`` and + - ``legend.axespad`` + + - For the `~.axes.Axes.hist` function, instead of *width*, use *rwidth* + (relative width). + + - On `.patches.Circle`, the *resolution* keyword argument has been removed. + For a circle made up of line segments, use + `.patches.CirclePolygon`. + + - The printing functions in the Wx backend have been removed due + to the burden of keeping them up-to-date. + + - ``mlab.liaupunov`` has been removed. + + - ``mlab.save``, ``mlab.load``, ``pylab.save`` and ``pylab.load`` have + been removed. We recommend using `numpy.savetxt` and + `numpy.loadtxt` instead. + + - ``widgets.HorizontalSpanSelector`` has been removed. Use + `.widgets.SpanSelector` instead. + +Code deprecation +---------------- + +* The CocoaAgg backend has been deprecated, with the possibility for + deletion or resurrection in a future release. + +* The top-level functions in `matplotlib.path` that are implemented in + C++ were never meant to be public. Instead, users should use the + Pythonic wrappers for them in the `.path.Path` and + `.collections.Collection` classes. Use the following mapping to update + your code: + + - ``point_in_path`` -> `.path.Path.contains_point` + - ``get_path_extents`` -> `.path.Path.get_extents` + - ``point_in_path_collection`` -> `.collections.Collection.contains` + - ``path_in_path`` -> `.path.Path.contains_path` + - ``path_intersects_path`` -> `.path.Path.intersects_path` + - ``convert_path_to_polygons`` -> `.path.Path.to_polygons` + - ``cleanup_path`` -> `.path.Path.cleaned` + - ``points_in_path`` -> `.path.Path.contains_points` + - ``clip_path_to_rect`` -> `.path.Path.clip_to_bbox` + +* ``matplotlib.colors.normalize`` and ``matplotlib.colors.no_norm`` have + been deprecated in favour of `matplotlib.colors.Normalize` and + `matplotlib.colors.NoNorm` respectively. + +* The `.ScalarMappable` class' ``set_colorbar`` method is now deprecated. + Instead, the :attr:`matplotlib.cm.ScalarMappable.colorbar` attribute should + be used. In previous Matplotlib versions this attribute was an undocumented + tuple of ``(colorbar_instance, colorbar_axes)`` but is now just + ``colorbar_instance``. To get the colorbar axes it is possible to just use + the ``matplotlib.colorbar.ColorbarBase.ax`` attribute on a colorbar + instance. + +* The ``matplotlib.mpl`` module is now deprecated. Those who relied on this + module should transition to simply using ``import matplotlib as mpl``. + +Code changes +------------ + +* :class:`~matplotlib.patches.Patch` now fully supports using RGBA values for + its ``facecolor`` and ``edgecolor`` attributes, which enables faces and + edges to have different alpha values. If the + :class:`~matplotlib.patches.Patch` object's ``alpha`` attribute is set to + anything other than ``None``, that value will override any alpha-channel + value in both the face and edge colors. Previously, if + :class:`~matplotlib.patches.Patch` had ``alpha=None``, the alpha component + of ``edgecolor`` would be applied to both the edge and face. + +* The optional ``isRGB`` argument to + :meth:`~matplotlib.backend_bases.GraphicsContextBase.set_foreground` (and + the other GraphicsContext classes that descend from it) has been renamed to + ``isRGBA``, and should now only be set to ``True`` if the ``fg`` color + argument is known to be an RGBA tuple. + +* For :class:`~matplotlib.patches.Patch`, the ``capstyle`` used is now + ``butt``, to be consistent with the default for most other objects, and to + avoid problems with non-solid ``linestyle`` appearing solid when using a + large ``linewidth``. Previously, :class:`~matplotlib.patches.Patch` used + ``capstyle='projecting'``. + +* `.Path` objects can now be marked as *readonly* by passing + ``readonly=True`` to its constructor. The built-in path singletons, + obtained through ``Path.unit*`` class methods return readonly paths. + If you have code that modified these, you will need to make a + deepcopy first, using either:: + + import copy + path = copy.deepcopy(Path.unit_circle()) + + # or + + path = Path.unit_circle().deepcopy() + + Deep copying a `.Path` always creates an editable (i.e. non-readonly) + `.Path`. + +* The list at ``Path.NUM_VERTICES`` was replaced by a dictionary mapping + Path codes to the number of expected vertices at + :attr:`~matplotlib.path.Path.NUM_VERTICES_FOR_CODE`. + +* To support XKCD style plots, the ``matplotlib.path.cleanup_path`` + method's signature was updated to require a sketch argument. Users of + ``matplotlib.path.cleanup_path`` are encouraged to use the new + :meth:`~matplotlib.path.Path.cleaned` Path method. + +* Data limits on a plot now start from a state of having "null" + limits, rather than limits in the range (0, 1). This has an effect + on artists that only control limits in one direction, such as + `.axes.Axes.axvline` and `.axes.Axes.axhline`, since their limits will no + longer also include the range (0, 1). This fixes some problems where the + computed limits would be dependent on the order in which artists + were added to the axes. + +* Fixed a bug in setting the position for the right/top spine with data + position type. Previously, it would draw the right or top spine at + +1 data offset. + +* In :class:`~matplotlib.patches.FancyArrow`, the default arrow head + width, ``head_width``, has been made larger to produce a visible + arrow head. The new value of this kwarg is ``head_width = 20 * + width``. + +* It is now possible to provide ``number of levels + 1`` colors in the case of + ``extend='both'`` for contourf (or just ``number of levels`` colors for an + extend value ``min`` or ``max``) such that the resulting colormap's + ``set_under`` and ``set_over`` are defined appropriately. Any other number + of colors will continue to behave as before (if more colors are provided + than levels, the colors will be unused). A similar change has been applied + to contour, where ``extend='both'`` would expect ``number of levels + 2`` + colors. + +* A new keyword *extendrect* in :meth:`~matplotlib.pyplot.colorbar` and + :class:`~matplotlib.colorbar.ColorbarBase` allows one to control the shape + of colorbar extensions. + +* The extension of :class:`~matplotlib.widgets.MultiCursor` to both vertical + (default) and/or horizontal cursor implied that ``self.line`` is replaced + by ``self.vline`` for vertical cursors lines and ``self.hline`` is added + for the horizontal cursors lines. + +* On POSIX platforms, the ``matplotlib.cbook.report_memory`` function + raises :class:`NotImplementedError` instead of :class:`OSError` if the + :command:`ps` command cannot be run. + +* The ``matplotlib.cbook.check_output`` function has been moved to + ``matplotlib.compat.subprocess``. + +Configuration and rcParams +-------------------------- + +* On Linux, the user-specific :file:`matplotlibrc` configuration file is now + located in :file:`~/.config/matplotlib/matplotlibrc` to conform to the + `XDG Base Directory Specification + `_. + +* The ``font.*`` rcParams now affect only text objects created after the + rcParam has been set, and will not retroactively affect already + existing text objects. This brings their behavior in line with most + other rcParams. + +* Removed call of :meth:`~matplotlib.axes.Axes.grid` in + ``matplotlib.pyplot.plotfile``. To draw the axes grid, set the + ``axes.grid`` rcParam to *True*, or explicitly call + :meth:`~matplotlib.axes.Axes.grid`. diff --git a/doc/api/prev_api_changes/api_changes_1.4.x.rst b/doc/api/prev_api_changes/api_changes_1.4.x.rst new file mode 100644 index 000000000000..915aa28a9f26 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.4.x.rst @@ -0,0 +1,212 @@ +API Changes in 1.4.x +==================== + +Code changes +------------ + +* A major refactoring of the axes module was made. The axes module has been + split into smaller modules: + + - the ``_base`` module, which contains a new private ``_AxesBase`` class. + This class contains all methods except plotting and labelling methods. + - the `~matplotlib.axes` module, which contains the `.axes.Axes` class. + This class inherits from ``_AxesBase``, and contains all plotting and + labelling methods. + - the ``_subplot`` module, with all the classes concerning subplotting. + + There are a couple of things that do not exists in the `~matplotlib.axes` + module's namespace anymore. If you use them, you need to import them from their + original location: + + - ``math`` -> ``import math`` + - ``ma`` -> ``from numpy import ma`` + - ``cbook`` -> ``from matplotlib import cbook`` + - ``docstring`` -> ``from matplotlib import docstring`` + - ``is_sequence_of_strings`` -> ``from matplotlib.cbook import is_sequence_of_strings`` + - ``is_string_like`` -> ``from matplotlib.cbook import is_string_like`` + - ``iterable`` -> ``from matplotlib.cbook import iterable`` + - ``itertools`` -> ``import itertools`` + - ``martist`` -> ``from matplotlib import artist as martist`` + - ``matplotlib`` -> ``import matplotlib`` + - ``mcoll`` -> ``from matplotlib import collections as mcoll`` + - ``mcolors`` -> ``from matplotlib import colors as mcolors`` + - ``mcontour`` -> ``from matplotlib import contour as mcontour`` + - ``mpatches`` -> ``from matplotlib import patches as mpatches`` + - ``mpath`` -> ``from matplotlib import path as mpath`` + - ``mquiver`` -> ``from matplotlib import quiver as mquiver`` + - ``mstack`` -> ``from matplotlib import stack as mstack`` + - ``mstream`` -> ``from matplotlib import stream as mstream`` + - ``mtable`` -> ``from matplotlib import table as mtable`` + +* As part of the refactoring to enable Qt5 support, the module + ``matplotlib.backends.qt4_compat`` was renamed to + ``matplotlib.backends.qt_compat``. ``qt4_compat`` is deprecated in 1.4 and + will be removed in 1.5. + +* The :func:`~matplotlib.pyplot.errorbar` method has been changed such that + the upper and lower limits (*lolims*, *uplims*, *xlolims*, *xuplims*) now + point in the correct direction. + +* The *fmt* kwarg for :func:`~matplotlib.pyplot.errorbar` now supports + the string 'none' to suppress drawing of a line and markers; use + of the *None* object for this is deprecated. The default *fmt* + value is changed to the empty string (''), so the line and markers + are governed by the :func:`~matplotlib.pyplot.plot` defaults. + +* A bug has been fixed in the path effects rendering of fonts, which now means + that the font size is consistent with non-path effect fonts. See + https://github.com/matplotlib/matplotlib/issues/2889 for more detail. + +* The Sphinx extensions ``ipython_directive`` and + ``ipython_console_highlighting`` have been moved to the IPython + project itself. While they remain in Matplotlib for this release, + they have been deprecated. Update your extensions in :file:`conf.py` to + point to ``IPython.sphinxext.ipython_directive`` instead of + ``matplotlib.sphinxext.ipython_directive``. + +* In ``matplotlib.finance``, almost all functions have been deprecated + and replaced with a pair of functions name ``*_ochl`` and ``*_ohlc``. + The former is the 'open-close-high-low' order of quotes used + previously in this module, and the latter is the + 'open-high-low-close' order that is standard in finance. + +* For consistency the ``face_alpha`` keyword to + :class:`matplotlib.patheffects.SimplePatchShadow` has been deprecated in + favour of the ``alpha`` keyword. Similarly, the keyword ``offset_xy`` is now + named ``offset`` across all :class:`~matplotlib.patheffects.AbstractPathEffect`\ s. + ``matplotlib.patheffects._Base`` has + been renamed to :class:`matplotlib.patheffects.AbstractPathEffect`. + ``matplotlib.patheffect.ProxyRenderer`` has been renamed to + :class:`matplotlib.patheffects.PathEffectRenderer` and is now a full + RendererBase subclass. + +* The artist used to draw the outline of a `.Figure.colorbar` has been changed + from a `matplotlib.lines.Line2D` to `matplotlib.patches.Polygon`, thus + ``colorbar.ColorbarBase.outline`` is now a `matplotlib.patches.Polygon` + object. + +* The legend handler interface has changed from a callable, to any object + which implements the ``legend_artists`` method (a deprecation phase will + see this interface be maintained for v1.4). See + :ref:`legend_guide` for further details. Further legend changes + include: + + * ``matplotlib.axes.Axes._get_legend_handles`` now returns a generator of + handles, rather than a list. + + * The :func:`~matplotlib.pyplot.legend` function's *loc* positional + argument has been deprecated. Use the *loc* keyword argument instead. + +* The :rc:`savefig.transparent` has been added to control + default transparency when saving figures. + +* Slightly refactored the `.Annotation` family. The text location in + `.Annotation` is now entirely handled by the underlying `.Text` + object so ``.set_position`` works as expected. The attributes *xytext* and + *textcoords* have been deprecated in favor of *xyann* and *anncoords* so + that `.Annotation` and `.AnnotationBbox` can share a common sensibly named + api for getting/setting the location of the text or box. + + - *xyann* -> set the location of the annotation + - *xy* -> set where the arrow points to + - *anncoords* -> set the units of the annotation location + - *xycoords* -> set the units of the point location + - ``set_position()`` -> `.Annotation` only set location of annotation + +* `matplotlib.mlab.specgram`, `matplotlib.mlab.psd`, `matplotlib.mlab.csd`, + `matplotlib.mlab.cohere`, ``matplotlib.mlab.cohere_pairs``, + `matplotlib.pyplot.specgram`, `matplotlib.pyplot.psd`, + `matplotlib.pyplot.csd`, and `matplotlib.pyplot.cohere` now raise + ValueError where they previously raised AssertionError. + +* For `matplotlib.mlab.psd`, `matplotlib.mlab.csd`, + `matplotlib.mlab.cohere`, ``matplotlib.mlab.cohere_pairs``, + `matplotlib.pyplot.specgram`, `matplotlib.pyplot.psd`, + `matplotlib.pyplot.csd`, and `matplotlib.pyplot.cohere`, in cases + where a shape (n, 1) array is returned, this is now converted to a (n, ) + array. Previously, (n, m) arrays were averaged to an (n, ) array, but + (n, 1) arrays were returned unchanged. This change makes the dimensions + consistent in both cases. + +* Added the :rc:`axes.formatter.useoffset` to control the default value + of *useOffset* in `.ticker.ScalarFormatter` + +* Added `.Formatter` sub-class `.StrMethodFormatter` which + does the exact same thing as `.FormatStrFormatter`, but for new-style + formatting strings. + +* Deprecated ``matplotlib.testing.image_util`` and the only function within, + ``matplotlib.testing.image_util.autocontrast``. These will be removed + completely in v1.5.0. + +* The ``fmt`` argument of ``Axes.plot_date`` has been + changed from ``bo`` to just ``o``, so color cycling can happen by default. + +* Removed the class ``FigureManagerQTAgg`` and deprecated + ``NavigationToolbar2QTAgg`` which will be removed in 1.5. + +* Removed formerly public (non-prefixed) attributes ``rect`` and + ``drawRect`` from ``FigureCanvasQTAgg``; they were always an + implementation detail of the (preserved) ``drawRectangle()`` function. + +* The function signatures of ``matplotlib.tight_bbox.adjust_bbox`` and + ``matplotlib.tight_bbox.process_figure_for_rasterizing`` have been changed. + A new *fixed_dpi* parameter allows for overriding the ``figure.dpi`` setting + instead of trying to deduce the intended behaviour from the file format. + +* Added support for horizontal/vertical axes padding to + `mpl_toolkits.axes_grid1.axes_grid.ImageGrid` --- argument *axes_pad* can now + be tuple-like if separate axis padding is required. + The original behavior is preserved. + +* Added support for skewed transforms to `matplotlib.transforms.Affine2D`, + which can be created using the `~.Affine2D.skew` and `~.Affine2D.skew_deg` + methods. + +* Added clockwise parameter to control sectors direction in `.axes.Axes.pie` + +* In `matplotlib.lines.Line2D` the *markevery* functionality has been extended. + Previously an integer start-index and stride-length could be specified using + either a two-element-list or a two-element-tuple. Now this can only be done + using a two-element-tuple. If a two-element-list is used then it will be + treated as NumPy fancy indexing and only the two markers corresponding to the + given indexes will be shown. + +* Removed *prop* keyword argument from + `mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar` call. It was + passed through to the base-class ``__init__`` and is only used for setting + padding. Now *fontproperties* (which is what is really used to set the font + properties of `.AnchoredSizeBar`) is passed through in place of *prop*. If + *fontproperties* is not passed in, but *prop* is, then *prop* is used in + place of *fontproperties*. If both are passed in, *prop* is silently + ignored. + + +* The use of the index 0 in `.pyplot.subplot` and related commands is + deprecated. Due to a lack of validation, calling ``plt.subplots(2, 2, 0)`` + does not raise an exception, but puts an axes in the _last_ + position. This is due to the indexing in subplot being 1-based (to + mirror MATLAB) so before indexing into the `.GridSpec` object used to + determine where the axes should go, 1 is subtracted off. Passing in + 0 results in passing -1 to `.GridSpec` which results in getting the + last position back. Even though this behavior is clearly wrong and + not intended, we are going through a deprecation cycle in an + abundance of caution that any users are exploiting this 'feature'. + The use of 0 as an index will raise a warning in 1.4 and an + exception in 1.5. + +* Clipping is now off by default on offset boxes. + +* Matplotlib now uses a less-aggressive call to ``gc.collect(1)`` when + closing figures to avoid major delays with large numbers of user objects + in memory. + +* The default clip value of *all* pie artists now defaults to ``False``. + + +Code removal +------------ + +* Removed ``mlab.levypdf``. The code raised a NumPy error (and has for + a long time) and was not the standard form of the Levy distribution. + ``scipy.stats.levy`` should be used instead diff --git a/doc/api/prev_api_changes/api_changes_1.5.0.rst b/doc/api/prev_api_changes/api_changes_1.5.0.rst new file mode 100644 index 000000000000..513971098b93 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.5.0.rst @@ -0,0 +1,406 @@ + +API Changes in 1.5.0 +==================== + +Code Changes +------------ + +Reversed `matplotlib.cbook.ls_mapper`, added `.ls_mapper_r` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `matplotlib.cbook.ls_mapper` was a dictionary with +the long-form line-style names (``"solid"``) as keys and the short +forms (``"-"``) as values. This long-to-short mapping is now done +by `.ls_mapper_r`, and the short-to-long mapping is done by the +`.ls_mapper`. + +Prevent moving artists between Axes, Property-ify Artist.axes, deprecate Artist.{get,set}_axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was done to prevent an Artist that is +already associated with an Axes from being moved/added to a different Axes. +This was never supported as it causes havoc with the transform stack. +The apparent support for this (as it did not raise an exception) was +the source of multiple bug reports and questions on SO. + +For almost all use-cases, the assignment of the axes to an artist should be +taken care of by the axes as part of the ``Axes.add_*`` method, hence the +deprecation of {get,set}_axes. + +Removing the ``set_axes`` method will also remove the 'axes' line from +the ACCEPTS kwarg tables (assuming that the removal date gets here +before that gets overhauled). + +Tightened input validation on 'pivot' kwarg to quiver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tightened validation so that only {'tip', 'tail', 'mid', and 'middle'} (but any +capitalization) are valid values for the *pivot* keyword argument in the +`.Quiver` class (and hence `.axes.Axes.quiver` and `.pyplot.quiver` which both +fully delegate to `.Quiver`). Previously any input matching 'mid.*' would be +interpreted as 'middle', 'tip.*' as 'tip' and any string not matching one of +those patterns as 'tail'. + +The value of ``Quiver.pivot`` is normalized to be in the set {'tip', 'tail', +'middle'} in `.Quiver`. + +Reordered ``Axes.get_children`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The artist order returned by `.axes.Axes.get_children` did not +match the one used by `.axes.Axes.draw`. They now use the same +order, as `.axes.Axes.draw` now calls `.axes.Axes.get_children`. + +Changed behaviour of contour plots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default behaviour of :func:`~matplotlib.pyplot.contour` and +:func:`~matplotlib.pyplot.contourf` when using a masked array is now determined +by the new keyword argument *corner_mask*, or if this is not specified then +the new :rc:`contour.corner_mask` instead. The new default behaviour is +equivalent to using ``corner_mask=True``; the previous behaviour can be obtained +using ``corner_mask=False`` or by changing the rcParam. The example +https://matplotlib.org/examples/pylab_examples/contour_corner_mask.html +demonstrates the difference. Use of the old contouring algorithm, which is +obtained with ``corner_mask='legacy'``, is now deprecated. + +Contour labels may now appear in different places than in earlier versions of +Matplotlib. + +In addition, the keyword argument *nchunk* now applies to +:func:`~matplotlib.pyplot.contour` as well as +:func:`~matplotlib.pyplot.contourf`, and it subdivides the domain into +subdomains of exactly *nchunk* by *nchunk* quads, whereas previously it was +only roughly *nchunk* by *nchunk* quads. + +The C/C++ object that performs contour calculations used to be stored in the +public attribute ``QuadContourSet.Cntr``, but is now stored in a private +attribute and should not be accessed by end users. + +Added set_params function to all Locator types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was a bug fix targeted at making the api for Locators more consistent. + +In the old behavior, only locators of type MaxNLocator have set_params() +defined, causing its use on any other Locator to raise an AttributeError *( +aside: set_params(args) is a function that sets the parameters of a Locator +instance to be as specified within args)*. The fix involves moving set_params() +to the Locator class such that all subtypes will have this function defined. + +Since each of the Locator subtypes have their own modifiable parameters, a +universal set_params() in Locator isn't ideal. Instead, a default no-operation +function that raises a warning is implemented in Locator. Subtypes extending +Locator will then override with their own implementations. Subtypes that do +not have a need for set_params() will fall back onto their parent's +implementation, which raises a warning as intended. + +In the new behavior, Locator instances will not raise an AttributeError +when set_params() is called. For Locators that do not implement set_params(), +the default implementation in Locator is used. + +Disallow ``None`` as x or y value in ax.plot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Do not allow ``None`` as a valid input for the ``x`` or ``y`` args in +`.axes.Axes.plot`. This may break some user code, but this was never +officially supported (ex documented) and allowing ``None`` objects through can +lead to confusing exceptions downstream. + +To create an empty line use :: + + ln1, = ax.plot([], [], ...) + ln2, = ax.plot([], ...) + +In either case to update the data in the `.Line2D` object you must update +both the ``x`` and ``y`` data. + + +Removed *args* and *kwargs* from ``MicrosecondLocator.__call__`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The call signature of ``matplotlib.dates.MicrosecondLocator.__call__`` +has changed from ``__call__(self, *args, **kwargs)`` to ``__call__(self)``. +This is consistent with the superclass :class:`~matplotlib.ticker.Locator` +and also all the other Locators derived from this superclass. + + +No `ValueError` for the MicrosecondLocator and YearLocator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :class:`~matplotlib.dates.MicrosecondLocator` and +:class:`~matplotlib.dates.YearLocator` objects when called will return +an empty list if the axes have no data or the view has no interval. +Previously, they raised a `ValueError`. This is consistent with all +the Date Locators. + +'OffsetBox.DrawingArea' respects the 'clip' keyword argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The call signature was ``OffsetBox.DrawingArea(..., clip=True)`` but nothing +was done with the *clip* argument. The object did not do any clipping +regardless of that parameter. Now the object can and does clip the +child `.Artist`\ s if they are set to be clipped. + +You can turn off the clipping on a per-child basis using +``child.set_clip_on(False)``. + +Add salt to clipPath id +~~~~~~~~~~~~~~~~~~~~~~~ + +Add salt to the hash used to determine the id of the ``clipPath`` +nodes. This is to avoid conflicts when two svg documents with the same +clip path are included in the same document (see +https://github.com/ipython/ipython/issues/8133 and +https://github.com/matplotlib/matplotlib/issues/4349 ), however this +means that the svg output is no longer deterministic if the same +figure is saved twice. It is not expected that this will affect any +users as the current ids are generated from an md5 hash of properties +of the clip path and any user would have a very difficult time +anticipating the value of the id. + +Changed snap threshold for circle markers to inf +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When drawing circle markers above some marker size (previously 6.0) +the path used to generate the marker was snapped to pixel centers. However, +this ends up distorting the marker away from a circle. By setting the +snap threshold to inf snapping is never done on circles. + +This change broke several tests, but is an improvement. + +Preserve units with Text position +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously the 'get_position' method on Text would strip away unit information +even though the units were still present. There was no inherent need to do +this, so it has been changed so that unit data (if present) will be preserved. +Essentially a call to 'get_position' will return the exact value from a call to +'set_position'. + +If you wish to get the old behaviour, then you can use the new method called +'get_unitless_position'. + +New API for custom Axes view changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Interactive pan and zoom were previously implemented using a Cartesian-specific +algorithm that was not necessarily applicable to custom Axes. Three new private +methods, ``matplotlib.axes._base._AxesBase._get_view``, +``matplotlib.axes._base._AxesBase._set_view``, and +``matplotlib.axes._base._AxesBase._set_view_from_bbox``, allow for custom +*Axes* classes to override the pan and zoom algorithms. Implementers of +custom *Axes* who override these methods may provide suitable behaviour for +both pan and zoom as well as the view navigation buttons on the interactive +toolbars. + +MathTex visual changes +---------------------- + +The spacing commands in mathtext have been changed to more closely +match vanilla TeX. + + +Improved spacing in mathtext +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The extra space that appeared after subscripts and superscripts has +been removed. + +No annotation coordinates wrap +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In #2351 for 1.4.0 the behavior of ['axes points', 'axes pixel', +'figure points', 'figure pixel'] as coordinates was change to +no longer wrap for negative values. In 1.4.3 this change was +reverted for 'axes points' and 'axes pixel' and in addition caused +'axes fraction' to wrap. For 1.5 the behavior has been reverted to +as it was in 1.4.0-1.4.2, no wrapping for any type of coordinate. + +Deprecation +----------- + +Deprecated ``GraphicsContextBase.set_graylevel`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``GraphicsContextBase.set_graylevel`` function has been deprecated in 1.5 +and will be removed in 1.6. It has been unused. The +`.GraphicsContextBase.set_foreground` could be used instead. + +deprecated idle_event +~~~~~~~~~~~~~~~~~~~~~ + +The ``idle_event`` was broken or missing in most backends and causes spurious +warnings in some cases, and its use in creating animations is now obsolete due +to the animations module. Therefore code involving it has been removed from all +but the wx backend (where it partially works), and its use is deprecated. The +`.animation` module may be used instead to create animations. + +``color_cycle`` deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In light of the new property cycling feature, +the Axes method ``set_color_cycle`` is now deprecated. +Calling this method will replace the current property cycle with +one that cycles just the given colors. + +Similarly, the rc parameter *axes.color_cycle* is also deprecated in +lieu of the new :rc:`axes.prop_cycle` parameter. Having both parameters in +the same rc file is not recommended as the result cannot be +predicted. For compatibility, setting *axes.color_cycle* will +replace the cycler in :rc:`axes.prop_cycle` with a color cycle. +Accessing *axes.color_cycle* will return just the color portion +of the property cycle, if it exists. + +Timeline for removal has not been set. + + +Bundled jquery +-------------- + +The version of jquery bundled with the webagg backend has been upgraded +from 1.7.1 to 1.11.3. If you are using the version of jquery bundled +with webagg you will need to update your html files as such + +.. code-block:: diff + + - + + + + +Code Removed +------------ + +Removed ``Image`` from main namespace +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Image`` was imported from PIL/pillow to test if PIL is available, but +there is no reason to keep ``Image`` in the namespace once the availability +has been determined. + +Removed ``lod`` from Artist +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Removed the method ``set_lod`` and all references to the attribute ``_lod`` as +they are not used anywhere else in the code base. It appears to be a feature +stub that was never built out. + +Removed threading related classes from cbook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The classes ``Scheduler``, ``Timeout``, and ``Idle`` were in cbook, but +are not used internally. They appear to be a prototype for the idle event +system which was not working and has recently been pulled out. + +Removed *Lena* images from sample_data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``lena.png`` and ``lena.jpg`` images have been removed from +Matplotlib's sample_data directory. The images are also no longer +available from `matplotlib.cbook.get_sample_data`. We suggest using +``matplotlib.cbook.get_sample_data('grace_hopper.png')`` or +``matplotlib.cbook.get_sample_data('grace_hopper.jpg')`` instead. + + +Legend +~~~~~~ +Removed handling of *loc* as a positional argument to `.Legend` + + +Legend handlers +~~~~~~~~~~~~~~~ +Remove code to allow legend handlers to be callable. They must now +implement a method ``legend_artist``. + + +Axis +~~~~ +Removed method ``set_scale``. This is now handled via a private method which +should not be used directly by users. It is called via ``Axes.set_{x,y}scale`` +which takes care of ensuring the related changes are also made to the Axes +object. + +finance.py +~~~~~~~~~~ + +Removed functions with ambiguous argument order from finance.py + + +Annotation +~~~~~~~~~~ + +Removed ``textcoords`` and ``xytext`` proprieties from Annotation objects. + + +sphinxext.ipython_*.py +~~~~~~~~~~~~~~~~~~~~~~ + +Both ``ipython_console_highlighting`` and ``ipython_directive`` have been +moved to IPython. + +Change your import from ``matplotlib.sphinxext.ipython_directive`` to +``IPython.sphinxext.ipython_directive`` and from +``matplotlib.sphinxext.ipython_directive`` to +``IPython.sphinxext.ipython_directive`` + + +LineCollection.color +~~~~~~~~~~~~~~~~~~~~ + +Deprecated in 2005, use ``set_color`` + + +remove ``'faceted'`` as a valid value for *shading* in ``tri.tripcolor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use *edgecolor* instead. Added validation on *shading* to only be valid +values. + + +Remove ``faceted`` kwarg from scatter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Remove support for the ``faceted`` kwarg. This was deprecated in +d48b34288e9651ff95c3b8a071ef5ac5cf50bae7 (2008-04-18!) and replaced by +``edgecolor``. + + +Remove ``set_colorbar`` method from ``ScalarMappable`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Remove ``set_colorbar`` method, use `~.cm.ScalarMappable.colorbar` attribute +directly. + + +patheffects.svg +~~~~~~~~~~~~~~~ + +- remove ``get_proxy_renderer`` method from ``AbstractPathEffect`` class +- remove ``patch_alpha`` and ``offset_xy`` from ``SimplePatchShadow`` + + +Remove ``testing.image_util.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contained only a no-longer used port of functionality from PIL + + +Remove ``mlab.FIFOBuffer`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Not used internally and not part of core mission of mpl. + + +Remove ``mlab.prepca`` +~~~~~~~~~~~~~~~~~~~~~~ +Deprecated in 2009. + + +Remove ``NavigationToolbar2QTAgg`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Added no functionality over the base ``NavigationToolbar2Qt`` + + +mpl.py +~~~~~~ + +Remove the module ``matplotlib.mpl``. Deprecated in 1.3 by +PR #1670 and commit 78ce67d161625833cacff23cfe5d74920248c5b2 diff --git a/doc/api/prev_api_changes/api_changes_1.5.2.rst b/doc/api/prev_api_changes/api_changes_1.5.2.rst new file mode 100644 index 000000000000..85c504fa6f12 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.5.2.rst @@ -0,0 +1,17 @@ +API Changes in 1.5.2 +==================== + + +Default Behavior Changes +------------------------ + +Changed default ``autorange`` behavior in boxplots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prior to v1.5.2, the whiskers of boxplots would extend to the minimum +and maximum values if the quartiles were all equal (i.e., Q1 = median += Q3). This behavior has been disabled by default to restore consistency +with other plotting packages. + +To restore the old behavior, simply set ``autorange=True`` when +calling ``plt.boxplot``. diff --git a/doc/api/prev_api_changes/api_changes_1.5.3.rst b/doc/api/prev_api_changes/api_changes_1.5.3.rst new file mode 100644 index 000000000000..ff5d6a9cf996 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_1.5.3.rst @@ -0,0 +1,26 @@ +API Changes in 1.5.3 +==================== + +``ax.plot(..., marker=None)`` gives default marker +-------------------------------------------------- + +Prior to 1.5.3 keyword arguments passed to `~matplotlib.axes.Axes.plot` were +handled in two parts -- default keyword arguments generated internal to +`~matplotlib.axes.Axes.plot` (such as the cycled styles) and user supplied +keyword arguments. The internally generated keyword arguments were passed to +the `matplotlib.lines.Line2D` and the user keyword arguments were passed to +``ln.set(**kwargs)`` to update the artist after it was created. Now both sets +of keyword arguments are merged and passed to `~matplotlib.lines.Line2D`. This +change was made to allow *None* to be passed in via the user keyword arguments +to mean 'do the default thing' as is the convention through out Matplotlib +rather than raising an exception. + +Unlike most `~matplotlib.lines.Line2D` setter methods +`~matplotlib.lines.Line2D.set_marker` did accept `None` as a valid +input which was mapped to 'no marker'. Thus, by routing this +``marker=None`` through ``__init__`` rather than ``set(...)`` the meaning +of ``ax.plot(..., marker=None)`` changed from 'no markers' to 'default markers +from rcparams'. + +This is change is only evident if ``mpl.rcParams['lines.marker']`` has a value +other than ``'None'`` (which is string ``'None'`` which means 'no marker'). diff --git a/doc/api/prev_api_changes/api_changes_2.0.0.rst b/doc/api/prev_api_changes/api_changes_2.0.0.rst new file mode 100644 index 000000000000..08f6a176963b --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.0.0.rst @@ -0,0 +1,205 @@ + +API Changes in 2.0.0 +==================== + +Deprecation and removal +----------------------- + +Color of Axes +~~~~~~~~~~~~~ +The ``axisbg`` and ``axis_bgcolor`` properties on *Axes* have been +deprecated in favor of ``facecolor``. + +GTK and GDK backends deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The GDK and GTK backends have been deprecated. These obsolete backends +allow figures to be rendered via the GDK API to files and GTK2 figures. +They are untested and known to be broken, and their use has been +discouraged for some time. Instead, use the ``GTKAgg`` and ``GTKCairo`` +backends for rendering to GTK2 windows. + +WX backend deprecated +~~~~~~~~~~~~~~~~~~~~~ +The WX backend has been deprecated. It is untested, and its +use has been discouraged for some time. Instead, use the ``WXAgg`` +backend for rendering figures to WX windows. + +CocoaAgg backend removed +~~~~~~~~~~~~~~~~~~~~~~~~ +The deprecated and not fully functional CocoaAgg backend has been removed. + +`round` removed from TkAgg Backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The TkAgg backend had its own implementation of the `round` function. This +was unused internally and has been removed. Instead, use either the +`round` builtin function or `numpy.around`. + +.. _v200_deprecate_hold: + +'hold' functionality deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'hold' keyword argument and all functions and methods related +to it are deprecated, along with the ``axes.hold`` rcParams entry. +The behavior will remain consistent with the default ``hold=True`` +state that has long been in place. Instead of using a function +or keyword argument (``hold=False``) to change that behavior, +explicitly clear the axes or figure as needed prior to subsequent +plotting commands. + + +`.Artist.update` has return value +--------------------------------- + +The methods `matplotlib.artist.Artist.set`, `matplotlib.artist.Artist.update`, +and the function `matplotlib.artist.setp` now use a common codepath to look up +how to update the given artist properties (either using the setter methods or +an attribute/property). + +The behavior of `matplotlib.artist.Artist.update` is slightly changed to return +a list of the values returned from the setter methods to avoid changing the API +of `matplotlib.artist.Artist.set` and `matplotlib.artist.setp`. + +The keys passed into `matplotlib.artist.Artist.update` are now converted to +lower case before being processed, to match the behavior of +`matplotlib.artist.Artist.set` and `matplotlib.artist.setp`. This should not +break any user code because there are no set methods with capitals in +their names, but this puts a constraint on naming properties in the future. + + +`.Legend` initializers gain *edgecolor* and *facecolor* keyword arguments +------------------------------------------------------------------------- + +The :class:`~matplotlib.legend.Legend` background patch (or 'frame') +can have its ``edgecolor`` and ``facecolor`` determined by the +corresponding keyword arguments to the :class:`matplotlib.legend.Legend` +initializer, or to any of the methods or functions that call that +initializer. If left to their default values of `None`, their values +will be taken from ``matplotlib.rcParams``. The previously-existing +``framealpha`` kwarg still controls the alpha transparency of the +patch. + + +Qualitative colormaps +--------------------- + +Colorbrewer's qualitative/discrete colormaps ("Accent", "Dark2", "Paired", +"Pastel1", "Pastel2", "Set1", "Set2", "Set3") are now implemented as +`.ListedColormap` instead of `.LinearSegmentedColormap`. + +To use these for images where categories are specified as integers, for +instance, use:: + + plt.imshow(x, cmap='Dark2', norm=colors.NoNorm()) + + +Change in the ``draw_image`` backend API +---------------------------------------- + +The ``draw_image`` method implemented by backends has changed its interface. + +This change is only relevant if the backend declares that it is able +to transform images by returning ``True`` from ``option_scale_image``. +See the ``draw_image`` docstring for more information. + + + +``matplotlib.ticker.LinearLocator`` algorithm update +---------------------------------------------------- + +The `matplotlib.ticker.LinearLocator` is used to define the range and +location of axis ticks when the user wants an exact number of ticks. +``LinearLocator`` thus differs from the default locator ``MaxNLocator``, +for which the user specifies a maximum number of intervals rather than +a precise number of ticks. + +The view range algorithm in ``matplotlib.ticker.LinearLocator`` has been +changed so that more convenient tick locations are chosen. The new algorithm +returns a plot view range that is a multiple of the user-requested number of +ticks. This ensures tick marks will be located at whole integers more +consistently. For example, when both y-axes of a``twinx`` plot use +``matplotlib.ticker.LinearLocator`` with the same number of ticks, +their y-tick locations and grid lines will coincide. + +`matplotlib.ticker.LogLocator` gains numticks kwarg +--------------------------------------------------- + +The maximum number of ticks generated by the +`~matplotlib.ticker.LogLocator` can now be controlled explicitly +via setting the new 'numticks' kwarg to an integer. By default +the kwarg is None which internally sets it to the 'auto' string, +triggering a new algorithm for adjusting the maximum according +to the axis length relative to the ticklabel font size. + +`matplotlib.ticker.LogFormatter`: two new kwargs +------------------------------------------------ + +Previously, minor ticks on log-scaled axes were not labeled by +default. An algorithm has been added to the +`~matplotlib.ticker.LogFormatter` to control the labeling of +ticks between integer powers of the base. The algorithm uses +two parameters supplied in a kwarg tuple named 'minor_thresholds'. +See the docstring for further explanation. + +To improve support for axes using `~matplotlib.ticker.SymmetricalLogLocator`, +a *linthresh* keyword argument was added. + + +New defaults for 3D quiver function in mpl_toolkits.mplot3d.axes3d.py +--------------------------------------------------------------------- + +Matplotlib has both a 2D and a 3D ``quiver`` function. These changes +affect only the 3D function and make the default behavior of the 3D +function match the 2D version. There are two changes: + +1) The 3D quiver function previously normalized the arrows to be the + same length, which makes it unusable for situations where the + arrows should be different lengths and does not match the behavior + of the 2D function. This normalization behavior is now controlled + with the ``normalize`` keyword, which defaults to False. + +2) The ``pivot`` keyword now defaults to ``tail`` instead of + ``tip``. This was done in order to match the default behavior of + the 2D quiver function. + +To obtain the previous behavior with the 3D quiver function, one can +call the function with :: + + ax.quiver(x, y, z, u, v, w, normalize=True, pivot='tip') + +where "ax" is an ``Axes3d`` object created with something like :: + + import mpl_toolkits.mplot3d.axes3d + ax = plt.subplot(111, projection='3d') + + +Stale figure behavior +--------------------- + +Attempting to draw the figure will now mark it as not stale (independent if +the draw succeeds). This change is to prevent repeatedly trying to re-draw a +figure which is raising an error on draw. The previous behavior would only mark +a figure as not stale after a full re-draw succeeded. + + +The spectral colormap is now nipy_spectral +------------------------------------------ + +The colormaps formerly known as ``spectral`` and ``spectral_r`` have been +replaced by ``nipy_spectral`` and ``nipy_spectral_r`` since Matplotlib +1.3.0. Even though the colormap was deprecated in Matplotlib 1.3.0, it never +raised a warning. As of Matplotlib 2.0.0, using the old names raises a +deprecation warning. In the future, using the old names will raise an error. + +Default install no longer includes test images +---------------------------------------------- + +To reduce the size of wheels and source installs, the tests and +baseline images are no longer included by default. + +To restore installing the tests and images, use a :file:`setup.cfg` with :: + + [packages] + tests = True + toolkits_tests = True + +in the source directory at build/install time. diff --git a/doc/api/prev_api_changes/api_changes_2.0.1.rst b/doc/api/prev_api_changes/api_changes_2.0.1.rst new file mode 100644 index 000000000000..57f149f6b3f7 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.0.1.rst @@ -0,0 +1,63 @@ + +API Changes in 2.0.1 +==================== + +Extensions to `matplotlib.backend_bases.GraphicsContextBase` +------------------------------------------------------------ + +To better support controlling the color of hatches, the method +`matplotlib.backend_bases.GraphicsContextBase.set_hatch_color` was +added to the expected API of ``GraphicsContext`` classes. Calls to +this method are currently wrapped with a ``try:...except Attribute:`` +block to preserve back-compatibility with any third-party backends +which do not extend `~matplotlib.backend_bases.GraphicsContextBase`. + +This value can be accessed in the backends via +`matplotlib.backend_bases.GraphicsContextBase.get_hatch_color` (which +was added in 2.0 see :ref:`gc_get_hatch_color_wn`) and should be used +to color the hatches. + +In the future there may also be ``hatch_linewidth`` and +``hatch_density`` related methods added. It is encouraged, but not +required that third-party backends extend +`~matplotlib.backend_bases.GraphicsContextBase` to make adapting to +these changes easier. + + +``afm.get_fontconfig_fonts`` returns a list of paths and does not check for existence +------------------------------------------------------------------------------------- + +``afm.get_fontconfig_fonts`` used to return a set of paths encoded as a +``{key: 1, ...}`` dict, and checked for the existence of the paths. It now +returns a list and dropped the existence check, as the same check is performed +by the caller (``afm.findSystemFonts``) as well. + + +``bar`` now returns rectangles of negative height or width if the corresponding input is negative +------------------------------------------------------------------------------------------------- + +`.pyplot.bar` used to normalize the coordinates of the rectangles that it +created, to keep their height and width positives, even if the corresponding +input was negative. This normalization has been removed to permit a simpler +computation of the correct `.Artist.sticky_edges` to use. + + +Do not clip line width when scaling dashes +------------------------------------------ + +The algorithm to scale dashes was changed to no longer clip the +scaling factor: the dash patterns now continue to shrink at thin line widths. +If the line width is smaller than the effective pixel size, this may result in +dashed lines turning into solid gray-ish lines. This also required slightly +tweaking the default patterns for '--', ':', and '.-' so that with the default +line width the final patterns would not change. + +There is no way to restore the old behavior. + + +Deprecate 'Vega' colormaps +-------------------------- + +The "Vega" colormaps are deprecated in Matplotlib 2.0.1 and will be +removed in Matplotlib 2.2. Use the "tab" colormaps instead: "tab10", +"tab20", "tab20b", "tab20c". diff --git a/doc/api/prev_api_changes/api_changes_2.1.0.rst b/doc/api/prev_api_changes/api_changes_2.1.0.rst new file mode 100644 index 000000000000..7d72d95783bb --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.1.0.rst @@ -0,0 +1,446 @@ + + +API Changes in 2.1.0 +==================== + + +Default behavior of log scales changed to mask <= 0 values +---------------------------------------------------------- + +Calling `matplotlib.axes.Axes.set_xscale` or `matplotlib.axes.Axes.set_yscale` +now uses 'mask' as the default method to handle invalid values (as opposed to +'clip'). This means that any values <= 0 on a log scale will not be shown. + +Previously they were clipped to a very small number and shown. + + +:meth:`matplotlib.cbook.CallbackRegistry.process` suppresses exceptions by default +---------------------------------------------------------------------------------- + +Matplotlib uses instances of :obj:`~matplotlib.cbook.CallbackRegistry` +as a bridge between user input event from the GUI and user callbacks. +Previously, any exceptions raised in a user call back would bubble out +of the ``process`` method, which is typically in the GUI event +loop. Most GUI frameworks simple print the traceback to the screen +and continue as there is not always a clear method of getting the +exception back to the user. However PyQt5 now exits the process when +it receives an un-handled python exception in the event loop. Thus, +:meth:`~matplotlib.cbook.CallbackRegistry.process` now suppresses and +prints tracebacks to stderr by default. + +What :meth:`~matplotlib.cbook.CallbackRegistry.process` does with exceptions +is now user configurable via the ``exception_handler`` attribute and kwarg. To +restore the previous behavior pass ``None`` :: + + cb = CallbackRegistry(exception_handler=None) + + +A function which take and ``Exception`` as its only argument may also be passed :: + + def maybe_reraise(exc): + if isinstance(exc, RuntimeError): + pass + else: + raise exc + + cb = CallbackRegistry(exception_handler=maybe_reraise) + + + +Improved toggling of the axes grids +----------------------------------- + +The ``g`` key binding now switches the states of the ``x`` and ``y`` grids +independently (by cycling through all four on/off combinations). + +The new ``G`` key binding switches the states of the minor grids. + +Both bindings are disabled if only a subset of the grid lines (in either +direction) is visible, to avoid making irreversible changes to the figure. + + +Ticklabels are turned off instead of being invisible +---------------------------------------------------- + +Internally, the `.Tick`'s ``~matplotlib.axis.Tick.label1On`` attribute +is now used to hide tick labels instead of setting the visibility on the tick +label objects. +This improves overall performance and fixes some issues. +As a consequence, in case those labels ought to be shown, +:func:`~matplotlib.axes.Axes.tick_params` +needs to be used, e.g. + +:: + + ax.tick_params(labelbottom=True) + + +Removal of warning on empty legends +----------------------------------- + +`.pyplot.legend` used to issue a warning when no labeled artist could be +found. This warning has been removed. + + +More accurate legend autopositioning +------------------------------------ + +Automatic positioning of legends now prefers using the area surrounded +by a `.Line2D` rather than placing the legend over the line itself. + + +Cleanup of stock sample data +---------------------------- + +The sample data of stocks has been cleaned up to remove redundancies and +increase portability. The ``AAPL.dat.gz``, ``INTC.dat.gz`` and ``aapl.csv`` +files have been removed entirely and will also no longer be available from +`matplotlib.cbook.get_sample_data`. If a CSV file is required, we suggest using +the ``msft.csv`` that continues to be shipped in the sample data. If a NumPy +binary file is acceptable, we suggest using one of the following two new files. +The ``aapl.npy.gz`` and ``goog.npy`` files have been replaced by ``aapl.npz`` +and ``goog.npz``, wherein the first column's type has changed from +`datetime.date` to `numpy.datetime64` for better portability across Python +versions. Note that Matplotlib does not fully support `numpy.datetime64` as +yet. + + +Updated qhull to 2015.2 +----------------------- + +The version of qhull shipped with Matplotlib, which is used for +Delaunay triangulation, has been updated from version 2012.1 to +2015.2. + +Improved Delaunay triangulations with large offsets +--------------------------------------------------- + +Delaunay triangulations now deal with large x,y offsets in a better +way. This can cause minor changes to any triangulations calculated +using Matplotlib, i.e. any use of `matplotlib.tri.Triangulation` that +requests that a Delaunay triangulation is calculated, which includes +`matplotlib.pyplot.tricontour`, `matplotlib.pyplot.tricontourf`, +`matplotlib.pyplot.tripcolor`, `matplotlib.pyplot.triplot`, +``matplotlib.mlab.griddata`` and +`mpl_toolkits.mplot3d.axes3d.Axes3D.plot_trisurf`. + + + +Use ``backports.functools_lru_cache`` instead of ``functools32`` +---------------------------------------------------------------- + +It's better maintained and more widely used (by pylint, jaraco, etc). + + + +``cbook.is_numlike`` only performs an instance check +---------------------------------------------------- + +``matplotlib.cbook.is_numlike`` now only checks that its argument +is an instance of ``(numbers.Number, np.Number)``. In particular, +this means that arrays are now not num-like. + + + +Elliptical arcs now drawn between correct angles +------------------------------------------------ + +The `matplotlib.patches.Arc` patch is now correctly drawn between the given +angles. + +Previously a circular arc was drawn and then stretched into an ellipse, +so the resulting arc did not lie between *theta1* and *theta2*. + + + +``-d$backend`` no longer sets the backend +----------------------------------------- + +It is no longer possible to set the backend by passing ``-d$backend`` +at the command line. Use the ``MPLBACKEND`` environment variable +instead. + + +Path.intersects_bbox always treats the bounding box as filled +------------------------------------------------------------- + +Previously, when ``Path.intersects_bbox`` was called with ``filled`` set to +``False``, it would treat both the path and the bounding box as unfilled. This +behavior was not well documented and it is usually not the desired behavior, +since bounding boxes are used to represent more complex shapes located inside +the bounding box. This behavior has now been changed: when ``filled`` is +``False``, the path will be treated as unfilled, but the bounding box is still +treated as filled. The old behavior was arguably an implementation bug. + +When ``Path.intersects_bbox`` is called with ``filled`` set to ``True`` +(the default value), there is no change in behavior. For those rare cases where +``Path.intersects_bbox`` was called with ``filled`` set to ``False`` and where +the old behavior is actually desired, the suggested workaround is to call +``Path.intersects_path`` with a rectangle as the path:: + + from matplotlib.path import Path + from matplotlib.transforms import Bbox, BboxTransformTo + rect = Path.unit_rectangle().transformed(BboxTransformTo(bbox)) + result = path.intersects_path(rect, filled=False) + + + + +WX no longer calls generates ``IdleEvent`` events or calls ``idle_event`` +------------------------------------------------------------------------- + +Removed unused private method ``_onIdle`` from ``FigureCanvasWx``. + +The ``IdleEvent`` class and ``FigureCanvasBase.idle_event`` method +will be removed in 2.2 + + + +Correct scaling of ``magnitude_spectrum()`` +------------------------------------------- + +The functions :func:`matplotlib.mlab.magnitude_spectrum()` and :func:`matplotlib.pyplot.magnitude_spectrum()` implicitly assumed the sum +of windowing function values to be one. In Matplotlib and Numpy the +standard windowing functions are scaled to have maximum value of one, +which usually results in a sum of the order of n/2 for a n-point +signal. Thus the amplitude scaling ``magnitude_spectrum()`` was +off by that amount when using standard windowing functions (`Bug 8417 +`_ ). Now the +behavior is consistent with :func:`matplotlib.pyplot.psd()` and +:func:`scipy.signal.welch()`. The following example demonstrates the +new and old scaling:: + + import matplotlib.pyplot as plt + import numpy as np + + tau, n = 10, 1024 # 10 second signal with 1024 points + T = tau/n # sampling interval + t = np.arange(n)*T + + a = 4 # amplitude + x = a*np.sin(40*np.pi*t) # 20 Hz sine with amplitude a + + # New correct behavior: Amplitude at 20 Hz is a/2 + plt.magnitude_spectrum(x, Fs=1/T, sides='onesided', scale='linear') + + # Original behavior: Amplitude at 20 Hz is (a/2)*(n/2) for a Hanning window + w = np.hanning(n) # default window is a Hanning window + plt.magnitude_spectrum(x*np.sum(w), Fs=1/T, sides='onesided', scale='linear') + + + + + +Change to signatures of :meth:`~matplotlib.axes.Axes.bar` & :meth:`~matplotlib.axes.Axes.barh` +---------------------------------------------------------------------------------------------- + +For 2.0 the :ref:`default value of *align* ` changed to +``'center'``. However this caused the signature of +:meth:`~matplotlib.axes.Axes.bar` and +:meth:`~matplotlib.axes.Axes.barh` to be misleading as the first parameters were +still *left* and *bottom* respectively:: + + bar(left, height, *, align='center', **kwargs) + barh(bottom, width, *, align='center', **kwargs) + +despite behaving as the center in both cases. The methods now take +``*args, **kwargs`` as input and are documented to have the primary +signatures of:: + + bar(x, height, *, align='center', **kwargs) + barh(y, width, *, align='center', **kwargs) + +Passing *left* and *bottom* as keyword arguments to +:meth:`~matplotlib.axes.Axes.bar` and +:meth:`~matplotlib.axes.Axes.barh` respectively will warn. +Support will be removed in Matplotlib 3.0. + + +Font cache as json +------------------ + +The font cache is now saved as json, rather than a pickle. + + +Invalid (Non-finite) Axis Limit Error +------------------------------------- + +When using :func:`~matplotlib.axes.Axes.set_xlim` and +:func:`~matplotlib.axes.Axes.set_ylim`, passing non-finite values now +results in a ``ValueError``. The previous behavior resulted in the +limits being erroneously reset to ``(-0.001, 0.001)``. + +``scatter`` and ``Collection`` offsets are no longer implicitly flattened +------------------------------------------------------------------------- + +`~matplotlib.collections.Collection` (and thus both 2D +`~matplotlib.axes.Axes.scatter` and 3D +`~mpl_toolkits.mplot3d.axes3d.Axes3D.scatter`) no +longer implicitly flattens its offsets. As a consequence, ``scatter``'s ``x`` +and ``y`` arguments can no longer be 2+-dimensional arrays. + +Deprecations +------------ + +``GraphicsContextBase``\'s ``linestyle`` property. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``GraphicsContextBase.get_linestyle`` and +``GraphicsContextBase.set_linestyle`` methods, which had no effect, +have been deprecated. All of the backends Matplotlib ships use +``GraphicsContextBase.get_dashes`` and +``GraphicsContextBase.set_dashes`` which are more general. +Third-party backends should also migrate to the ``*_dashes`` methods. + + +``NavigationToolbar2.dynamic_update`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use `~.FigureCanvasBase.draw_idle` method on the ``Canvas`` instance instead. + + +Testing +~~~~~~~ + +``matplotlib.testing.noseclasses`` is deprecated and will be removed in 2.3 + + +``EngFormatter`` *num* arg as string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing a string as *num* argument when calling an instance of +`matplotlib.ticker.EngFormatter` is deprecated and will be removed in 2.3. + + +``mpl_toolkits.axes_grid`` module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All functionally from ``mpl_toolkits.axes_grid`` can be found in either +`mpl_toolkits.axes_grid1` or `mpl_toolkits.axisartist`. Axes classes +from ``mpl_toolkits.axes_grid`` based on ``Axis`` from +`mpl_toolkits.axisartist` can be found in `mpl_toolkits.axisartist`. + + +``Axes`` collision in ``Figure.add_axes`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Adding an axes instance to a figure by using the same arguments as for +a previous axes instance currently reuses the earlier instance. This +behavior has been deprecated in Matplotlib 2.1. In a future version, a +*new* instance will always be created and returned. Meanwhile, in such +a situation, a deprecation warning is raised by +``matplotlib.figure.AxesStack``. + +This warning can be suppressed, and the future behavior ensured, by passing +a *unique* label to each axes instance. See the docstring of +:meth:`~matplotlib.figure.Figure.add_axes` for more information. + +Additional details on the rationale behind this deprecation can be found +in :ghissue:`7377` and :ghissue:`9024`. + + +Former validators for ``contour.negative_linestyle`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +The former public validation functions ``validate_negative_linestyle`` +and ``validate_negative_linestyle_legacy`` will be deprecated in 2.1 and +may be removed in 2.3. There are no public functions to replace them. + + + +``cbook`` +~~~~~~~~~ + +Many unused or near-unused :mod:`matplotlib.cbook` functions and +classes have been deprecated: ``converter``, ``tostr``, +``todatetime``, ``todate``, ``tofloat``, ``toint``, ``unique``, +``is_string_like``, ``is_sequence_of_strings``, ``is_scalar``, +``Sorter``, ``Xlator``, ``soundex``, ``Null``, ``dict_delall``, +``RingBuffer``, ``get_split_ind``, ``wrap``, +``get_recursive_filelist``, ``pieces``, ``exception_to_str``, +``allequal``, ``alltrue``, ``onetrue``, ``allpairs``, ``finddir``, +``reverse_dict``, ``restrict_dict``, ``issubclass_safe``, +``recursive_remove``, ``unmasked_index_ranges``. + + +Code Removal +------------ + +qt4_compat.py +~~~~~~~~~~~~~ + +Moved to ``qt_compat.py``. Renamed because it now handles Qt5 as well. + + +Previously Deprecated methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``GraphicsContextBase.set_graylevel``, ``FigureCanvasBase.onHilite`` and +``mpl_toolkits.axes_grid1.mpl_axes.Axes.toggle_axisline`` methods have been +removed. + +The ``ArtistInspector.findobj`` method, which was never working due to the lack +of a ``get_children`` method, has been removed. + +The deprecated ``point_in_path``, ``get_path_extents``, +``point_in_path_collection``, ``path_intersects_path``, +``convert_path_to_polygons``, ``cleanup_path`` and ``clip_path_to_rect`` +functions in the ``matplotlib.path`` module have been removed. Their +functionality remains exposed as methods on the ``Path`` class. + +The deprecated ``Artist.get_axes`` and ``Artist.set_axes`` methods +have been removed + + +The ``matplotlib.backends.backend_ps.seq_allequal`` function has been removed. +Use ``np.array_equal`` instead. + +The deprecated ``matplotlib.rcsetup.validate_maskedarray``, +``matplotlib.rcsetup.deprecate_savefig_extension`` and +``matplotlib.rcsetup.validate_tkpythoninspect`` functions, and associated +``savefig.extension`` and ``tk.pythoninspect`` rcparams entries have been +removed. + + +The keyword argument *resolution* of +:class:`matplotlib.projections.polar.PolarAxes` has been removed. It +has deprecation with no effect from version *0.98.x*. + + +``Axes.set_aspect("normal")`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for setting an ``Axes``\'s aspect to ``"normal"`` has been +removed, in favor of the synonym ``"auto"``. + + +``shading`` kwarg to ``pcolor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``shading`` kwarg to `~matplotlib.axes.Axes.pcolor` has been +removed. Set ``edgecolors`` appropriately instead. + + +Functions removed from the ``lines`` module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :mod:`matplotlib.lines` module no longer imports the +``pts_to_prestep``, ``pts_to_midstep`` and ``pts_to_poststep`` +functions from :mod:`matplotlib.cbook`. + + +PDF backend functions +~~~~~~~~~~~~~~~~~~~~~ + +The methods ``embedTeXFont`` and ``tex_font_mapping`` of +:class:`matplotlib.backends.backend_pdf.PdfFile` have been removed. It is +unlikely that external users would have called these methods, which +are related to the font system internal to the PDF backend. + + +matplotlib.delaunay +~~~~~~~~~~~~~~~~~~~ + +Remove the delaunay triangulation code which is now handled by Qhull +via :mod:`matplotlib.tri`. diff --git a/doc/api/prev_api_changes/api_changes_2.1.1.rst b/doc/api/prev_api_changes/api_changes_2.1.1.rst new file mode 100644 index 000000000000..39ebbb635373 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.1.1.rst @@ -0,0 +1,13 @@ +API Changes in 2.1.1 +==================== + +Default behavior of log scales reverted to clip <= 0 values +----------------------------------------------------------- + +The change it 2.1.0 to mask in logscale by default had more disruptive +changes than anticipated and has been reverted, however the clipping is now +done in a way that fixes the issues that motivated changing the default behavior +to ``'mask'``. + +As a side effect of this change, error bars which go negative now work as expected +on log scales. diff --git a/doc/api/prev_api_changes/api_changes_2.1.2.rst b/doc/api/prev_api_changes/api_changes_2.1.2.rst new file mode 100644 index 000000000000..92a72523443d --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.1.2.rst @@ -0,0 +1,23 @@ + +API Changes in 2.1.2 +==================== + +`.Figure.legend` no longer checks for repeated lines to ignore +-------------------------------------------------------------- + +`matplotlib.figure.Figure.legend` used to check if a line had the +same label as an existing legend entry. If it also had the same line color +or marker color legend didn't add a new entry for that line. However, the +list of conditions was incomplete, didn't handle RGB tuples, +didn't handle linewidths or linestyles etc. + +This logic did not exist in `.axes.Axes.legend`. It was included (erroneously) +in Matplotlib 2.1.1 when the legend argument parsing was unified :ghpull:`9324`. +This change removes that check in `.axes.Axes.legend` again to restore the old +behavior. + +This logic has also been dropped from `.Figure.legend`, where it +was previously undocumented. Repeated +lines with the same label will now each have an entry in the legend. If +you do not want the duplicate entries, don't add a label to the line, or +prepend the label with an underscore. diff --git a/doc/api/prev_api_changes/api_changes_2.2.0.rst b/doc/api/prev_api_changes/api_changes_2.2.0.rst new file mode 100644 index 000000000000..404d0ca3ba38 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_2.2.0.rst @@ -0,0 +1,286 @@ + +API Changes in 2.2.0 +==================== + + + +New dependency +-------------- + +`kiwisolver `__ is now a required +dependency to support the new constrained_layout, see +:ref:`constrainedlayout_guide` for +more details. + + +Deprecations +------------ + +Classes, functions, and methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The unused and untested ``Artist.onRemove`` and ``Artist.hitlist`` methods have +been deprecated. + +The now unused ``mlab.less_simple_linear_interpolation`` function is +deprecated. + +The unused ``ContourLabeler.get_real_label_width`` method is deprecated. + +The unused ``FigureManagerBase.show_popup`` method is deprecated. This +introduced in e945059b327d42a99938b939a1be867fa023e7ba in 2005 but never built +out into any of the backends. + +``backend_tkagg.AxisMenu`` is deprecated, as it has become unused since the +removal of "classic" toolbars. + + +Changed function signatures +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +kwarg ``fig`` to `.GridSpec.get_subplot_params` is +deprecated, use ``figure`` instead. + +Using `.pyplot.axes` with an `~matplotlib.axes.Axes` as argument is deprecated. This sets +the current axes, i.e. it has the same effect as `.pyplot.sca`. For clarity +``plt.sca(ax)`` should be preferred over ``plt.axes(ax)``. + + +Using strings instead of booleans to control grid and tick visibility +is deprecated. Using ``"on"``, ``"off"``, ``"true"``, or ``"false"`` +to control grid and tick visibility has been deprecated. Instead, use +normal booleans (``True``/``False``) or boolean-likes. In the future, +all non-empty strings may be interpreted as ``True``. + +When given 2D inputs with non-matching numbers of columns, `~.pyplot.plot` +currently cycles through the columns of the narrower input, until all the +columns of the wider input have been plotted. This behavior is deprecated; in +the future, only broadcasting (1 column to *n* columns) will be performed. + + +rcparams +~~~~~~~~ + +The ``backend.qt4`` and ``backend.qt5`` rcParams were deprecated +in version 2.2. In order to force the use of a specific Qt binding, +either import that binding first, or set the ``QT_API`` environment +variable. + +Deprecation of the ``nbagg.transparent`` rcParam. To control +transparency of figure patches in the nbagg (or any other) backend, +directly set ``figure.patch.facecolor``, or the ``figure.facecolor`` +rcParam. + +Deprecated ``Axis.unit_data`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use ``Axis.units`` (which has long existed) instead. + + +Removals +-------- + +Function Signatures +~~~~~~~~~~~~~~~~~~~ + +Contouring no longer supports ``legacy`` corner masking. The +deprecated ``ContourSet.vmin`` and ``ContourSet.vmax`` properties have +been removed. + +Passing ``None`` instead of ``"none"`` as format to `~.Axes.errorbar` is no +longer supported. + +The ``bgcolor`` keyword argument to ``Axes`` has been removed. + +Modules, methods, and functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``matplotlib.finance``, ``mpl_toolkits.exceltools`` and +``mpl_toolkits.gtktools`` modules have been removed. ``matplotlib.finance`` +remains available at https://github.com/matplotlib/mpl_finance. + +The ``mpl_toolkits.mplot3d.art3d.iscolor`` function has been removed. + +The ``Axes.get_axis_bgcolor``, ``Axes.set_axis_bgcolor``, +``Bbox.update_from_data``, ``Bbox.update_datalim_numerix``, +``MaxNLocator.bin_boundaries`` methods have been removed. + +``mencoder`` can no longer be used to encode animations. + +The unused ``FONT_SCALE`` and ``fontd`` attributes of the `.RendererSVG` +class have been removed. + +colormaps +~~~~~~~~~ + +The ``spectral`` colormap has been removed. The ``Vega*`` colormaps, which +were aliases for the ``tab*`` colormaps, have been removed. + + +rcparams +~~~~~~~~ + +The following deprecated rcParams have been removed: + +- ``axes.color_cycle`` (see ``axes.prop_cycle``), +- ``legend.isaxes``, +- ``svg.embed_char_paths`` (see ``svg.fonttype``), +- ``text.fontstyle``, ``text.fontangle``, ``text.fontvariant``, + ``text.fontweight``, ``text.fontsize`` (renamed to ``text.style``, etc.), +- ``tick.size`` (renamed to ``tick.major.size``). + + + +Only accept string-like for Categorical input +--------------------------------------------- + +Do not accept mixed string / float / int input, only +strings are valid categoricals. + +Removal of unused imports +------------------------- +Many unused imports were removed from the codebase. As a result, +trying to import certain classes or functions from the "wrong" module +(e.g. `~.Figure` from :mod:`matplotlib.backends.backend_agg` instead of +:mod:`matplotlib.figure`) will now raise an `ImportError`. + + +``Axes3D.get_xlim``, ``get_ylim`` and ``get_zlim`` now return a tuple +--------------------------------------------------------------------- + +They previously returned an array. Returning a tuple is consistent with the +behavior for 2D axes. + + +Exception type changes +---------------------- + +If `.MovieWriterRegistry` can't find the requested `.MovieWriter`, a +more helpful `RuntimeError` message is now raised instead of the +previously raised `KeyError`. + +``matplotlib.tight_layout.auto_adjust_subplotpars`` now raises `ValueError` +instead of `RuntimeError` when sizes of input lists don't match + + +`.Figure.set_figwidth` and `.Figure.set_figheight` default *forward* to True +---------------------------------------------------------------------------- + +`matplotlib.figure.Figure.set_figwidth` and +`matplotlib.figure.Figure.set_figheight` had the keyword argument +``forward=False`` by default, but `.figure.Figure.set_size_inches` now defaults +to ``forward=True``. This makes these functions consistent. + + +Do not truncate svg sizes to nearest point +------------------------------------------ + +There is no reason to size the SVG out put in integer points, change +to out putting floats for the *height*, *width*, and *viewBox* attributes +of the *svg* element. + + +Fontsizes less than 1 pt are clipped to be 1 pt. +------------------------------------------------ + +FreeType doesn't allow fonts to get smaller than 1 pt, so all Agg +backends were silently rounding up to 1 pt. PDF (other vector +backends?) were letting us write fonts that were less than 1 pt, but +they could not be placed properly because position information comes from +FreeType. This change makes it so no backends can use fonts smaller than +1 pt, consistent with FreeType and ensuring more consistent results across +backends. + + + +Changes to Qt backend class MRO +------------------------------- + +To support both Agg and cairo rendering for Qt backends all of the non-Agg +specific code previously in ``backend_qt5agg.FigureCanvasQTAggBase`` has been +moved to ``backend_qt5.FigureCanvasQT`` so it can be shared with the +cairo implementation. The ``FigureCanvasQTAggBase.paintEvent``, +``FigureCanvasQTAggBase.blit``, and ``FigureCanvasQTAggBase.print_figure`` +methods have moved to ``FigureCanvasQTAgg.paintEvent``, +``FigureCanvasQTAgg.blit``, and ``FigureCanvasQTAgg.print_figure``. +The first two methods assume that the instance is also a ``QWidget`` so to use +``FigureCanvasQTAggBase`` it was required to multiple inherit from a +``QWidget`` sub-class. + +Having moved all of its methods either up or down the class hierarchy +``FigureCanvasQTAggBase`` has been deprecated. To do this without warning and +to preserve as much API as possible, ``.backend_qt5agg.FigureCanvasQTAggBase`` +now inherits from ``backend_qt5.FigureCanvasQTAgg``. + +The MRO for ``FigureCanvasQTAgg`` and ``FigureCanvasQTAggBase`` used to +be :: + + + [matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg, + matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase, + matplotlib.backends.backend_agg.FigureCanvasAgg, + matplotlib.backends.backend_qt5.FigureCanvasQT, + PyQt5.QtWidgets.QWidget, + PyQt5.QtCore.QObject, + sip.wrapper, + PyQt5.QtGui.QPaintDevice, + sip.simplewrapper, + matplotlib.backend_bases.FigureCanvasBase, + object] + +and :: + + + [matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase, + matplotlib.backends.backend_agg.FigureCanvasAgg, + matplotlib.backend_bases.FigureCanvasBase, + object] + + +respectively. They are now :: + + [matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg, + matplotlib.backends.backend_agg.FigureCanvasAgg, + matplotlib.backends.backend_qt5.FigureCanvasQT, + PyQt5.QtWidgets.QWidget, + PyQt5.QtCore.QObject, + sip.wrapper, + PyQt5.QtGui.QPaintDevice, + sip.simplewrapper, + matplotlib.backend_bases.FigureCanvasBase, + object] + +and :: + + [matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase, + matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg, + matplotlib.backends.backend_agg.FigureCanvasAgg, + matplotlib.backends.backend_qt5.FigureCanvasQT, + PyQt5.QtWidgets.QWidget, + PyQt5.QtCore.QObject, + sip.wrapper, + PyQt5.QtGui.QPaintDevice, + sip.simplewrapper, + matplotlib.backend_bases.FigureCanvasBase, + object] + + + + +`.axes.Axes.imshow` clips RGB values to the valid range +------------------------------------------------------- + +When `.axes.Axes.imshow` is passed an RGB or RGBA value with out-of-range +values, it now logs a warning and clips them to the valid range. +The old behaviour, wrapping back in to the range, often hid outliers +and made interpreting RGB images unreliable. + + +GTKAgg and GTKCairo backends deprecated +--------------------------------------- + +The GTKAgg and GTKCairo backends have been deprecated. These obsolete backends +allow figures to be rendered via the GTK+ 2 toolkit. They are untested, known +to be broken, will not work with Python 3, and their use has been discouraged +for some time. Instead, use the ``GTK3Agg`` and ``GTK3Cairo`` backends for +rendering to GTK+ 3 windows. diff --git a/doc/api/prev_api_changes/api_changes_3.0.0.rst b/doc/api/prev_api_changes/api_changes_3.0.0.rst new file mode 100644 index 000000000000..c24e1a312f4d --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.0.0.rst @@ -0,0 +1,559 @@ +API Changes for 3.0.0 +===================== + +Drop support for python 2 +------------------------- + +Matplotlib 3 only supports python 3.5 and higher. + + +Changes to backend loading +-------------------------- + +Failure to load backend modules (``macosx`` on non-framework builds and +``gtk3`` when running headless) now raises `ImportError` (instead of +`RuntimeError` and `TypeError`, respectively). + +Third-party backends that integrate with an interactive framework are now +encouraged to define the ``required_interactive_framework`` global value to one +of the following values: "qt5", "qt4", "gtk3", "wx", "tk", or "macosx". This +information will be used to determine whether it is possible to switch from a +backend to another (specifically, whether they use the same interactive +framework). + + + +`.Axes.hist2d` now uses `~.Axes.pcolormesh` instead of `~.Axes.pcolorfast` +-------------------------------------------------------------------------- + +`.Axes.hist2d` now uses `~.Axes.pcolormesh` instead of `~.Axes.pcolorfast`, +which will improve the handling of log-axes. Note that the +returned *image* now is of type `~.matplotlib.collections.QuadMesh` +instead of `~.matplotlib.image.AxesImage`. + +`.matplotlib.axes.Axes.get_tightbbox` now includes all artists +-------------------------------------------------------------- + +For Matplotlib 3.0, *all* artists are now included in the bounding box +returned by `.matplotlib.axes.Axes.get_tightbbox`. + +`.matplotlib.axes.Axes.get_tightbbox` adds a new kwarg ``bbox_extra_artists`` +to manually specify the list of artists on the axes to include in the +tight bounding box calculation. + +Layout tools like `.Figure.tight_layout`, ``constrained_layout``, +and ``fig.savefig('fname.png', bbox_inches="tight")`` use +`.matplotlib.axes.Axes.get_tightbbox` to determine the bounds of each axes on +a figure and adjust spacing between axes. + +In Matplotlib 2.2 ``get_tightbbox`` started to include legends made on the +axes, but still excluded some other artists, like text that may overspill an +axes. This has been expanded to include *all* artists. + +This new default may be overridden in either of three ways: + +1. Make the artist to be excluded a child of the figure, not the axes. E.g., + call ``fig.legend()`` instead of ``ax.legend()`` (perhaps using + `~.matplotlib.axes.Axes.get_legend_handles_labels` to gather handles and + labels from the parent axes). +2. If the artist is a child of the axes, set the artist property + ``artist.set_in_layout(False)``. +3. Manually specify a list of artists in the new kwarg ``bbox_extra_artists``. + + +`.Text.set_text` with string argument ``None`` sets string to empty +------------------------------------------------------------------- + +`.Text.set_text` when passed a string value of ``None`` would set the +string to ``"None"``, so subsequent calls to `.Text.get_text` would return +the ambiguous ``"None"`` string. + +This change sets text objects passed ``None`` to have empty strings, so that +`.Text.get_text` returns an empty string. + + + + +``Axes3D.get_xlim``, ``get_ylim`` and ``get_zlim`` now return a tuple +--------------------------------------------------------------------- + +They previously returned an array. Returning a tuple is consistent with the +behavior for 2D axes. + + + + +``font_manager.list_fonts`` now follows the platform's casefolding semantics +---------------------------------------------------------------------------- + +i.e., it behaves case-insensitively on Windows only. + + +``bar`` / ``barh`` no longer accepts ``left`` / ``bottom`` as first named argument +---------------------------------------------------------------------------------- + +These arguments were renamed in 2.0 to ``x`` / ``y`` following the change of the +default alignment from ``edge`` to ``center``. + + +Different exception types for undocumented options +-------------------------------------------------- + +- Passing ``style='comma'`` to :meth:`~matplotlib.axes.Axes.ticklabel_format` + was never supported. It now raises ``ValueError`` like all other + unsupported styles, rather than ``NotImplementedError``. + +- Passing the undocumented ``xmin`` or ``xmax`` arguments to + :meth:`~matplotlib.axes.Axes.set_xlim` would silently override the ``left`` + and ``right`` arguments. :meth:`~matplotlib.axes.Axes.set_ylim` and the + 3D equivalents (e.g. `~.Axes3D.set_zlim`) had a + corresponding problem. + A ``TypeError`` will be raised if they would override the earlier + limit arguments. In 3.0 these were kwargs were deprecated, but in 3.1 + the deprecation was undone. + + +Improved call signature for ``Axes.margins`` +-------------------------------------------- + +`.Axes.margins` and `.Axes3D.margins` +no longer accept arbitrary keywords. ``TypeError`` will therefore be raised +if unknown kwargs are passed; previously they would be silently ignored. + +If too many positional arguments are passed, ``TypeError`` will be raised +instead of ``ValueError``, for consistency with other call-signature violations. + +`.Axes3D.margins` now raises ``TypeError`` instead of emitting a deprecation +warning if only two positional arguments are passed. To supply only ``x`` and +``y`` margins, use keyword arguments. + + + +Explicit arguments instead of \*args, \*\*kwargs +------------------------------------------------ + +:PEP:`3102` describes keyword-only arguments, which allow Matplotlib +to provide explicit call signatures - where we previously used +``*args, **kwargs`` and ``kwargs.pop``, we can now expose named +arguments. In some places, unknown kwargs were previously ignored but +now raise ``TypeError`` because ``**kwargs`` has been removed. + +- :meth:`matplotlib.axes.Axes.stem` no longer accepts unknown keywords, + and raises ``TypeError`` instead of emitting a deprecation. +- :meth:`matplotlib.axes.Axes.stem` now raises TypeError when passed + unhandled positional arguments. If two or more arguments are passed + (ie X, Y, [linefmt], ...) and Y cannot be cast to an array, an error + will be raised instead of treating X as Y and Y as linefmt. +- `mpl_toolkits.axes_grid1.axes_divider.SubplotDivider` raises + ``TypeError`` instead of ``Exception`` when passed unknown kwargs. + + + +Cleanup decorators and test classes no longer destroy warnings filter on exit +----------------------------------------------------------------------------- + +The decorators and classes in matplotlib.testing.decorators no longer +destroy the warnings filter on exit. Instead, they restore the warnings +filter that existed before the test started using ``warnings.catch_warnings``. + + +Non-interactive FigureManager classes are now aliases of FigureManagerBase +-------------------------------------------------------------------------- + +The ``FigureManagerPdf``, ``FigureManagerPS``, and ``FigureManagerSVG`` classes, +which were previously empty subclasses of `.FigureManagerBase` (i.e., not +adding or overriding any attribute or method), are now direct aliases for +`.FigureManagerBase`. + + +Change to the output of `.image.thumbnail` +------------------------------------------ + +When called with ``preview=False``, `.image.thumbnail` previously returned an +figure whose canvas class was set according to the output file extension. It +now returns a figure whose canvas class is the base `.FigureCanvasBase` (and +relies on `.FigureCanvasBase.print_figure`) to handle the canvas switching +properly). + +As a side effect of this change, `.image.thumbnail` now also supports .ps, .eps, +and .svgz output. + + + +`.FuncAnimation` now draws artists according to their zorder when blitting +-------------------------------------------------------------------------- + +`.FuncAnimation` now draws artists returned by the user- +function according to their zorder when using blitting, +instead of using the order in which they are being passed. +However, note that only zorder of passed artists will be +respected, as they are drawn on top of any existing artists +(see `#11369 `_). + + +Contour color autoscaling improvements +-------------------------------------- + +Selection of contour levels is now the same for contour and +contourf; previously, for contour, levels outside the data range were +deleted. (Exception: if no contour levels are found within the +data range, the ``levels`` attribute is replaced with a list holding +only the minimum of the data range.) + +When contour is called with levels specified as a target number rather +than a list, and the 'extend' kwarg is used, the levels are now chosen +such that some data typically will fall in the extended range. + +When contour is called with a `.LogNorm` or a `.LogLocator`, it will now +select colors using the geometric mean rather than the arithmetic mean +of the contour levels. + + +Streamplot last row and column fixed +------------------------------------ + +A bug was fixed where the last row and column of data in +`~.Axes.streamplot` were being dropped. + + +Changed default `.AutoDateLocator` kwarg *interval_multiples* to ``True`` +------------------------------------------------------------------------- + +The default value of the tick locator for dates, `.dates.AutoDateLocator` +kwarg *interval_multiples* was set to ``False`` which leads to not-nice +looking automatic ticks in many instances. The much nicer +``interval_multiples=True`` is the new default. See below to get the +old behavior back: + +.. plot:: + + import matplotlib.pyplot as plt + import datetime + import matplotlib.dates as mdates + + t0 = datetime.datetime(2009, 8, 20, 1, 10, 12) + tf = datetime.datetime(2009, 8, 20, 1, 42, 11) + + + fig, axs = plt.subplots(1, 2, constrained_layout=True) + ax = axs[0] + ax.axhspan(t0, tf, facecolor="blue", alpha=0.25) + ax.set_ylim(t0 - datetime.timedelta(minutes=3), + tf + datetime.timedelta(minutes=3)) + ax.set_title('NEW DEFAULT') + + ax = axs[1] + ax.axhspan(t0, tf, facecolor="blue", alpha=0.25) + ax.set_ylim(t0 - datetime.timedelta(minutes=3), + tf + datetime.timedelta(minutes=3)) + # old behavior + locator = mdates.AutoDateLocator(interval_multiples=False, ) + ax.yaxis.set_major_locator(locator) + ax.yaxis.set_major_formatter(mdates.AutoDateFormatter(locator)) + + ax.set_title('OLD') + plt.show() + + +`.Axes.get_position` now returns actual position if aspect changed +------------------------------------------------------------------ + +`.Axes.get_position` used to return the original position unless a +draw had been triggered or `.Axes.apply_aspect` had been called, even +if the kwarg *original* was set to ``False``. Now `.Axes.apply_aspect` +is called so ``ax.get_position()`` will return the new modified position. +To get the old behavior use ``ax.get_position(original=True)``. + + +The ticks for colorbar now adjust for the size of the colorbar +-------------------------------------------------------------- + +Colorbar ticks now adjust for the size of the colorbar if the +colorbar is made from a mappable that is not a contour or +doesn't have a BoundaryNorm, or boundaries are not specified. +If boundaries, etc are specified, the colorbar maintains the +original behavior. + + +Colorbar for log-scaled hexbin +------------------------------ + +When using `~.Axes.hexbin` and plotting with a logarithmic color scale, the colorbar +ticks are now correctly log scaled. Previously the tick values were linear +scaled log(number of counts). + +PGF backend now explicitly makes black text black +------------------------------------------------- + +Previous behavior with the pgf backend was for text specified as black to +actually be the default color of whatever was rendering the pgf file (which was +of course usually black). The new behavior is that black text is black, +regardless of the default color. However, this means that there is no way to +fall back on the default color of the renderer. + + +Blacklisted rcparams no longer updated by `~matplotlib.rcdefaults`, `~matplotlib.rc_file_defaults`, `~matplotlib.rc_file` +------------------------------------------------------------------------------------------------------------------------- + +The rc modifier functions `~matplotlib.rcdefaults`, +`~matplotlib.rc_file_defaults` and `~matplotlib.rc_file` +now ignore rcParams in the ``matplotlib.style.core.STYLE_BLACKLIST`` set. In +particular, this prevents the ``backend`` and ``interactive`` rcParams from +being incorrectly modified by these functions. + + + +`.CallbackRegistry` now stores callbacks using stdlib's `weakref.WeakMethod`\s +------------------------------------------------------------------------------ + +In particular, this implies that ``CallbackRegistry.callbacks[signal]`` is now +a mapping of callback ids to `weakref.WeakMethod`\s (i.e., they need to be first called +with no arguments to retrieve the method itself). + + +Changes regarding the text.latex.unicode rcParam +------------------------------------------------ + +The rcParam now defaults to True and is deprecated (i.e., in future versions +of Matplotlib, unicode input will always be supported). + +Moreover, the underlying implementation now uses ``\usepackage[utf8]{inputenc}`` +instead of ``\usepackage{ucs}\usepackage[utf8x]{inputenc}``. + + +Return type of ArtistInspector.get_aliases changed +-------------------------------------------------- + +``ArtistInspector.get_aliases`` previously returned the set of aliases as +``{fullname: {alias1: None, alias2: None, ...}}``. The dict-to-None mapping +was used to simulate a set in earlier versions of Python. It has now been +replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. + +This value is also stored in ``ArtistInspector.aliasd``, which has likewise +changed. + + +Removed ``pytz`` as a dependency +-------------------------------- + +Since ``dateutil`` and ``pytz`` both provide time zones, and +matplotlib already depends on ``dateutil``, matplotlib will now use +``dateutil`` time zones internally and drop the redundant dependency +on ``pytz``. While ``dateutil`` time zones are preferred (and +currently recommended in the Python documentation), the explicit use +of ``pytz`` zones is still supported. + +Deprecations +------------ + +Modules +``````` +The following modules are deprecated: + +- ``matplotlib.compat.subprocess``. This was a python 2 workaround, but all + the functionality can now be found in the python 3 standard library + :mod:`subprocess`. +- ``matplotlib.backends.wx_compat``. Python 3 is only compatible with + wxPython 4, so support for wxPython 3 or earlier can be dropped. + +Classes, methods, functions, and attributes +``````````````````````````````````````````` + +The following classes, methods, functions, and attributes are deprecated: + +- ``RcParams.msg_depr``, ``RcParams.msg_depr_ignore``, + ``RcParams.msg_depr_set``, ``RcParams.msg_obsolete``, + ``RcParams.msg_backend_obsolete`` +- ``afm.parse_afm`` +- ``backend_pdf.PdfFile.texFontMap`` +- ``backend_pgf.get_texcommand`` +- ``backend_ps.get_bbox`` +- ``backend_qt5.FigureCanvasQT.keyAutoRepeat`` (directly check + ``event.guiEvent.isAutoRepeat()`` in the event handler to decide whether to + handle autorepeated key presses). +- ``backend_qt5.error_msg_qt``, ``backend_qt5.exception_handler`` +- ``backend_wx.FigureCanvasWx.macros`` +- ``backends.pylab_setup`` +- ``cbook.GetRealpathAndStat``, ``cbook.Locked`` +- ``cbook.is_numlike`` (use ``isinstance(..., numbers.Number)`` instead), + ``cbook.listFiles``, ``cbook.unicode_safe`` +- ``container.Container.set_remove_method``, +- ``contour.ContourLabeler.cl``, ``.cl_xy``, and ``.cl_cvalues`` +- ``dates.DateFormatter.strftime_pre_1900``, ``dates.DateFormatter.strftime`` +- ``font_manager.TempCache`` +- ``image._ImageBase.iterpnames``, use the ``interpolation_names`` property + instead. (this affects classes that inherit from ``_ImageBase`` including + `.FigureImage`, `.BboxImage`, and `.AxesImage`) +- ``mathtext.unichr_safe`` (use ``chr`` instead) +- ``patches.Polygon.xy`` +- ``table.Table.get_child_artists`` (use ``get_children`` instead) +- ``testing.compare.ImageComparisonTest``, ``testing.compare.compare_float`` +- ``testing.decorators.CleanupTest``, + ``testing.decorators.skip_if_command_unavailable`` +- ``FigureCanvasQT.keyAutoRepeat`` (directly check + ``event.guiEvent.isAutoRepeat()`` in the event handler to decide whether to + handle autorepeated key presses) +- ``FigureCanvasWx.macros`` +- ``_ImageBase.iterpnames``, use the ``interpolation_names`` property instead. + (this affects classes that inherit from ``_ImageBase`` including + `.FigureImage`, `.BboxImage`, and `.AxesImage`) +- ``patches.Polygon.xy`` +- ``texmanager.dvipng_hack_alpha`` +- ``text.Annotation.arrow`` +- ``Legend.draggable()``, in favor of `.Legend.set_draggable()` + (``Legend.draggable`` may be reintroduced as a property in future releases) +- ``textpath.TextToPath.tex_font_map`` +- ``matplotlib.cbook.deprecation.mplDeprecation`` will be removed + in future versions. It is just an alias for + ``matplotlib.cbook.deprecation.MatplotlibDeprecationWarning``. Please + use ``matplotlib.cbook.MatplotlibDeprecationWarning`` directly if necessary. +- The ``matplotlib.cbook.Bunch`` class has been deprecated. Instead, use + `types.SimpleNamespace` from the standard library which provides the same + functionality. +- ``Axes.mouseover_set`` is now a frozenset, and deprecated. Directly + manipulate the artist's ``.mouseover`` attribute to change their mouseover + status. + +The following keyword arguments are deprecated: + +- passing ``verts`` to ``Axes.scatter`` (use ``marker`` instead) +- passing ``obj_type`` to ``cbook.deprecated`` + +The following call signatures are deprecated: + +- passing a ``wx.EvtHandler`` as first argument to ``backend_wx.TimerWx`` + + +rcParams +```````` + +The following rcParams are deprecated: + +- ``examples.directory`` (use ``datapath`` instead) +- ``pgf.debug`` (the pgf backend relies on logging) +- ``text.latex.unicode`` (always True now) + + +marker styles +````````````` +- Using ``(n, 3)`` as marker style to specify a circle marker is deprecated. Use + ``"o"`` instead. +- Using ``([(x0, y0), (x1, y1), ...], 0)`` as marker style to specify a custom + marker path is deprecated. Use ``[(x0, y0), (x1, y1), ...]`` instead. + + +Deprecation of ``LocatableAxes`` in toolkits +```````````````````````````````````````````` + +The ``LocatableAxes`` classes in toolkits have been deprecated. The base `~.axes.Axes` +classes provide the same functionality to all subclasses, thus these mixins are +no longer necessary. Related functions have also been deprecated. Specifically: + +* ``mpl_toolkits.axes_grid1.axes_divider.LocatableAxesBase``: no specific + replacement; use any other ``Axes``-derived class directly instead. +* ``mpl_toolkits.axes_grid1.axes_divider.locatable_axes_factory``: no specific + replacement; use any other ``Axes``-derived class directly instead. +* ``mpl_toolkits.axes_grid1.axes_divider.Axes``: use + `mpl_toolkits.axes_grid1.mpl_axes.Axes` directly. +* ``mpl_toolkits.axes_grid1.axes_divider.LocatableAxes``: use + `mpl_toolkits.axes_grid1.mpl_axes.Axes` directly. +* ``mpl_toolkits.axisartist.axes_divider.Axes``: use + `mpl_toolkits.axisartist.axislines.Axes` directly. +* ``mpl_toolkits.axisartist.axes_divider.LocatableAxes``: use + `mpl_toolkits.axisartist.axislines.Axes` directly. + +Removals +-------- + +Hold machinery +`````````````` + +Setting or unsetting ``hold`` (:ref:`deprecated in version 2.0`) has now +been completely removed. Matplotlib now always behaves as if ``hold=True``. +To clear an axes you can manually use :meth:`~.axes.Axes.cla()`, +or to clear an entire figure use :meth:`~.figure.Figure.clear()`. + + +Removal of deprecated backends +`````````````````````````````` + +Deprecated backends have been removed: + +- GTKAgg +- GTKCairo +- GTK +- GDK + + +Deprecated APIs +``````````````` + +The following deprecated API elements have been removed: + +- The deprecated methods ``knownfailureif`` and ``remove_text`` have been removed + from :mod:`matplotlib.testing.decorators`. +- The entire contents of ``testing.noseclasses`` have also been removed. +- ``matplotlib.checkdep_tex``, ``matplotlib.checkdep_xmllint`` +- ``backend_bases.IdleEvent`` +- ``cbook.converter``, ``cbook.tostr``, ``cbook.todatetime``, ``cbook.todate``, + ``cbook.tofloat``, ``cbook.toint``, ``cbook.unique``, + ``cbook.is_string_like``, ``cbook.is_sequence_of_strings``, + ``cbook.is_scalar``, ``cbook.soundex``, ``cbook.dict_delall``, + ``cbook.get_split_ind``, ``cbook.wrap``, ``cbook.get_recursive_filelist``, + ``cbook.pieces``, ``cbook.exception_to_str``, ``cbook.allequal``, + ``cbook.alltrue``, ``cbook.onetrue``, ``cbook.allpairs``, ``cbook.finddir``, + ``cbook.reverse_dict``, ``cbook.restrict_dict``, ``cbook.issubclass_safe``, + ``cbook.recursive_remove``, ``cbook.unmasked_index_ranges``, + ``cbook.Null``, ``cbook.RingBuffer``, ``cbook.Sorter``, ``cbook.Xlator``, +- ``font_manager.weight_as_number``, ``font_manager.ttfdict_to_fnames`` +- ``pyplot.colors``, ``pyplot.spectral`` +- ``rcsetup.validate_negative_linestyle``, + ``rcsetup.validate_negative_linestyle_legacy``, +- ``testing.compare.verifiers``, ``testing.compare.verify`` +- ``testing.decorators.knownfailureif``, + ``testing.decorators.ImageComparisonTest.remove_text`` +- ``tests.assert_str_equal``, ``tests.test_tinypages.file_same`` +- ``texmanager.dvipng_hack_alpha``, +- ``_AxesBase.axesPatch``, ``_AxesBase.set_color_cycle``, + ``_AxesBase.get_cursor_props``, ``_AxesBase.set_cursor_props`` +- ``_ImageBase.iterpnames`` +- ``FigureCanvasBase.start_event_loop_default``; +- ``FigureCanvasBase.stop_event_loop_default``; +- ``Figure.figurePatch``, +- ``FigureCanvasBase.dynamic_update``, ``FigureCanvasBase.idle_event``, + ``FigureCanvasBase.get_linestyle``, ``FigureCanvasBase.set_linestyle`` +- ``FigureCanvasQTAggBase`` +- ``FigureCanvasQTAgg.blitbox`` +- ``FigureCanvasTk.show`` (alternative: ``FigureCanvasTk.draw``) +- ``FigureManagerTkAgg`` (alternative: ``FigureManagerTk``) +- ``NavigationToolbar2TkAgg`` (alternative: ``NavigationToolbar2Tk``) +- ``backend_wxagg.Toolbar`` (alternative: ``backend_wxagg.NavigationToolbar2WxAgg``) +- ``RendererAgg.debug()`` +- passing non-numbers to ``EngFormatter.format_eng`` +- passing ``frac`` to ``PolarAxes.set_theta_grids`` +- any mention of idle events + +The following API elements have been removed: + +- ``backend_cairo.HAS_CAIRO_CFFI`` +- ``sphinxext.sphinx_version`` + + +Proprietary sphinx directives +````````````````````````````` + +The matplotlib documentation used the proprietary sphinx directives +``.. htmlonly::``, and ``.. latexonly::``. These have been replaced with the +standard sphinx directives ``.. only:: html`` and ``.. only:: latex``. This +change will not affect any users. Only downstream package maintainers, who +have used the proprietary directives in their docs, will have to switch to the +sphinx directives. + + +lib/mpl_examples symlink +```````````````````````` + +The symlink from lib/mpl_examples to ../examples has been removed. +This is not installed as an importable package and should not affect +end users, however this may require down-stream packagers to adjust. +The content is still available top-level examples directory. diff --git a/doc/api/prev_api_changes/api_changes_3.0.1.rst b/doc/api/prev_api_changes/api_changes_3.0.1.rst new file mode 100644 index 000000000000..4b203cd04596 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.0.1.rst @@ -0,0 +1,21 @@ +API Changes for 3.0.1 +===================== + +``matplotlib.tight_layout.auto_adjust_subplotpars`` can return ``None`` now if +the new subplotparams will collapse axes to zero width or height. +This prevents ``tight_layout`` from being executed. Similarly +``matplotlib.tight_layout.get_tight_layout_figure`` will return None. + +To improve import (startup) time, private modules are now imported lazily. +These modules are no longer available at these locations: + +- ``matplotlib.backends.backend_agg._png`` +- ``matplotlib.contour._contour`` +- ``matplotlib.image._png`` +- ``matplotlib.mathtext._png`` +- ``matplotlib.testing.compare._png`` +- ``matplotlib.texmanager._png`` +- ``matplotlib.tri.triangulation._tri`` +- ``matplotlib.tri.triangulation._qhull`` +- ``matplotlib.tri.tricontour._tri`` +- ``matplotlib.tri.trifinder._tri`` diff --git a/doc/api/prev_api_changes/api_changes_3.1.0.rst b/doc/api/prev_api_changes/api_changes_3.1.0.rst new file mode 100644 index 000000000000..d5b2a1369cf1 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.1.0.rst @@ -0,0 +1,1159 @@ +API Changes for 3.1.0 +===================== + +.. contents:: + :local: + :depth: 1 + + +Behavior changes +---------------- + + +Matplotlib.use +~~~~~~~~~~~~~~ +Switching backends via `matplotlib.use` is now allowed by default, +regardless of whether `matplotlib.pyplot` has been imported. If the user +tries to switch from an already-started interactive backend to a different +interactive backend, an `ImportError` will be raised. + +Invalid points in PathCollections +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +PathCollections created with `~.Axes.scatter` now keep track of invalid points. +Previously, points with nonfinite (infinite or nan) coordinates would not be +included in the offsets (as returned by `.PathCollection.get_offsets`) of a +`.PathCollection` created by `~.Axes.scatter`, and points with nonfinite values +(as specified by the *c* kwarg) would not be included in the array (as returned +by `.PathCollection.get_array`) + +Such points are now included, but masked out by returning a masked array. + +If the *plotnonfinite* kwarg to `~.Axes.scatter` is set, then points +with nonfinite values are plotted using the bad color of the +`.collections.PathCollection`\ 's colormap (as set by +:meth:`.colors.Colormap.set_bad`). + +Alpha blending in imshow of RBGA input +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The alpha-channel of RBGA images is now re-sampled independently of +RGB channels. While this is a bug fix, it does change the output and +may result in some down-stream image comparison tests to fail. + +Autoscaling +~~~~~~~~~~~ +On log-axes where a single value is plotted at a "full" decade (1, 10, 100, +etc.), the autoscaling now expands the axis symmetrically around that point, +instead of adding a decade only to the right. + +Log-scaled axes +~~~~~~~~~~~~~~~ +When the default `.LogLocator` would generate no ticks for an axis (e.g., an +axis with limits from 0.31 to 0.39) or only a single tick, it now instead falls +back on the linear `.AutoLocator` to pick reasonable tick positions. + +`.Figure.add_subplot` with no arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Calling `.Figure.add_subplot()` with no positional arguments used to do +nothing; this now is equivalent to calling ``add_subplot(111)`` instead. + +`~.Axes.bxp` and rcparams +~~~~~~~~~~~~~~~~~~~~~~~~~ +`~.Axes.bxp` now respects :rc:`boxplot.boxprops.linewidth` even when +*patch_artist* is set. +Previously, when the *patch_artist* parameter was set, `~.Axes.bxp` would ignore +:rc:`boxplot.boxprops.linewidth`. This was an oversight -- in particular, +`~.Axes.boxplot` did not ignore it. + +Major/minor tick collisions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Minor ticks that collide with major ticks are now hidden by default. +Previously, certain locator classes (`~.ticker.LogLocator`, +`~.ticker.AutoMinorLocator`) contained custom logic to avoid emitting +tick locations that collided with major ticks when they were used as +minor locators. This logic has now moved to the `~.axis.Axis` class, +and is used regardless of the locator class. You can control this +behavior via the `~.Axis.remove_overlapping_locs` attribute on +`~.axis.Axis`. + +If you were relying on both the major and minor tick labels to appear +on the same tick, you may need to update your code. For example, the +following snippet :: + + import numpy as np + import matplotlib.dates as mdates + import matplotlib.pyplot as plt + + t = np.arange("2018-11-03", "2018-11-06", dtype="datetime64") + x = np.random.rand(len(t)) + + fig, ax = plt.subplots() + ax.plot(t, x) + ax.xaxis.set( + major_locator=mdates.DayLocator(), + major_formatter=mdates.DateFormatter("\n%a"), + minor_locator=mdates.HourLocator((0, 6, 12, 18)), + minor_formatter=mdates.DateFormatter("%H:%M"), + ) + # disable removing overlapping locations + ax.xaxis.remove_overlapping_locs = False + plt.show() + +labeled days using major ticks, and hours and minutes using minor +ticks and added a newline to the major ticks labels to avoid them +crashing into the minor tick labels. Setting the +`~.Axis.remove_overlapping_locs` property (also accessible via +`~.Axis.set_remove_overlapping_locs` / +`~.Axis.get_remove_overlapping_locs` and `~.pyplot.setp`) disables +removing overlapping tick locations. + +The major tick labels could also be adjusted include hours and +minutes, as the minor ticks are gone, so the ``major_formatter`` +would be:: + + mdates.DateFormatter("%H:%M\n%a") + +usetex support +~~~~~~~~~~~~~~ +Previously, if :rc:`text.usetex` was True, then constructing a `.TextPath` on +a non-mathtext string with ``usetex=False`` would rely on the mathtext parser +(but not on usetex support!) to parse the string. The mathtext parser is not +invoked anymore, which may cause slight changes in glyph positioning. + +get_window_extents +~~~~~~~~~~~~~~~~~~ + +`.matplotlib.axes.Axes.get_window_extent` used to return a bounding box +that was slightly larger than the axes, presumably to take into account +the ticks that may be on a spine. However, it was not scaling the tick sizes +according to the dpi of the canvas, and it did not check if the ticks were +visible, or on the spine. + +Now `.matplotlib.axes.Axes.get_window_extent` just returns the axes extent +with no padding for ticks. + +This affects `.matplotlib.axes.Axes.get_tightbbox` in cases where there are +outward ticks with no tick labels, and it also removes the (small) pad around +axes in that case. + +`.spines.Spine.get_window_extent` now takes into account ticks that are on the +spine. + +Sankey +~~~~~~ +Previously, `.Sankey.add` would only accept a single string as the *labels* +argument if its length is equal to the number of flows, in which case it would +use one character of the string for each flow. + +The behavior has been changed to match the documented one: when a single string +is passed, it is used to label all the flows. + +`~.font_manager.FontManager` scores +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.font_manager.FontManager.score_weight` is now more strict with its +inputs. Previously, when a weight string was passed to +`.font_manager.FontManager.score_weight`, + +- if the weight was the string representation of an integer, it would be + converted to that integer, +- otherwise, if the weight was not a standard weight name, it would be silently + replaced by a value of 500 ("normal" weight). + +`.font_manager.FontManager.score_weight` now raises an exception on such inputs. + +Text alignment +~~~~~~~~~~~~~~ + +Text alignment was previously incorrect, in particular for multiline text +objects with large descenders (i.e. subscripts) and rotated text. These have +been fixed and made more consistent, but could make old code that has +compensated for this no longer have the correct alignment. + +Upper case color strings +~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for passing single-letter colors (one of "rgbcmykw") as UPPERCASE +characters is deprecated; these colors will become case-sensitive (lowercase) +after the deprecation period has passed. + +The goal is to decrease the number of ambiguous cases when using the ``data`` +keyword to plotting methods; e.g. ``plot("X", "Y", data={"X": ..., "Y": ...})`` +will not warn about "Y" possibly being a color anymore after the deprecation +period has passed. + +Degenerate limits +~~~~~~~~~~~~~~~~~ + +When bounds passed to `~.axes.Axes.set_xlim` are degenerate (i.e. the +lower and upper value are equal), the method used to "expand" the +bounds now matches the expansion behavior of autoscaling when the plot +contains a single x-value, and should in particular produce nicer +limits for non-linear scales. + +`~.Axes.plot` format string parsing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In certain cases, `~.Axes.plot` would previously accept format strings +specifying more than one linestyle (e.g. ``"---."`` which specifies both +``"--"`` and ``"-."``); only use one of them would be used. This now raises a +`ValueError` instead. + +HTMLWriter +~~~~~~~~~~ +The HTMLWriter constructor is more strict: it no longer normalizes unknown +values of *default_mode* to 'loop', but errors out instead. + +AFM parsing +~~~~~~~~~~~ +In accordance with the AFM spec, the AFM parser no longer truncates the +``UnderlinePosition`` and ``UnderlineThickness`` fields to integers. + +The ``Notice`` field (which can only be publicly accessed by the deprecated +``afm.parse_afm`` API) is no longer decoded to a `str`, but instead kept as +`bytes`, to support non-conformant AFM files that use non-ASCII characters in +that field. + +`.Artist.set` keyword normalisation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.Artist.set` now normalizes keywords before sorting them. Previously it sorted +its keyword arguments in reverse alphabetical order (with a special-case to +put ``color`` at the end) before applying them. + +It now normalizes aliases (and, as above, emits a warning on duplicate +properties) before doing the sorting (so ``c`` goes to the end too). + +`.Axes.tick_params` argument checking +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously `.Axes.tick_params` silently did nothing when an invalid *axis* +parameter was supplied. This behavior has been changed to raise a `ValueError` +instead. + +`.Axes.hist` output +~~~~~~~~~~~~~~~~~~~ + +Input that consists of multiple empty lists will now return a list of histogram +values for each one of the lists. For example, an input of ``[[],[]]`` will +return 2 lists of histogram values. Previously, a single list was returned. + +``backend_bases.TimerBase.remove_callback`` future signature change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, ``backend_bases.TimerBase.remove_callback(func, *args, +**kwargs)`` removes a callback previously added by +``backend_bases.Timer.add_callback(func, *args, **kwargs)``, but if +``*args, **kwargs`` is not passed in (i.e., +``TimerBase.remove_callback(func)``), then the first callback with a +matching ``func`` is removed, regardless of whether it was added with +or without ``*args, **kwargs``. + +In a future version, `.TimerBase.remove_callback` will always use the latter +behavior (not consider ``*args, **kwargs``); to specifically consider them, add +the callback as a `functools.partial` object :: + + cb = timer.add_callback(functools.partial(func, *args, **kwargs)) + # ... + # later + timer.remove_callback(cb) + +`.TimerBase.add_callback` was modified to return *func* to +simplify the above usage (previously it returned None); this also +allows using it as a decorator. + +The new API is modelled after `atexit.register` / `atexit.unregister`. + +`~.container.StemContainer` performance increase +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.container.StemContainer` objects can now store a +`~.collections.LineCollection` object instead of a list of +`~.lines.Line2D` objects for stem lines plotted using +`~.Axes.stem`. This gives a very large performance boost to displaying +and moving `~.Axes.stem` plots. + +This will become the default behaviour in Matplotlib 3.3. To use it +now, the *use_line_collection* keyword argument to `~.Axes.stem` can +be set to `True` :: + + ax.stem(..., use_line_collection=True) + +Individual line segments can be extracted from the +`~.collections.LineCollection` using +`~.collections.LineCollection.get_segments()`. See the +`~.collections.LineCollection` documentation for other methods to +retrieve the collection properties. + + +`~matplotlib.colorbar.ColorbarBase` inheritance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`matplotlib.colorbar.ColorbarBase` is no longer a subclass of +`.cm.ScalarMappable`. This inheritance lead to a confusing situation +where the `.cm.ScalarMappable` passed to `matplotlib.colorbar.Colorbar` +(`~.Figure.colorbar`) had a ``set_norm`` method, as did the colorbar. +The colorbar is now purely a follower to the `.ScalarMappable` norm and +colormap, and the old inherited methods +``matplotlib.colorbar.ColorbarBase.set_norm``, +``matplotlib.colorbar.ColorbarBase.set_cmap``, +``matplotlib.colorbar.ColorbarBase.set_clim`` are deprecated, as are +the getter versions of those calls. To set the norm associated with a +colorbar do ``colorbar.mappable.set_norm()`` etc. + + +FreeType and libpng search paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``MPLBASEDIRLIST`` environment variables and ``basedirlist`` entry in +``setup.cfg`` have no effect anymore. Instead, if building in situations where +FreeType or libpng are not in the compiler or linker's default path, set the +standard environment variables ``CFLAGS``/``LDFLAGS`` on Linux or OSX, or +``CL``/``LINK`` on Windows, to indicate the relevant paths. + +See details in :doc:`/install/index`. + +Setting artist properties twice or more in the same call +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting the same artist property multiple time via aliases is deprecated. +Previously, code such as :: + + plt.plot([0, 1], c="red", color="blue") + +would emit a warning indicating that ``c`` and ``color`` are aliases +of one another, and only keep the ``color`` kwarg. This behavior has +been deprecated; in a future version, this will raise a TypeError, +similar to Python's behavior when a keyword argument is passed twice :: + + plt.plot([0, 1], c="red", c="blue") + +This warning is raised by `~.cbook.normalize_kwargs`. + +Path code types +~~~~~~~~~~~~~~~ +Path code types like ``Path.MOVETO`` are now ``np.uint8`` instead of ``int`` +``Path.STOP``, ``Path.MOVETO``, ``Path.LINETO``, ``Path.CURVE3``, +``Path.CURVE4`` and ``Path.CLOSEPOLY`` are now of the type ``Path.code_type`` +(``np.uint8`` by default) instead of plain ``int``. This makes their type +match the array value type of the ``Path.codes`` array. + +LaTeX code in matplotlibrc file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, the rc file keys ``pgf.preamble`` and ``text.latex.preamble`` were +parsed using commas as separators. This would break valid LaTeX code, such as:: + + \usepackage[protrusion=true, expansion=false]{microtype} + +The parsing has been modified to pass the complete line to the LaTeX system, +keeping all commas. Passing a list of strings from within a Python script still +works as it used to. Passing a list containing non-strings now fails, instead +of coercing the results to strings. + +`.Axes.spy` +~~~~~~~~~~~ + +The method `.Axes.spy` now raises a `TypeError` for the keyword +arguments *interpolation* and *linestyle* instead of silently ignoring +them. + +Furthermore, `.Axes.spy` spy does now allow for an *extent* argument +(was silently ignored so far). + +A bug with ``Axes.spy(..., origin='lower')`` is fixed. Previously this +flipped the data but not the y-axis resulting in a mismatch between +axes labels and actual data indices. Now, *origin='lower'* flips both +the data and the y-axis labels. + +Boxplot tick methods +~~~~~~~~~~~~~~~~~~~~ + +The *manage_xticks* parameter of `~.Axes.boxplot` and `~.Axes.bxp` has +been renamed (with a deprecation period) to *manage_ticks*, to take +into account the fact that it manages either x or y ticks depending on +the *vert* parameter. + +When ``manage_ticks=True`` (the default), these methods now attempt to +take previously drawn boxplots into account when setting the axis +limits, ticks, and tick labels. + +MouseEvents +~~~~~~~~~~~ +MouseEvents now include the event name in their ``str()``. +Previously they contained the prefix "MPL MouseEvent". + +RGBA buffer return type +~~~~~~~~~~~~~~~~~~~~~~~ + +`.FigureCanvasAgg.buffer_rgba` and `.RendererAgg.buffer_rgba` now +return a memoryview The ``buffer_rgba`` method now allows direct +access to the renderer's underlying buffer (as a ``(m, n, 4)``-shape +memoryview) rather than copying the data to a new bytestring. This is +consistent with the behavior on Py2, where a buffer object was +returned. + + +``matplotlib.font_manager.win32InstalledFonts`` return type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``matplotlib.font_manager.win32InstalledFonts`` returns an empty list instead +of None if no fonts are found. + +``Axes.fmt_xdata`` and ``Axes.fmt_ydata`` error handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, if the user provided a ``Axes.fmt_xdata`` or +``Axes.fmt_ydata`` function that raised a `TypeError` (or set them to a +non-callable), the exception would be silently ignored and the default +formatter be used instead. This is no longer the case; the exception +is now propagated out. + +Deprecation of redundant `.Tick` attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``gridOn``, ``tick1On``, ``tick2On``, ``label1On``, and ``label2On`` +`~.Tick` attributes have been deprecated. Directly get and set the visibility +on the underlying artists, available as the ``gridline``, ``tick1line``, +``tick2line``, ``label1``, and ``label2`` attributes. + +The ``label`` attribute, which was an alias for ``label1``, has been +deprecated. + +Subclasses that relied on setting the above visibility attributes needs to be +updated; see e.g. :file:`examples/api/skewt.py`. + +Passing a Line2D's drawstyle together with the linestyle is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of ``plt.plot(..., linestyle="steps--")``, use ``plt.plot(..., +linestyle="--", drawstyle="steps")``. ``ds`` is now an alias for ``drawstyle``. + + +``pgi`` support dropped +----------------------- + +Support for ``pgi`` in the GTK3 backends has been dropped. ``pgi`` is +an alternative implementation to ``PyGObject``. ``PyGObject`` should +be used instead. + +rcParam changes +--------------- + +Removed +~~~~~~~ +The following deprecated rcParams have been removed: + +- ``text.dvipnghack`` +- ``nbagg.transparent`` (use :rc:`figure.facecolor` instead) +- ``plugins.directory`` +- ``axes.hold`` +- ``backend.qt4`` and ``backend.qt5`` (set the :envvar:`QT_API` environment + variable instead) + +Deprecated +~~~~~~~~~~ +The associated validator functions ``rcsetup.validate_qt4`` and +``validate_qt5`` are deprecated. + +The ``verbose.fileo`` and ``verbose.level`` rcParams have been deprecated. +These have had no effect since the switch from Matplotlib's old custom Verbose +logging to the stdlib's `logging` module. In addition the +``rcsetup.validate_verbose`` function is deprecated. + +The ``text.latex.unicode`` rcParam now defaults to ``True`` and is +deprecated (i.e., in future versions +of Matplotlib, unicode input will always be supported). +Moreover, the underlying implementation now uses ``\usepackage[utf8]{inputenc}`` +instead of ``\usepackage{ucs}\usepackage[utf8x]{inputenc}``. + +Exception changes +----------------- +- ``mpl_toolkits.axes_grid1.axes_size.GetExtentHelper`` now raises `ValueError` + for invalid directions instead of `KeyError`. +- Previously, subprocess failures in the animation framework would raise either + in a `RuntimeError` or a `ValueError` depending on when the error occurred. + They now raise a `subprocess.CalledProcessError` with attributes set as + documented by the exception class. +- In certain cases, Axes methods (and pyplot functions) used to raise + a `RuntimeError` if they were called with a ``data`` kwarg and + otherwise mismatched arguments. They now raise a `TypeError` + instead. +- `.Axes.streamplot` does not support irregularly gridded ``x`` and ``y`` values. + So far, it used to silently plot an incorrect result. This has been changed to + raise a `ValueError` instead. +- The ``streamplot.Grid`` class, which is internally used by streamplot + code, also throws a `ValueError` when irregularly gridded values are + passed in. + +Removals +-------- +The following deprecated APIs have been removed: + +Classes and methods +~~~~~~~~~~~~~~~~~~~ +- ``Verbose`` (replaced by python logging library) +- ``artist.Artist.hitlist`` (no replacement) +- ``artist.Artist.is_figure_set`` (use ``artist.figure is not None`` instead) +- ``axis.Axis.unit_data`` (use ``axis.Axis.units`` instead) +- ``backend_bases.FigureCanvasBase.onRemove`` (no replacement) + ``backend_bases.FigureManagerBase.show_popup`` (this never did anything) +- ``backend_wx.SubplotToolWx`` (no replacement) +- ``backend_wx.Toolbar`` (use ``backend_wx.NavigationToolbar2Wx`` instead) +- ``cbook.align_iterators`` (no replacement) +- ``contour.ContourLabeler.get_real_label_width`` (no replacement) +- ``legend.Legend.draggable`` (use `.legend.Legend.set_draggable()` instead) +- ``texmanager.TexManager.postscriptd``, ``texmanager.TexManager.pscnt``, + ``texmanager.TexManager.make_ps``, ``texmanager.TexManager.get_ps_bbox`` + (no replacements) + +Arguments +~~~~~~~~~ +- The *fig* kwarg to `.GridSpec.get_subplot_params` and + `.GridSpecFromSubplotSpec.get_subplot_params` (use the argument + *figure* instead) +- Passing 'box-forced' to `.Axes.set_adjustable` (use 'box' instead) +- Support for the strings 'on'/'true'/'off'/'false' to mean + `True` / `False` (directly use `True` / `False` instead). + The following functions are affected: + + - `.axes.Axes.grid` + - `.Axes3D.grid` + - `.Axis.set_tick_params` + - `.pyplot.box` +- Using `.pyplot.axes` with an `.axes.Axes` type argument + (use `.pyplot.sca` instead) + +Other +~~~~~ +The following miscellaneous API elements have been removed + +- svgfont support (in :rc:`svg.fonttype`) +- Logging is now done with the standard python ``logging`` library. + ``matplotlib.verbose`` and the command line switches ``--verbose-LEVEL`` have + been removed. + + To control the logging output use:: + + import logging + logger = logging.getLogger('matplotlib') + logger.setLevel(logging.INFO) + # configure log handling: Either include it into your ``logging`` hierarchy, + # e.g. by configuring a root logger using ``logging.basicConfig()``, + # or add a standalone handler to the matplotlib logger: + logger.addHandler(logging.StreamHandler()) + +- ``__version__numpy__`` +- ``collections.CIRCLE_AREA_FACTOR`` +- ``font_manager.USE_FONTCONFIG`` +- ``font_manager.cachedir`` + +:mod:`matplotlib.mlab` removals +------------------------------- +Lots of code inside the :mod:`matplotlib.mlab` module which was deprecated +in Matplotlib 2.2 has been removed. See below for a list: + +- ``mlab.exp_safe`` (use `numpy.exp` instead) +- ``mlab.amap`` +- ``mlab.logspace`` (use `numpy.logspace` instead) +- ``mlab.rms_flat`` +- ``mlab.l1norm`` (use ``numpy.linalg.norm(a, ord=1)`` instead) +- ``mlab.l2norm`` (use ``numpy.linalg.norm(a, ord=2)`` instead) +- ``mlab.norm_flat`` (use ``numpy.linalg.norm(a.flat, ord=2)`` instead) +- ``mlab.frange`` (use `numpy.arange` instead) +- ``mlab.identity`` (use `numpy.identity` instead) +- ``mlab.base_repr`` +- ``mlab.binary_repr`` +- ``mlab.ispower2`` +- ``mlab.log2`` (use `numpy.log2` instead) +- ``mlab.isvector`` +- ``mlab.movavg`` +- ``mlab.safe_isinf`` (use `numpy.isinf` instead) +- ``mlab.safe_isnan`` (use `numpy.isnan` instead) +- ``mlab.cohere_pairs`` (use `scipy.signal.coherence` instead) +- ``mlab.entropy`` (use `scipy.stats.entropy` instead) +- ``mlab.normpdf`` (use ``scipy.stats.norm.pdf`` instead) +- ``mlab.find`` (use ``np.nonzero(np.ravel(condition))`` instead) +- ``mlab.longest_contiguous_ones`` +- ``mlab.longest_ones`` +- ``mlab.PCA`` +- ``mlab.prctile`` (use `numpy.percentile` instead) +- ``mlab.prctile_rank`` +- ``mlab.center_matrix`` +- ``mlab.rk4`` (use `scipy.integrate.ode` instead) +- ``mlab.bivariate_normal`` +- ``mlab.get_xyz_where`` +- ``mlab.get_sparse_matrix`` +- ``mlab.dist`` (use `numpy.hypot` instead) +- ``mlab.dist_point_to_segment`` +- ``mlab.griddata`` (use `scipy.interpolate.griddata`) +- ``mlab.less_simple_linear_interpolation`` (use `numpy.interp`) +- ``mlab.slopes`` +- ``mlab.stineman_interp`` +- ``mlab.segments_intersect`` +- ``mlab.fftsurr`` +- ``mlab.offset_line`` +- ``mlab.quad2cubic`` +- ``mlab.vector_lengths`` +- ``mlab.distances_along_curve`` +- ``mlab.path_length`` +- ``mlab.cross_from_above`` +- ``mlab.cross_from_below`` +- ``mlab.contiguous_regions`` (use `.cbook.contiguous_regions` instead) +- ``mlab.is_closed_polygon`` +- ``mlab.poly_between`` +- ``mlab.poly_below`` +- ``mlab.inside_poly`` +- ``mlab.csv2rec`` +- ``mlab.rec2csv`` (use `numpy.recarray.tofile` instead) +- ``mlab.rec2text`` (use `numpy.recarray.tofile` instead) +- ``mlab.rec_summarize`` +- ``mlab.rec_join`` +- ``mlab.recs_join`` +- ``mlab.rec_groupby`` +- ``mlab.rec_keep_fields`` +- ``mlab.rec_drop_fields`` +- ``mlab.rec_append_fields`` +- ``mlab.csvformat_factory`` +- ``mlab.get_formatd`` +- ``mlab.FormatDatetime`` (use `datetime.datetime.strftime` instead) +- ``mlab.FormatDate`` (use `datetime.date.strftime` instead) +- ``mlab.FormatMillions``, ``mlab.FormatThousands``, ``mlab.FormatPercent``, + ``mlab.FormatBool``, ``mlab.FormatInt``, ``mlab.FormatFloat``, + ``mlab.FormatFormatStr``, ``mlab.FormatString``, ``mlab.FormatObj`` +- ``mlab.donothing_callback`` + +`pylab` removals +---------------- +Lots of code inside the :mod:`matplotlib.mlab` module which was deprecated +in Matplotlib 2.2 has been removed. This means the following functions are +no longer available in the `pylab` module: + +- ``amap`` +- ``base_repr`` +- ``binary_repr`` +- ``bivariate_normal`` +- ``center_matrix`` +- ``csv2rec`` (use `numpy.recarray.tofile` instead) +- ``dist`` (use `numpy.hypot` instead) +- ``dist_point_to_segment`` +- ``distances_along_curve`` +- ``entropy`` (use `scipy.stats.entropy` instead) +- ``exp_safe`` (use `numpy.exp` instead) +- ``fftsurr`` +- ``find`` (use ``np.nonzero(np.ravel(condition))`` instead) +- ``frange`` (use `numpy.arange` instead) +- ``get_sparse_matrix`` +- ``get_xyz_where`` +- ``griddata`` (use `scipy.interpolate.griddata` instead) +- ``identity`` (use `numpy.identity` instead) +- ``inside_poly`` +- ``is_closed_polygon`` +- ``ispower2`` +- ``isvector`` +- ``l1norm`` (use ``numpy.linalg.norm(a, ord=1)`` instead) +- ``l2norm`` (use ``numpy.linalg.norm(a, ord=2)`` instead) +- ``log2`` (use `numpy.log2` instead) +- ``longest_contiguous_ones`` +- ``longest_ones`` +- ``movavg`` +- ``norm_flat`` (use ``numpy.linalg.norm(a.flat, ord=2)`` instead) +- ``normpdf`` (use ``scipy.stats.norm.pdf`` instead) +- ``path_length`` +- ``poly_below`` +- ``poly_between`` +- ``prctile`` (use `numpy.percentile` instead) +- ``prctile_rank`` +- ``rec2csv`` (use `numpy.recarray.tofile` instead) +- ``rec_append_fields`` +- ``rec_drop_fields`` +- ``rec_join`` +- ``rk4`` (use `scipy.integrate.ode` instead) +- ``rms_flat`` +- ``segments_intersect`` +- ``slopes`` +- ``stineman_interp`` +- ``vector_lengths`` + +mplot3d changes +--------------- + +Voxel shading +~~~~~~~~~~~~~ +`.Axes3D.voxels` now shades the resulting voxels; for more details see +What's new. The previous behavior can be achieved by passing :: + + ax.voxels(.., shade=False) + + + +Equal aspect axes disabled +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting the aspect on 3D axes previously returned non-sensical results +(e.g. see :ghissue:`1077`). Calling ``ax.set_aspect('equal')`` or +``ax.set_aspect(num)`` on a 3D axes now raises a +`NotImplementedError`. + +`.Poly3DCollection.set_zsort` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.Poly3DCollection.set_zsort` no longer silently ignores invalid +inputs, or `False` (which was always broken). Passing `True` to mean +``"average"`` is deprecated. + +Testing +------- +The ``--no-network`` flag to ``tests.py`` has been removed (no test requires +internet access anymore). If it is desired to disable internet access both for +old and new versions of Matplotlib, use ``tests.py -m 'not network'`` (which is +now a no-op). + +The image comparison test decorators now skip (rather than xfail) the test for +uncomparable formats. The affected decorators are `~.image_comparison` and +`~.check_figures_equal`. The deprecated ``ImageComparisonTest`` class is +likewise changed. + +Dependency changes +------------------ + +NumPy +~~~~~ +Matplotlib 3.1 now requires NumPy>=1.11. + +ghostscript +~~~~~~~~~~~ +Support for ghostscript 8.60 (released in 2007) has been removed. The oldest +supported version of ghostscript is now 9.0 (released in 2010). + +Mathtext changes +---------------- +- In constructs such as ``"$1~2$"``, mathtext now interprets the tilde as a + space, consistently with TeX (this was previously a parse error). + +Deprecations +~~~~~~~~~~~~ +- The ``\stackrel`` mathtext command has been deprecated (it behaved differently + from LaTeX's ``\stackrel``. To stack two mathtext expressions, use + ``\genfrac{left-delim}{right-delim}{fraction-bar-thickness}{}{top}{bottom}``. +- The ``\mathcircled`` mathtext command (which is not a real TeX command) + is deprecated. Directly use unicode characters (e.g. + ``"\N{CIRCLED LATIN CAPITAL LETTER A}"`` or ``"\u24b6"``) instead. +- Support for setting :rc:`mathtext.default` to circled is deprecated. + +Signature deprecations +---------------------- +The following signature related behaviours are deprecated: + +- The *withdash* keyword argument to `.Axes.text()`. Consider using + `.Axes.annotate()` instead. +- Passing (n, 1)-shaped error arrays to `.Axes.errorbar()`, which was not + documented and did not work for ``n = 2``. Pass a 1D array instead. +- The *frameon* keyword argument to `~.Figure.savefig` and the ``savefig.frameon`` + rcParam. To emulate ``frameon = False``, set *facecolor* to fully + transparent (``"none"``, or ``(0, 0, 0, 0)``). +- Passing a non-1D (typically, (n, 1)-shaped) input to `.Axes.pie`. + Pass a 1D array instead. +- The `.TextPath` constructor used to silently drop ignored arguments; this + behavior is deprecated. +- The *usetex* parameter of `.TextToPath.get_text_path` is deprecated and + folded into the *ismath* parameter, which can now take the values + `False`, `True`, and ``"TeX"``, consistently with other low-level + text processing functions. +- Passing ``'normal'`` to `.axes.Axes.axis()` is deprecated, use + ``ax.axis('auto')`` instead. +- Passing the *block* argument of `.pyplot.show` positionally is deprecated; it + should be passed by keyword. +- When using the nbagg backend, `.pyplot.show` used to silently accept and ignore + all combinations of positional and keyword arguments. This behavior is + deprecated. +- The unused *shape* and *imlim* parameters to `.Axes.imshow` are + deprecated. To avoid triggering the deprecation warning, the *filternorm*, + *filterrad*, *resample*, and *url* arguments should be passed by + keyword. +- The *interp_at_native* parameter to `.BboxImage`, which has had no effect + since Matplotlib 2.0, is deprecated. +- All arguments to the ``matplotlib.cbook.deprecation.deprecated`` decorator + and ``matplotlib.cbook.deprecation.warn_deprecated`` function, except the + first one (the version where the deprecation occurred), are now keyword-only. + The goal is to avoid accidentally setting the "message" argument when the + "name" (or "alternative") argument was intended, as this has repeatedly + occurred in the past. +- The arguments of `matplotlib.testing.compare.calculate_rms` have been renamed + from ``expectedImage, actualImage``, to ``expected_image, actual_image``. +- Passing positional arguments to `.Axis.set_ticklabels` beyond *ticklabels* + itself has no effect, and support for them is deprecated. +- Passing ``shade=None`` to `~.axes3d.Axes3D.plot_surface` is deprecated. This + was an unintended implementation detail with the same semantics as + ``shade=False``. Please use the latter code instead. +- `matplotlib.ticker.MaxNLocator` and its *set_params* method will issue + a warning on unknown keyword arguments instead of silently ignoring them. + Future versions will raise an error. + +Changes in parameter names +-------------------------- + +- The *arg* parameter to `matplotlib.use` has been renamed to *backend*. + + This will only affect cases where that parameter has been set + as a keyword argument. The common usage pattern as a positional argument + ``matplotlib.use('Qt5Agg')`` is not affected. +- The *normed* parameter to `.Axes.hist2d` has been renamed to *density*. +- The *s* parameter to `.Annotation` (and indirectly `.Axes.annotate`) has + been renamed to *text*. +- The *tolerence* parameter to + `.bezier.find_bezier_t_intersecting_with_closedpath`, + `.bezier.split_bezier_intersecting_with_closedpath`, + ``bezier.find_r_to_boundary_of_closedpath``, + `.bezier.split_path_inout` and `.bezier.check_if_parallel` has been renamed to + *tolerance*. + +In each case, the old parameter name remains supported (it cannot be used +simultaneously with the new name), but support for it will be dropped in +Matplotlib 3.3. + +Class/method/attribute deprecations +----------------------------------- + + + +Support for custom backends that do not provide a +`.GraphicsContextBase.set_hatch_color` method is deprecated. We +suggest that custom backends let their ``GraphicsContext`` class +inherit from `.GraphicsContextBase`, to at least provide stubs for all +required methods. + +- ``spine.Spine.is_frame_like`` + +This has not been used in the codebase since its addition in 2009. + +- ``axis3d.Axis.get_tick_positions`` + + This has never been used internally, there is no equivalent method exists on + the 2D Axis classes, and despite the similar name, it has a completely + different behavior from the 2D Axis' ``axis.Axis.get_ticks_position`` method. +- ``.backend_pgf.LatexManagerFactory`` + +- ``mpl_toolkits.axisartist.axislines.SimpleChainedObjects`` +- ``mpl_toolkits.Axes.AxisDict`` + +Internal Helper Functions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``checkdep_dvipng`` +- ``checkdep_ghostscript`` +- ``checkdep_pdftops`` +- ``checkdep_inkscape`` + + +- ``ticker.decade_up`` +- ``ticker.decade_down`` + + +- ``cbook.dedent`` +- ``docstring.Appender`` +- ``docstring.dedent`` +- ``docstring.copy_dedent`` + +Use the standard library's docstring manipulation tools instead, such as +`inspect.cleandoc` and `inspect.getdoc`. + + + +- ``matplotlib.scale.get_scale_docs()`` +- ``matplotlib.pyplot.get_scale_docs()`` + +These are considered internal and will be removed from the public API in a +future version. + +- ``projections.process_projection_requirements`` + +- ``backend_ps.PsBackendHelper`` +- ``backend_ps.ps_backend_helper``, + +- ``cbook.iterable`` +- ``cbook.get_label`` +- ``cbook.safezip`` + Manually check the lengths of the inputs instead, or rely on NumPy to do it. +- ``cbook.is_hashable`` + Use ``isinstance(..., collections.abc.Hashable)`` instead. + +- The ``.backend_bases.RendererBase.strip_math``. Use + `.cbook.strip_math` instead. + +Multiple internal functions that were exposed as part of the public API +of `.mpl_toolkits.mplot3d` are deprecated, + +**mpl_toolkits.mplot3d.art3d** + +- ``mpl_toolkits.mplot3d.art3d.norm_angle`` +- ``mpl_toolkits.mplot3d.art3d.norm_text_angle`` +- ``mpl_toolkits.mplot3d.art3d.path_to_3d_segment`` +- ``mpl_toolkits.mplot3d.art3d.paths_to_3d_segments`` +- ``mpl_toolkits.mplot3d.art3d.path_to_3d_segment_with_codes`` +- ``mpl_toolkits.mplot3d.art3d.paths_to_3d_segments_with_codes`` +- ``mpl_toolkits.mplot3d.art3d.get_patch_verts`` +- ``mpl_toolkits.mplot3d.art3d.get_colors`` +- ``mpl_toolkits.mplot3d.art3d.zalpha`` + +**mpl_toolkits.mplot3d.proj3d** + +- ``mpl_toolkits.mplot3d.proj3d.line2d`` +- ``mpl_toolkits.mplot3d.proj3d.line2d_dist`` +- ``mpl_toolkits.mplot3d.proj3d.line2d_seg_dist`` +- ``mpl_toolkits.mplot3d.proj3d.mod`` +- ``mpl_toolkits.mplot3d.proj3d.proj_transform_vec`` +- ``mpl_toolkits.mplot3d.proj3d.proj_transform_vec_clip`` +- ``mpl_toolkits.mplot3d.proj3d.vec_pad_ones`` +- ``mpl_toolkits.mplot3d.proj3d.proj_trans_clip_points`` + +If your project relies on these functions, consider vendoring them. + + +Font Handling +~~~~~~~~~~~~~ + +- ``backend_pdf.RendererPdf.afm_font_cache`` +- ``backend_ps.RendererPS.afmfontd`` +- ``font_manager.OSXInstalledFonts`` +- ``.TextToPath.glyph_to_path`` (Instead call ``font.get_path()`` and manually + transform the path.) + + +Date related functions +~~~~~~~~~~~~~~~~~~~~~~ + +- ``dates.seconds()`` +- ``dates.minutes()`` +- ``dates.hours()`` +- ``dates.weeks()`` +- ``dates.strpdate2num`` +- ``dates.bytespdate2num`` + +These are brittle in the presence of locale changes. Use standard datetime +parsers such as `time.strptime` or `dateutil.parser.parse`, and additionally +call `matplotlib.dates.date2num` if you need to convert to Matplotlib's +internal datetime representation; or use ``dates.datestr2num``. + +Axes3D +~~~~~~ + +- ``.axes3d.Axes3D.w_xaxis`` +- ``.axes3d.Axes3D.w_yaxis`` +- ``.axes3d.Axes3D.w_zaxis`` + +Use ``axes3d.Axes3D.xaxis``, ``axes3d.Axes3D.yaxis`` and +``axes3d.Axes3D.zaxis`` instead. + +Testing +~~~~~~~ + +- ``matplotlib.testing.decorators.switch_backend`` decorator + +Test functions should use ``pytest.mark.backend``, and the mark will be +picked up by the ``matplotlib.testing.conftest.mpl_test_settings`` fixture. + +Quiver +~~~~~~ + +- ``.color`` attribute of `.Quiver` objects + +Instead, use (as for any `.Collection`) the ``get_facecolor`` method. +Note that setting to the ``.color`` attribute did not update the quiver artist, +whereas calling ``set_facecolor`` does. + +GUI / backend details +~~~~~~~~~~~~~~~~~~~~~ + +- ``.get_py2exe_datafiles`` +- ``.tk_window_focus`` +- ``.backend_gtk3.FileChooserDialog`` +- ``.backend_gtk3.NavigationToolbar2GTK3.get_filechooser`` +- ``.backend_gtk3.SaveFigureGTK3.get_filechooser`` +- ``.NavigationToolbar2QT.adj_window`` attribute. This is unused and always ``None``. +- ``.backend_wx.IDLE_DELAY`` global variable + This is unused and only relevant to the now removed wx "idling" code (note that + as it is a module-level global, no deprecation warning is emitted when + accessing it). +- ``mlab.demean`` +- ``backend_gtk3cairo.FigureCanvasGTK3Cairo``, +- ``backend_wx.debug_on_error``, ``backend_wx.fake_stderr``, + ``backend_wx.raise_msg_to_str``, ``backend_wx.MenuButtonWx``, + ``backend_wx.PrintoutWx``, +- ``matplotlib.backends.qt_editor.formlayout`` module + +This module is a vendored, modified version of the official formlayout_ module +available on PyPI. Install that module separately if you need it. + +.. _formlayout: https://pypi.org/project/formlayout/ + +- ``GraphicsContextPS.shouldstroke`` + + +Transforms / scales +~~~~~~~~~~~~~~~~~~~ + +- ``LogTransformBase`` +- ``Log10Transform`` +- ``Log2Transform``, +- ``NaturalLogTransformLog`` +- ``InvertedLogTransformBase`` +- ``InvertedLog10Transform`` +- ``InvertedLog2Transform`` +- ``InvertedNaturalLogTransform`` + +These classes defined in :mod:`matplotlib.scale` are deprecated. +As a replacement, use the general `~.scale.LogTransform` and `~.scale.InvertedLogTransform` +classes, whose constructors take a *base* argument. + +Locators / Formatters +~~~~~~~~~~~~~~~~~~~~~ + +- ``OldScalarFormatter.pprint_val`` +- ``ScalarFormatter.pprint_val`` +- ``LogFormatter.pprint_val`` + +These are helper methods that do not have a consistent signature across +formatter classes. + +Path tools +~~~~~~~~~~ + +- ``path.get_paths_extents`` + +Use `~.path.get_path_collection_extents` instead. + +- ``.Path.has_nonfinite`` attribute + +Use ``not np.isfinite(path.vertices).all()`` instead. + +- ``.bezier.find_r_to_boundary_of_closedpath`` function is deprecated + +This has always returned None instead of the requested radius. + +Text +~~~~ + +- ``text.TextWithDash`` +- ``Text.is_math_text`` +- ``TextPath.is_math_text`` +- ``TextPath.text_get_vertices_codes`` (As an alternative, construct a new ``TextPath`` object.) + +Unused attributes +~~~~~~~~~~~~~~~~~ + +- ``NavigationToolbar2QT.buttons`` +- ``Line2D.verticalOffset`` +- ``Quiver.keytext`` +- ``Quiver.keyvec`` +- ``SpanSelector.buttonDown`` + +These are unused and never updated. + + +Sphinx extensions +~~~~~~~~~~~~~~~~~ + +- ``matplotlib.sphinxext.mathmpl.math_directive`` +- ``matplotlib.sphinxext.plot_directive.plot_directive`` + +This is because the ``matplotlib.sphinxext.mathmpl`` and +``matplotlib.sphinxext.plot_directive`` interfaces have changed from the +(Sphinx-)deprecated function-based interface to a class-based interface; this +should not affect end users. + +- ``mpl_toolkits.axisartist.axis_artist.UnimplementedException`` + +Environmental Variables +~~~~~~~~~~~~~~~~~~~~~~~ + +- The ``MATPLOTLIBDATA`` environment variable + + +Axis +~~~~ + +- ``Axis.iter_ticks`` + +This only served as a helper to the private ``Axis._update_ticks`` + + +Undeprecations +-------------- +The following API elements have been un-deprecated: + +- The *obj_type* keyword argument to the + ``matplotlib.cbook.deprecation.deprecated`` decorator. +- *xmin*, *xmax* keyword arguments to `.Axes.set_xlim` and *ymin*, *ymax* + keyword arguments to `.Axes.set_ylim` + + +New features +------------ + +`.Text` now has a ``c`` alias for the ``color`` property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For consistency with `.Line2D`, the `~.text.Text` class has gained the ``c`` +alias for the ``color`` property. For example, one can now write :: + + ax.text(.5, .5, "foo", c="red") + + +``Cn`` colors now support ``n>=10`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It is now possible to go beyond the tenth color in the property cycle using +``Cn`` syntax, e.g. :: + + plt.plot([1, 2], color="C11") + +now uses the 12th color in the cycle. + +Note that previously, a construct such as:: + + plt.plot([1, 2], "C11") + +would be interpreted as a request to use color ``C1`` and marker ``1`` +(an "inverted Y"). To obtain such a plot, one should now use :: + + plt.plot([1, 2], "1C1") + +(so that the first "1" gets correctly interpreted as a marker +specification), or, more explicitly:: + + plt.plot([1, 2], marker="1", color="C1") + + +New `.Formatter.format_ticks` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The `.Formatter` class gained a new `~.Formatter.format_ticks` method, which +takes the list of all tick locations as a single argument and returns the list +of all formatted values. It is called by the axis tick handling code and, by +default, first calls `~.Formatter.set_locs` with all locations, then repeatedly +calls ``Formatter.__call__`` for each location. + +Tick-handling code in the codebase that previously performed this sequence +(`~.Formatter.set_locs` followed by repeated ``Formatter.__call__``) have been +updated to use `~.Formatter.format_ticks`. + +`~.Formatter.format_ticks` is intended to be overridden by `.Formatter` +subclasses for which the formatting of a tick value depends on other tick +values, such as `.ConciseDateFormatter`. + +Added support for RGB(A) images in pcolorfast +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +pcolorfast now accepts 3D images (RGB or RGBA) arrays if the X and Y +specifications allow image or pcolorimage rendering; they remain unsupported by +the more general quadmesh rendering + + +Invalid inputs +-------------- + +Passing invalid locations to `~.Axes.legend` and `~.Axes.table` used +to fallback on a default location. This behavior is deprecated and +will throw an exception in a future version. + +`.offsetbox.AnchoredText` is unable to handle the *horizontalalignment* or +*verticalalignment* kwargs, and used to ignore them with a warning. This +behavior is deprecated and will throw an exception in a future version. + +Passing steps less than 1 or greater than 10 to `~.ticker.MaxNLocator` used to +result in undefined behavior. It now throws a `ValueError`. + +The signature of the (private) ``Axis._update_ticks`` has been changed to not +take the renderer as argument anymore (that argument is unused). diff --git a/doc/api/prev_api_changes/api_changes_3.1.1.rst b/doc/api/prev_api_changes/api_changes_3.1.1.rst new file mode 100644 index 000000000000..7bc2466602c5 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.1.1.rst @@ -0,0 +1,16 @@ +API Changes for 3.1.1 +===================== + +.. contents:: + :local: + :depth: 1 + +Behavior changes +---------------- + +Locator.nonsingular return order +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.Locator.nonsingular` (introduced in mpl 3.1) now returns a range ``v0, v1`` +with ``v0 <= v1``. This behavior is consistent with the implementation of +``nonsingular`` by the `.LogLocator` and `.LogitLocator` subclasses. diff --git a/doc/api/prev_api_changes/api_changes_3.10.0.rst b/doc/api/prev_api_changes/api_changes_3.10.0.rst new file mode 100644 index 000000000000..ac4e4e981b21 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.10.0 +====================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.10.0/behavior.rst + +.. include:: /api/prev_api_changes/api_changes_3.10.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.10.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.10.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.10.0/behavior.rst b/doc/api/prev_api_changes/api_changes_3.10.0/behavior.rst new file mode 100644 index 000000000000..ae50371fa7aa --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.0/behavior.rst @@ -0,0 +1,122 @@ +Behavior Changes +---------------- + + +onselect argument to selector widgets made optional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *onselect* argument to `.EllipseSelector`, `.LassoSelector`, `.PolygonSelector`, and +`.RectangleSelector` is no longer required. + +``NavigationToolbar2.save_figure`` now returns filepath of saved figure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``NavigationToolbar2.save_figure`` function may return the filename of the saved figure. + +If a backend implements this functionality it should return `None` +in the case where no figure is actually saved (because the user closed the dialog without saving). + +If the backend does not or can not implement this functionality (currently the Gtk4 backends +and webagg backends do not) this method will return ``NavigationToolbar2.UNKNOWN_SAVED_STATUS``. + +SVG output: improved reproducibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some SVG-format plots `produced different output on each render `__, even with a static ``svg.hashsalt`` value configured. + +The problem was a non-deterministic ID-generation scheme for clip paths; the fix introduces a repeatable, monotonically increasing integer ID scheme as a replacement. + +Provided that plots add clip paths themselves in deterministic order, this enables repeatable (a.k.a. reproducible, deterministic) SVG output. + +ft2font classes are now final +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ft2font classes `.ft2font.FT2Font`, and `.ft2font.FT2Image` are now final +and can no longer be subclassed. + +``InsetIndicator`` artist +~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.Axes.indicate_inset` and `~.Axes.indicate_inset_zoom` now return an instance +of `~matplotlib.inset.InsetIndicator`. Use the +`~matplotlib.inset.InsetIndicator.rectangle` and +`~matplotlib.inset.InsetIndicator.connectors` properties of this artist to +access the objects that were previously returned directly. + +``imshow`` *interpolation_stage* default changed to 'auto' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *interpolation_stage* parameter of `~.Axes.imshow` has a new default +value 'auto'. For images that are up-sampled less than a factor of +three or down-sampled, image interpolation will occur in 'rgba' space. For images +that are up-sampled by a factor of 3 or more, then image interpolation occurs +in 'data' space. + +The previous default was 'data', so down-sampled images may change subtly with +the new default. However, the new default also avoids floating point artifacts +at sharp boundaries in a colormap when down-sampling. + +The previous behavior can achieved by setting the *interpolation_stage* parameter +or :rc:`image.interpolation_stage` to 'data'. + +imshow default *interpolation* changed to 'auto' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *interpolation* parameter of `~.Axes.imshow` has a new default +value 'auto', changed from 'antialiased', for consistency with *interpolation_stage* +and because the interpolation is only anti-aliasing during down-sampling. Passing +'antialiased' still works, and behaves exactly the same as 'auto', but is discouraged. + +dark_background and fivethirtyeight styles no longer set ``savefig.facecolor`` and ``savefig.edgecolor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using these styles, :rc:`savefig.facecolor` and :rc:`savefig.edgecolor` +now inherit the global default value of "auto", which means that the actual +figure colors will be used. Previously, these rcParams were set to the same +values as :rc:`figure.facecolor` and :rc:`figure.edgecolor`, i.e. a saved +figure would always use the theme colors even if the user manually overrode +them; this is no longer the case. + +This change should have no impact for users that do not manually set the figure +face and edge colors. + +Add zorder option in QuiverKey +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``zorder`` can be used as a keyword argument to `.QuiverKey`. Previously, +that parameter did not have any effect because the zorder was hard coded. + +Subfigures +~~~~~~~~~~ + +`.Figure.subfigures` are now added in row-major order to be consistent with +`.Figure.subplots`. The return value of `~.Figure.subfigures` is not changed, +but the order of ``fig.subfigs`` is. + +(Sub)Figure.get_figure +~~~~~~~~~~~~~~~~~~~~~~ + +...in future will by default return the direct parent figure, which may be a SubFigure. +This will make the default behavior consistent with the +`~matplotlib.artist.Artist.get_figure` method of other artists. To control the +behavior, use the newly introduced *root* parameter. + + +``transforms.AffineDeltaTransform`` updates correctly on axis limit changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before this change, transform sub-graphs with ``AffineDeltaTransform`` did not update correctly. +This PR ensures that changes to the child transform are passed through correctly. + +The offset string associated with ConciseDateFormatter will now invert when the axis is inverted +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, when the axis was inverted, the offset string associated with ConciseDateFormatter would not change, +so the offset string indicated the axis was oriented in the wrong direction. Now, when the axis is inverted, the offset +string is oriented correctly. + +``suptitle`` in compressed layout +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Compressed layout now automatically positions the `~.Figure.suptitle` just +above the top row of axes. To keep this title in its previous position, +either pass ``in_layout=False`` or explicitly set ``y=0.98`` in the +`~.Figure.suptitle` call. diff --git a/doc/api/prev_api_changes/api_changes_3.10.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.10.0/deprecations.rst new file mode 100644 index 000000000000..383c19f3c811 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.0/deprecations.rst @@ -0,0 +1,169 @@ +Deprecations +------------ + + +Positional parameters in plotting functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Many plotting functions will restrict positional arguments to the first few parameters +in the future. All further configuration parameters will have to be passed as keyword +arguments. This is to enforce better code and and allow for future changes with reduced +risk of breaking existing code. + +Changing ``Figure.number`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Changing ``Figure.number`` is deprecated. This value is used by `.pyplot` +to identify figures. It must stay in sync with the pyplot internal state +and is not intended to be modified by the user. + +``PdfFile.hatchPatterns`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. + +(Sub)Figure.set_figure +~~~~~~~~~~~~~~~~~~~~~~ + +...is deprecated and in future will always raise an exception. The parent and +root figures of a (Sub)Figure are set at instantiation and cannot be changed. + +``Poly3DCollection.get_vector`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated with no replacement. + +Deprecated ``register`` on ``matplotlib.patches._Styles`` and subclasses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This class method is never used internally. Due to the internal check in the +method it only accepts subclasses of a private baseclass embedded in the host +class which makes it unlikely that it has been used externally. + +matplotlib.validate_backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +...is deprecated. Please use `matplotlib.rcsetup.validate_backend` instead. + + +matplotlib.sanitize_sequence +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +...is deprecated. Please use `matplotlib.cbook.sanitize_sequence` instead. + +ft2font module-level constants replaced by enums +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `.ft2font`-level constants have been converted to `enum` classes, and all API using +them now take/return the new types. + +The following constants are now part of `.ft2font.Kerning` (without the ``KERNING_`` +prefix): + +- ``KERNING_DEFAULT`` +- ``KERNING_UNFITTED`` +- ``KERNING_UNSCALED`` + +The following constants are now part of `.ft2font.LoadFlags` (without the ``LOAD_`` +prefix): + +- ``LOAD_DEFAULT`` +- ``LOAD_NO_SCALE`` +- ``LOAD_NO_HINTING`` +- ``LOAD_RENDER`` +- ``LOAD_NO_BITMAP`` +- ``LOAD_VERTICAL_LAYOUT`` +- ``LOAD_FORCE_AUTOHINT`` +- ``LOAD_CROP_BITMAP`` +- ``LOAD_PEDANTIC`` +- ``LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH`` +- ``LOAD_NO_RECURSE`` +- ``LOAD_IGNORE_TRANSFORM`` +- ``LOAD_MONOCHROME`` +- ``LOAD_LINEAR_DESIGN`` +- ``LOAD_NO_AUTOHINT`` +- ``LOAD_TARGET_NORMAL`` +- ``LOAD_TARGET_LIGHT`` +- ``LOAD_TARGET_MONO`` +- ``LOAD_TARGET_LCD`` +- ``LOAD_TARGET_LCD_V`` + +The following constants are now part of `.ft2font.FaceFlags`: + +- ``EXTERNAL_STREAM`` +- ``FAST_GLYPHS`` +- ``FIXED_SIZES`` +- ``FIXED_WIDTH`` +- ``GLYPH_NAMES`` +- ``HORIZONTAL`` +- ``KERNING`` +- ``MULTIPLE_MASTERS`` +- ``SCALABLE`` +- ``SFNT`` +- ``VERTICAL`` + +The following constants are now part of `.ft2font.StyleFlags`: + +- ``ITALIC`` +- ``BOLD`` + +FontProperties initialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.FontProperties` initialization is limited to the two call patterns: + +- single positional parameter, interpreted as fontconfig pattern +- only keyword parameters for setting individual properties + +All other previously supported call patterns are deprecated. + +``AxLine`` ``xy1`` and ``xy2`` setters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These setters now each take a single argument, ``xy1`` or ``xy2`` as a tuple. +The old form, where ``x`` and ``y`` were passed as separate arguments, is +deprecated. + +Calling ``pyplot.polar()`` with an existing non-polar Axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This currently plots the data into the non-polar Axes, ignoring +the "polar" intention. This usage scenario is deprecated and +will raise an error in the future. + +Passing floating-point values to ``RendererAgg.draw_text_image`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Any floating-point values passed to the *x* and *y* parameters were truncated to integers +silently. This behaviour is now deprecated, and only `int` values should be used. + +Passing floating-point values to ``FT2Image`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Any floating-point values passed to the `.FT2Image` constructor, or the *x0*, *y0*, *x1*, +and *y1* parameters of `.FT2Image.draw_rect_filled` were truncated to integers silently. +This behaviour is now deprecated, and only `int` values should be used. + +``boxplot`` and ``bxp`` *vert* parameter, and ``rcParams["boxplot.vertical"]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter *vert: bool* has been deprecated on `~.Axes.boxplot` and +`~.Axes.bxp`. It is replaced by *orientation: {"vertical", "horizontal"}* +for API consistency. + +``rcParams["boxplot.vertical"]``, which controlled the orientation of ``boxplot``, +is deprecated without replacement. + +This deprecation is currently marked as pending and will be fully deprecated in Matplotlib 3.11. + +``violinplot`` and ``violin`` *vert* parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter *vert: bool* has been deprecated on `~.Axes.violinplot` and +`~.Axes.violin`. +It will be replaced by *orientation: {"vertical", "horizontal"}* for API +consistency. + +This deprecation is currently marked as pending and will be fully deprecated in Matplotlib 3.11. + +``proj3d.proj_transform_clip`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated with no replacement. diff --git a/doc/api/prev_api_changes/api_changes_3.10.0/development.rst b/doc/api/prev_api_changes/api_changes_3.10.0/development.rst new file mode 100644 index 000000000000..329256b466b5 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.0/development.rst @@ -0,0 +1,25 @@ +Development changes +------------------- + +Documentation-specific custom Sphinx roles are now semi-public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For third-party packages that derive types from Matplotlib, our use of custom roles may +prevent Sphinx from building their docs. These custom Sphinx roles are now public solely +for the purposes of use within projects that derive from Matplotlib types. See +:mod:`matplotlib.sphinxext.roles` for details. + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.10, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+----------------+ +| Dependency | min in mpl3.9 | min in mpl3.10 | ++============+=================+================+ +| Python | 3.9 | 3.10 | ++------------+-----------------+----------------+ + +This is consistent with our :ref:`min_deps_policy` and `SPEC0 +`__ diff --git a/doc/api/prev_api_changes/api_changes_3.10.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.10.0/removals.rst new file mode 100644 index 000000000000..7ed06e7446ef --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.0/removals.rst @@ -0,0 +1,307 @@ +Removals +-------- + + +ttconv removed +~~~~~~~~~~~~~~ + +The ``matplotlib._ttconv`` extension has been removed. Most of its +functionaliy was already replaced by other code, and the only thing left +was embedding TTF fonts in PostScript in Type 42 format. This is now +done in the PS backend using the FontTools library. + +Remove hard reference to ``lastevent`` in ``LocationEvent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +This was previously used to detect exiting from axes, however the hard +reference would keep closed `.Figure` objects and their children alive longer +than expected. + +``ft2font.FT2Image.draw_rect`` and ``ft2font.FT2Font.get_xys`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... have been removed as they are unused. + +``Tick.set_label``, ``Tick.set_label1`` and ``Tick.set_label2`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are removed. Calling these methods from third-party code usually had no +effect, as the labels are overwritten at draw time by the tick formatter. + + +Functions in ``mpl_toolkits.mplot3d.proj3d`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The function ``transform`` is just an alias for ``proj_transform``, +use the latter instead. + +The following functions were either unused (so no longer required in Matplotlib) +or considered private. + +* ``ortho_transformation`` +* ``persp_transformation`` +* ``proj_points`` +* ``proj_trans_points`` +* ``rot_x`` +* ``rotation_about_vector`` +* ``view_transformation`` + + +Arguments other than ``renderer`` to ``get_tightbbox`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are keyword-only arguments. This is for consistency and that +different classes have different additional arguments. + + +Method parameters renamed to match base classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The only parameter of ``transform_affine`` and ``transform_non_affine`` in ``Transform`` subclasses is renamed +to *values*. + +The *points* parameter of ``transforms.IdentityTransform.transform`` is renamed to *values*. + +The *trans* parameter of ``table.Cell.set_transform`` is renamed to *t* consistently with +`.Artist.set_transform`. + +The *clippath* parameters of ``axis.Axis.set_clip_path`` and ``axis.Tick.set_clip_path`` are +renamed to *path* consistently with `.Artist.set_clip_path`. + +The *s* parameter of ``images.NonUniformImage.set_filternorm`` is renamed to *filternorm* +consistently with ``_ImageBase.set_filternorm``. + +The *s* parameter of ``images.NonUniformImage.set_filterrad`` is renamed to *filterrad* +consistently with ``_ImageBase.set_filterrad``. + +The only parameter of ``Annotation.contains`` and ``Legend.contains`` is renamed to *mouseevent* +consistently with `.Artist.contains`. + +Method parameters renamed +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *p* parameter of ``BboxBase.padded`` is renamed to *w_pad*, consistently with the other parameter, *h_pad* + +*numdecs* parameter and attribute of ``LogLocator`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are removed without replacement, because they had no effect. +The ``PolyQuadMesh`` class requires full 2D arrays of values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, if a masked array was input, the list of polygons within the collection +would shrink to the size of valid polygons and users were required to keep track of +which polygons were drawn and call ``set_array()`` with the smaller "compressed" +array size. Passing the "compressed" and flattened array values will no longer +work and the full 2D array of values (including the mask) should be passed +to `.PolyQuadMesh.set_array`. +``ContourSet.collections`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. `~.ContourSet` is now implemented as a single +`~.Collection` of paths, each path corresponding to a contour level, possibly +including multiple unconnected components. + +``ContourSet.antialiased`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. Use `~.Collection.get_antialiased` or +`~.Collection.set_antialiased` instead. Note that `~.Collection.get_antialiased` +returns an array. + +``tcolors`` and ``tlinewidths`` attributes of ``ContourSet`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... have been removed. Use `~.Collection.get_facecolor`, `~.Collection.get_edgecolor` +or `~.Collection.get_linewidths` instead. + + +``calc_label_rot_and_inline`` method of ``ContourLabeler`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed without replacement. + + +``add_label_clabeltext`` method of ``ContourLabeler`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. Use `~.ContourLabeler.add_label` instead. +Passing extra positional arguments to ``Figure.add_axes`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional arguments passed to `.Figure.add_axes` other than a rect or an existing +``Axes`` were previously ignored, and is now an error. + + +Artists explicitly passed in will no longer be filtered by legend() based on their label +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, artists explicitly passed to ``legend(handles=[...])`` are filtered out if +their label starts with an underscore. This filter is no longer applied; explicitly +filter out such artists (``[art for art in artists if not +art.get_label().startswith('_')]``) if necessary. + +Note that if no handles are specified at all, then the default still filters out labels +starting with an underscore. + + +The parameter of ``Annotation.contains`` and ``Legend.contains`` is renamed to *mouseevent* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... consistently with `.Artist.contains`. + + +Support for passing the "frac" key in ``annotate(..., arrowprops={"frac": ...})`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. This key has had no effect since Matplotlib 1.5. + + +Passing non-int or sequence of non-int to ``Table.auto_set_column_width`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Column numbers are ints, and formerly passing any other type was effectively ignored. +This has now become an error. + + +Widgets +~~~~~~~ + +The *visible* attribute getter of ``*Selector`` widgets has been removed; use +``get_visible`` instead. + + +Auto-closing of figures when switching backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Allowable backend switches (i.e. those that do not swap a GUI event loop with another +one) will not close existing figures. If necessary, call ``plt.close("all")`` before +switching. + + +``FigureCanvasBase.switch_backends`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed with no replacement. + + +Accessing ``event.guiEvent`` after event handlers return +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is no longer supported, and ``event.guiEvent`` will be set to None once the event +handlers return. For some GUI toolkits, it is unsafe to use the event, though you may +separately stash the object at your own risk. + + +``PdfPages(keep_empty=True)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A zero-page PDF is not valid, thus passing ``keep_empty=True`` to `.backend_pdf.PdfPages` +and `.backend_pgf.PdfPages`, and the ``keep_empty`` attribute of these classes, is no +longer allowed, and empty PDF files will not be created. + +Furthermore, `.backend_pdf.PdfPages` no longer immediately creates the target file upon +instantiation, but only when the first figure is saved. To fully control file creation, +directly pass an opened file object as argument (``with open(path, "wb") as file, +PdfPages(file) as pdf: ...``). + + +``backend_ps.psDefs`` +~~~~~~~~~~~~~~~~~~~~~ + +The ``psDefs`` module-level variable in ``backend_ps`` has been removed with no +replacement. + + +Automatic papersize selection in PostScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting :rc:`ps.papersize` to ``'auto'`` or passing ``papersize='auto'`` to +`.Figure.savefig` is no longer supported. Either pass an explicit paper type name, or +omit this parameter to use the default from the rcParam. + + +``RendererAgg.tostring_rgb`` and ``FigureCanvasAgg.tostring_rgb`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... have been remove with no direct replacement. Consider using ``buffer_rgba`` instead, +which should cover most use cases. + + +``NavigationToolbar2QT.message`` has been removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... with no replacement. + + +``TexManager.texcache`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is considered private and has been removed. The location of the cache directory is +clarified in the doc-string. + + +``cbook`` API changes +~~~~~~~~~~~~~~~~~~~~~ + +``cbook.Stack`` has been removed with no replacement. + +``Grouper.clean()`` has been removed with no replacement. The Grouper class now cleans +itself up automatically. + +The *np_load* parameter of ``cbook.get_sample_data`` has been removed; `.get_sample_data` +now auto-loads numpy arrays. Use ``get_sample_data(..., asfileobj=False)`` instead to get +the filename of the data file, which can then be passed to `open`, if desired. + + +Calling ``paths.get_path_collection_extents`` with empty *offsets* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Calling `~.get_path_collection_extents` with an empty *offsets* parameter has an +ambiguous interpretation and is no longer allowed. + + +``bbox.anchored()`` with no explicit container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Not passing a *container* argument to `.BboxBase.anchored` is no longer supported. + + +``INVALID_NON_AFFINE``, ``INVALID_AFFINE``, ``INVALID`` attributes of ``TransformNode`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These attributes have been removed. + + +``axes_grid1`` API changes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``anchored_artists.AnchoredEllipse`` has been removed. Instead, directly construct an +`.AnchoredOffsetbox`, an `.AuxTransformBox`, and an `~.patches.Ellipse`, as demonstrated +in :doc:`/gallery/misc/anchored_artists`. + +The ``axes_divider.AxesLocator`` class has been removed. The ``new_locator`` method of +divider instances now instead returns an opaque callable (which can still be passed to +``ax.set_axes_locator``). + +``axes_divider.Divider.locate`` has been removed; use ``Divider.new_locator(...)(ax, +renderer)`` instead. + +``axes_grid.CbarAxesBase.toggle_label`` has been removed. Instead, use standard methods +for manipulating colorbar labels (`.Colorbar.set_label`) and tick labels +(`.Axes.tick_params`). + +``inset_location.InsetPosition`` has been removed; use `~.Axes.inset_axes` instead. + + +``axisartist`` API changes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``axisartist.axes_grid`` and ``axisartist.axes_rgb`` modules, which provide wrappers +combining the functionality of `.axes_grid1` and `.axisartist`, have been removed; +directly use e.g. ``AxesGrid(..., axes_class=axislines.Axes)`` instead. + +Calling an axisartist Axes to mean `~matplotlib.pyplot.axis` has been removed; explicitly +call the method instead. + +``floating_axes.GridHelperCurveLinear.get_data_boundary`` has been removed. Use +``grid_finder.extreme_finder(*[None] * 5)`` to get the extremes of the grid. diff --git a/doc/api/prev_api_changes/api_changes_3.10.1.rst b/doc/api/prev_api_changes/api_changes_3.10.1.rst new file mode 100644 index 000000000000..71a2f71efc94 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.1.rst @@ -0,0 +1,14 @@ +API Changes for 3.10.1 +====================== + +Behaviour +--------- + +*alpha* parameter handling on images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When passing and array to ``imshow(..., alpha=...)``, the parameter was silently ignored +if the image data was an RGB or RBGA image or if :rc:`image.interpolation_stage` +resolved to "rbga". + +This is now fixed, and the alpha array overwrites any previous transparency information. diff --git a/doc/api/prev_api_changes/api_changes_3.10.7.rst b/doc/api/prev_api_changes/api_changes_3.10.7.rst new file mode 100644 index 000000000000..a60061e86277 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.10.7.rst @@ -0,0 +1,10 @@ +API Changes for 3.10.7 +====================== + +Development +----------- + +New minimum version of pyparsing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The minimum required version of ``pyparsing`` has been updated from 2.3.1 to 3.0.0. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0.rst b/doc/api/prev_api_changes/api_changes_3.2.0.rst new file mode 100644 index 000000000000..e6d79890e2cc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.2.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.2.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.2.0/behavior.rst + +.. include:: /api/prev_api_changes/api_changes_3.2.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.2.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.2.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst new file mode 100644 index 000000000000..6c1960c4dfaf --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.2.0/behavior.rst @@ -0,0 +1,323 @@ + +Behavior changes +---------------- + +Reduced default value of :rc:`axes.formatter.limits` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Changed the default value of :rc:`axes.formatter.limits` from -7, 7 to +-5, 6 for better readability. + +.. plot:: + + import matplotlib.pyplot as plt + import numpy as np + + fig, (ax_old, ax_new) = plt.subplots(1, 2, constrained_layout=True) + + ax_new.set_title('new values (-5, 6)') + ax_old.set_title('old values (-7, 7)') + + x = np.logspace(-8, 8, 1024) + y = 1e-5 * np.exp(-x / 1e5) + 1e-6 + + ax_old.xaxis.get_major_formatter().set_powerlimits((-7, 7)) + ax_old.yaxis.get_major_formatter().set_powerlimits((-7, 7)) + + for ax in [ax_new, ax_old]: + ax.plot(x, y) + ax.set_xlim(0, 1e6) + ax.set_ylim(1e-6, 1e-5) + + +`matplotlib.colorbar.Colorbar` uses un-normalized axes for all mappables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Before 3.0, `matplotlib.colorbar.Colorbar` (`~.Figure.colorbar`) normalized +all axes limits between 0 and 1 and had custom tickers to handle the +labelling of the colorbar ticks. After 3.0, colorbars constructed from +mappables that were *not* contours were constructed with axes that had +limits between ``vmin`` and ``vmax`` of the mappable's norm, and the tickers +were made children of the normal axes tickers. + +This version of Matplotlib extends that to mappables made by contours, and +allows the axes to run between the lowest boundary in the contour and the +highest. + +Code that worked around the normalization between 0 and 1 will need to be +modified. + +``MovieWriterRegistry`` +~~~~~~~~~~~~~~~~~~~~~~~ +`.MovieWriterRegistry` now always checks the availability of the writer classes +before returning them. If one wishes, for example, to get the first available +writer, without performing the availability check on subsequent writers, it is +now possible to iterate over the registry, which will yield the names of the +available classes. + +.. _api-changes-3-2-0-autoscaling: + +Autoscaling +~~~~~~~~~~~ + +Matplotlib used to recompute autoscaled limits after every plotting +(``plot()``, ``bar()``, etc.) call. It now only does so when actually +rendering the canvas, or when the user queries the Axes limits. This is a +major performance improvement for plots with a large number of artists. + +In particular, this means that artists added manually with `.Axes.add_line`, +`.Axes.add_patch`, etc. will be taken into account by the autoscale, even +without an explicit call to `.Axes.autoscale_view`. + +In some cases, this can result in different limits being reported. If this is +an issue, consider triggering a draw with ``fig.canvas.draw()``. + +Autoscaling has also changed for artists that are based on the `.Collection` +class. Previously, the method that calculates the automatic limits +`.Collection.get_datalim` tried to take into account the size of objects +in the collection and make the limits large enough to not clip any of the +object, i.e., for `.Axes.scatter` it would make the limits large enough to not +clip any markers in the scatter. This is problematic when the object size is +specified in physical space, or figure-relative space, because the transform +from physical units to data limits requires knowing the data limits, and +becomes invalid when the new limits are applied. This is an inverse +problem that is theoretically solvable (if the object is physically smaller +than the axes), but the extra complexity was not deemed worth it, particularly +as the most common use case is for markers in scatter that are usually small +enough to be accommodated by the default data limit margins. + +While the new behavior is algorithmically simpler, it is conditional on +properties of the `.Collection` object: + +1. ``offsets = None``, ``transform`` is a child of ``Axes.transData``: use the paths + for the automatic limits (i.e. for `.LineCollection` in `.Axes.streamplot`). +2. ``offsets != None``, and ``offset_transform`` is child of ``Axes.transData``: + + a) ``transform`` is child of ``Axes.transData``: use the ``path + offset`` for + limits (i.e., for `.Axes.bar`). + b) ``transform`` is not a child of ``Axes.transData``: just use the offsets + for the limits (i.e. for scatter) + +3. otherwise return a null `.Bbox`. + +While this seems complicated, the logic is simply to use the information from +the object that are in data space for the limits, but not information that is +in physical units. + +log-scale bar() / hist() autolimits +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The autolimits computation in `~.Axes.bar` and `~.Axes.hist` when the axes +already uses log-scale has changed to match the computation when the axes is +switched to log-scale after the call to `~.Axes.bar` and `~.Axes.hist`, and +when calling ``bar(..., log=True)`` / ``hist(..., log=True)``: if there are +at least two different bar heights, add the normal axes margins to them (in +log-scale); if there is only a single bar height, expand the axes limits by one +order of magnitude around it and then apply axes margins. + + +Axes labels spanning multiple rows/columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Axes.label_outer`` now correctly keep the x labels and tick labels visible +for Axes spanning multiple rows, as long as they cover the last row of the Axes +grid. (This is consistent with keeping the y labels and tick labels visible +for Axes spanning multiple columns as long as they cover the first column of +the Axes grid.) + +The ``Axes.is_last_row`` and ``Axes.is_last_col`` methods now correctly return +True for Axes spanning multiple rows, as long as they cover the last row or +column respectively. Again this is consistent with the behavior for axes +covering the first row or column. + +The ``Axes.rowNum`` and ``Axes.colNum`` attributes are deprecated, as they only +refer to the first grid cell covered by the Axes. Instead, use the new +``ax.get_subplotspec().rowspan`` and ``ax.get_subplotspec().colspan`` +properties, which are `range` objects indicating the whole span of rows and +columns covered by the subplot. + +(Note that all methods and attributes mentioned here actually only exist on +the ``Subplot`` subclass of `~.axes.Axes`, which is used for grid-positioned Axes but +not for Axes positioned directly in absolute coordinates.) + +The `.GridSpec` class gained the ``nrows`` and ``ncols`` properties as more +explicit synonyms for the parameters returned by ``GridSpec.get_geometry``. + + +Locators +~~~~~~~~ +When more than `.Locator.MAXTICKS` ticks are generated, the behavior of +`.Locator.raise_if_exceeds` changed from raising a RuntimeError to emitting a +log at WARNING level. + +nonsingular Locators +~~~~~~~~~~~~~~~~~~~~ +``Locator.nonsingular`` (introduced in mpl 3.1), ``DateLocator.nonsingular``, and +``AutoDateLocator.nonsingular`` now returns a range ``v0, v1`` with ``v0 <= v1``. +This behavior is consistent with the implementation of ``nonsingular`` by the +``LogLocator`` and ``LogitLocator`` subclasses. + +``get_data_ratio`` +~~~~~~~~~~~~~~~~~~ +``Axes.get_data_ratio`` now takes the axes scale into account (linear, log, +logit, etc.) before computing the y-to-x ratio. This change allows fixed +aspects to be applied to any combination of x and y scales. + +Artist sticky edges +~~~~~~~~~~~~~~~~~~~ +Previously, the ``sticky_edges`` attribute of artists was a list of values such +that if an axis limit coincides with a sticky edge, it would not be expanded by +the axes margins (this is the mechanism that e.g. prevents margins from being +added around images). + +``sticky_edges`` now have an additional effect on margins application: even if +an axis limit did not coincide with a sticky edge, it cannot *cross* a sticky +edge through margin application -- instead, the margins will only expand the +axis limit until it bumps against the sticky edge. + +This change improves the margins of axes displaying a `~.Axes.streamplot`: + +- if the streamplot goes all the way to the edges of the vector field, then the + axis limits are set to match exactly the vector field limits (whereas they + would sometimes be off by a small floating point error previously). +- if the streamplot does not reach the edges of the vector field (e.g., due to + the use of ``start_points`` and ``maxlength``), then margins expansion will + not cross the vector field limits anymore. + +This change is also used internally to ensure that polar plots don't display +negative *r* values unless the user really passes in a negative value. + +``gid`` in svg output +~~~~~~~~~~~~~~~~~~~~~ +Previously, if a figure, axis, legend or some other artists had a custom +``gid`` set (e.g. via ``.set_gid()``), this would not be reflected in +the svg output. Instead a default gid, like ``figure_1`` would be shown. +This is now fixed, such that e.g. ``fig.set_gid("myfigure")`` correctly +shows up as ```` in the svg file. If you relied on the +gid having the default format, you now need to make sure not to set the +``gid`` parameter of the artists. + +Fonts +~~~~~ +Font weight guessing now first checks for the presence of the FT_STYLE_BOLD_FLAG +before trying to match substrings in the font name. In particular, this means +that Times New Roman Bold is now correctly detected as bold, not normal weight. + +Color-like checking +~~~~~~~~~~~~~~~~~~~ +`matplotlib.colors.is_color_like` used to return True for all string +representations of floats. However, only those with values in 0-1 are valid +colors (representing grayscale values). `.is_color_like` now returns False +for string representations of floats outside 0-1. + +Default image interpolation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Images displayed in Matplotlib previously used nearest-neighbor +interpolation, leading to aliasing effects for downscaling and non-integer +upscaling. + +New default for :rc:`image.interpolation` is the new option "antialiased". +``imshow(A, interpolation='antialiased')`` will apply a Hanning filter when +resampling the data in A for display (or saving to file) *if* the upsample +rate is less than a factor of three, and not an integer; downsampled data is +always smoothed at resampling. + +To get the old behavior, set :rc:`image.interpolation` to the old default "nearest" +(or specify the ``interpolation`` kwarg of `.Axes.imshow`) + +To always get the anti-aliasing behavior, no matter what the up/down sample +rate, set :rc:`image.interpolation` to "hanning" (or one of the other filters +available). + +Note that the "hanning" filter was chosen because it has only a modest +performance penalty. Anti-aliasing can be improved with other filters. + +rcParams +~~~~~~~~ +When using `.RendererSVG` with ``rcParams["svg.image_inline"] == +True``, externally written images now use a single counter even if the +``renderer.basename`` attribute is overwritten, rather than a counter per +basename. + +This change will only affect you if you used ``rcParams["svg.image_inline"] = True`` +(the default is False) *and* manually modified ``renderer.basename``. + +Changed the default value of :rc:`axes.formatter.limits` from -7, 7 to -5, 6 +for better readability. + +``add_subplot()`` +~~~~~~~~~~~~~~~~~ +`.Figure.add_subplot()` and `.pyplot.subplot()` do not accept a *figure* +keyword argument anymore. It only used to work anyway if the passed figure +was ``self`` or the current figure, respectively. + +``indicate_inset()`` +~~~~~~~~~~~~~~~~~~~~ +In <= 3.1.0, `~matplotlib.axes.Axes.indicate_inset` and +`~matplotlib.axes.Axes.indicate_inset_zoom` were documented as returning +a 4-tuple of `~matplotlib.patches.ConnectionPatch`, where in fact they +returned a 4-length list. + +They now correctly return a 4-tuple. +`~matplotlib.axes.Axes.indicate_inset` would previously raise an error if +the optional *inset_ax* was not supplied; it now completes successfully, +and returns *None* instead of the tuple of ``ConnectionPatch``. + +PGF backend +~~~~~~~~~~~ +The pgf backend's get_canvas_width_height now returns the canvas size in +display units rather than in inches, which it previously did. +The new behavior is the correct one given the uses of ``get_canvas_width_height`` +in the rest of the codebase. + +The pgf backend now includes images using ``\includegraphics`` instead of +``\pgfimage`` if the version of ``graphicx`` is recent enough to support the +``interpolate`` option (this is detected automatically). + +`~matplotlib.cbook` +~~~~~~~~~~~~~~~~~~~ +The default value of the "obj_type" parameter to ``cbook.warn_deprecated`` has +been changed from "attribute" (a default that was never used internally) to the +empty string. + +Testing +~~~~~~~ +The test suite no longer turns on the Python fault handler by default. +Set the standard ``PYTHONFAULTHANDLER`` environment variable to do so. + +Backend ``supports_blit`` +~~~~~~~~~~~~~~~~~~~~~~~~~ +Backends do not need to explicitly define the flag ``supports_blit`` anymore. +This is only relevant for backend developers. Backends had to define the flag +``supports_blit``. This is not needed anymore because the blitting capability +is now automatically detected. + +Exception changes +~~~~~~~~~~~~~~~~~ +Various APIs that raised a `ValueError` for incorrectly typed inputs now raise +`TypeError` instead: `.backend_bases.GraphicsContextBase.set_clip_path`, +``blocking_input.BlockingInput.__call__``, ``matplotlib.cm.register_cmap``, +`.dviread.DviFont`, `.rcsetup.validate_hatch`, +``.rcsetup.validate_animation_writer_path``, `.spines.Spine`, many classes in +the :mod:`matplotlib.transforms` module and :mod:`matplotlib.tri` package, and +Axes methods that take a ``norm`` parameter. + +If extra kwargs are passed to `.LogScale`, `TypeError` will now be +raised instead of `ValueError`. + +mplot3d auto-registration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +`mpl_toolkits.mplot3d` is always registered by default now. It is no +longer necessary to import mplot3d to create 3d axes with :: + + ax = fig.add_subplot(111, projection="3d") + +`.SymLogNorm` now has a *base* parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, `.SymLogNorm` had no *base* keyword argument and the base was +hard-coded to ``base=np.e``. This was inconsistent with the default behavior of +`.SymmetricalLogScale` (which defaults to ``base=10``) and the use of the word +"decade" in the documentation. + +In preparation for changing the default base to 10, calling `.SymLogNorm` +without the new *base* keyword argument emits a deprecation warning. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst new file mode 100644 index 000000000000..65b72c7e0558 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.2.0/deprecations.rst @@ -0,0 +1,304 @@ + +Deprecations +------------ + +`matplotlib.use` +~~~~~~~~~~~~~~~~ +The ``warn`` parameter to `matplotlib.use()` is deprecated (catch the +`ImportError` emitted on backend switch failure and reemit a warning yourself +if so desired). + +plotfile +~~~~~~~~ +``.pyplot.plotfile`` is deprecated in favor of separately loading and plotting +the data. Use pandas or NumPy to load data, and pandas or matplotlib to plot +the resulting data. + +axes and axis +~~~~~~~~~~~~~ +Setting ``Axis.major.locator``, ``Axis.minor.locator``, ``Axis.major.formatter`` +or ``Axis.minor.formatter`` to an object that is not a subclass of `.Locator` or +`.Formatter` (respectively) is deprecated. Note that these attributes should +usually be set using `.Axis.set_major_locator`, `.Axis.set_minor_locator`, etc. +which already raise an exception when an object of the wrong class is passed. + +Passing more than one positional argument or unsupported keyword arguments to +`~matplotlib.axes.Axes.axis()` is deprecated (such arguments used to be +silently ignored). + +``minor`` argument will become keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using the parameter ``minor`` to ``get_*ticks()`` / ``set_*ticks()`` as a +positional parameter is deprecated. It will become keyword-only in future +versions. + +``axes_grid1`` +~~~~~~~~~~~~~~ +The ``mpl_toolkits.axes_grid1.colorbar`` module and its colorbar implementation +are deprecated in favor of :mod:`matplotlib.colorbar`, as the former is +essentially abandoned and the latter is a more featureful replacement with a +nearly compatible API (for example, the following additional keywords are +supported: ``panchor``, ``extendfrac``, ``extendrect``). + +The main differences are: + +- Setting the ticks on the colorbar is done by calling ``colorbar.set_ticks`` + rather than ``colorbar.cbar_axis.set_xticks`` or + ``colorbar.cbar_axis.set_yticks``; the ``locator`` parameter to ``colorbar()`` + is deprecated in favor of its synonym ``ticks`` (which already existed + previously, and is consistent with :mod:`matplotlib.colorbar`). +- The colorbar's long axis is accessed with ``colorbar.xaxis`` or + ``colorbar.yaxis`` depending on the orientation, rather than + ``colorbar.cbar_axis``. +- The default ticker is no longer ``MaxNLocator(5)``, but a + ``_ColorbarAutoLocator``. +- Overdrawing multiple colorbars on top of one another in a single Axes (e.g. + when using the ``cax`` attribute of `~.axes_grid1.axes_grid.ImageGrid` + elements) is not supported; if you previously relied on the second colorbar + being drawn over the first, you can call ``cax.cla()`` to clear the axes + before drawing the second colorbar. + +During the deprecation period, the ``mpl_toolkits.legacy_colorbar`` +rcParam can be set to True to use ``mpl_toolkits.axes_grid1.colorbar`` in +:mod:`mpl_toolkits.axes_grid1` code with a deprecation warning (the default), +or to False to use ``matplotlib.colorbar``. + +Passing a ``pad`` size of ``None`` (the default) as a synonym for zero to +the ``append_axes``, ``new_horizontal`` and ``new_vertical`` methods of +`.axes_grid1.axes_divider.AxesDivider` is deprecated. In a future release, the +default value of ``None`` will mean "use :rc:`figure.subplot.wspace` or +:rc:`figure.subplot.hspace`" (depending on the orientation). Explicitly pass +``pad=0`` to keep the old behavior. + +Axes3D +~~~~~~ +``mplot3d.axis3d.get_flip_min_max`` is deprecated. + +``axes3d.unit_bbox`` is deprecated (use ``Bbox.unit`` instead). + +``axes3d.Axes3D.w_xaxis``, ``.w_yaxis``, and ``.w_zaxis`` are deprecated (use +``.xaxis``, ``.yaxis``, and ``.zaxis`` instead). + +`matplotlib.cm` +~~~~~~~~~~~~~~~ +``cm.revcmap`` is deprecated. Use `.Colormap.reversed` to reverse a colormap. + +``cm.datad`` no longer contains entries for reversed colormaps in their +"unconverted" form. + +axisartist +~~~~~~~~~~ +``mpl_toolkits.axisartist.grid_finder.GridFinderBase`` is deprecated (its +only use is to be inherited by the `.GridFinder` class which just provides +more defaults in the constructor and directly sets the transforms, so +``GridFinderBase``'s methods were just moved to `.GridFinder`). + +``axisartist.axis_artist.BezierPath`` is deprecated (use `.patches.PathPatch` +to draw arbitrary Paths). + +``AxisArtist.line`` is now a `.patches.PathPatch` instance instead of a +``BezierPath`` instance. + +Returning a factor equal to None from axisartist Locators (which are **not** +the same as "standard" tick Locators), or passing a factor equal to None +to axisartist Formatters (which are **not** the same as "standard" tick +Formatters) is deprecated. Pass a factor equal to 1 instead. + +For the `mpl_toolkits.axisartist.axis_artist.AttributeCopier` class, the +constructor and the ``set_ref_artist`` method, and the *default_value* +parameter of ``get_attribute_from_ref_artist``, are deprecated. + +Deprecation of the constructor means that classes inheriting from +`.AttributeCopier` should no longer call its constructor. + +Locators +~~~~~~~~ +The unused ``Locator.autoscale`` method is deprecated (pass the axis limits to +`.Locator.view_limits` instead). + +Animation +~~~~~~~~~ +The following methods and attributes of the `.MovieWriterRegistry` class are +deprecated: ``set_dirty``, ``ensure_not_dirty``, ``reset_available_writers``, +``avail``. + +``smart_bounds()`` +~~~~~~~~~~~~~~~~~~ +The "smart_bounds" functionality is deprecated. This includes +``Axis.set_smart_bounds()``, ``Axis.get_smart_bounds()``, +``Spine.set_smart_bounds()``, and ``Spine.get_smart_bounds()``. + +``boxplot()`` +~~~~~~~~~~~~~ +Setting the ``whis`` parameter of `.Axes.boxplot` and `.cbook.boxplot_stats` to +"range" to mean "the whole data range" is deprecated; set it to (0, 100) (which +gets interpreted as percentiles) to achieve the same effect. + +``fill_between()`` +~~~~~~~~~~~~~~~~~~ +Passing scalars to parameter *where* in ``fill_between()`` and +``fill_betweenx()`` is deprecated. While the documentation already states that +*where* must be of the same size as *x* (or *y*), scalars were accepted and +broadcasted to the size of *x*. Non-matching sizes will raise a ``ValueError`` +in the future. + +``scatter()`` +~~~~~~~~~~~~~ +Passing the *verts* parameter to `.axes.Axes.scatter` is deprecated; use the +*marker* parameter instead. + +``tight_layout()`` +~~~~~~~~~~~~~~~~~~ +The ``renderer`` parameter to `.Figure.tight_layout` is deprecated; this method +now always uses the renderer instance cached on the `.Figure`. + +rcParams +~~~~~~~~ +The ``rcsetup.validate_animation_writer_path`` function is deprecated. + +Setting :rc:`savefig.format` to "auto" is deprecated; use its synonym "png" instead. + +Setting :rc:`text.hinting` to True or False is deprecated; use their synonyms +"auto" or "none" instead. + +``rcsetup.update_savefig_format`` is deprecated. + +``rcsetup.validate_path_exists`` is deprecated (use ``os.path.exists`` to check +whether a path exists). + +``rcsetup.ValidateInterval`` is deprecated. + +Dates +~~~~~ +``dates.mx2num`` is deprecated. + +TK +~~ +``NavigationToolbar2Tk.set_active`` is deprecated, as it has no (observable) +effect. + +WX +~~ +``FigureFrameWx.statusbar`` and ``NavigationToolbar2Wx.statbar`` are deprecated. +The status bar can be retrieved by calling standard wx methods +(``frame.GetStatusBar()`` and ``toolbar.GetTopLevelParent().GetStatusBar()``). + +``backend_wx.ConfigureSubplotsWx.configure_subplots`` and +``backend_wx.ConfigureSubplotsWx.get_canvas`` are deprecated. + +PGF +~~~ +``backend_pgf.repl_escapetext`` and ``backend_pgf.repl_mathdefault`` are +deprecated. + +``RendererPgf.latexManager`` is deprecated. + +FigureCanvas +~~~~~~~~~~~~ +``FigureCanvasBase.draw_cursor`` (which has never done anything and has never +been overridden in any backend) is deprecated. + +``FigureCanvasMac.invalidate`` is deprecated in favor of its synonym, +``FigureCanvasMac.draw_idle``. + +The ``dryrun`` parameter to the various ``FigureCanvasFoo.print_foo`` methods +is deprecated. + + +QuiverKey doc +~~~~~~~~~~~~~ +``quiver.QuiverKey.quiverkey_doc`` is deprecated; use +``quiver.QuiverKey.__init__.__doc__`` instead. + +`matplotlib.mlab` +~~~~~~~~~~~~~~~~~ +``mlab.apply_window`` and ``mlab.stride_repeat`` are deprecated. + +Fonts +~~~~~ +``font_manager.JSONEncoder`` is deprecated. Use `.font_manager.json_dump` to +dump a `.FontManager` instance. + +``font_manager.createFontList`` is deprecated. `.font_manager.FontManager.addfont` +is now available to register a font at a given path. + +The ``as_str``, ``as_rgba_str``, ``as_array``, ``get_width`` and ``get_height`` +methods of ``matplotlib.ft2font.FT2Image`` are deprecated. Convert the ``FT2Image`` +to a NumPy array with ``np.asarray`` before processing it. + +Colors +~~~~~~ +The function ``matplotlib.colors.makeMappingArray`` is not considered part of +the public API any longer. Thus, it's deprecated. + +Using a string of single-character colors as a color sequence (e.g. "rgb") is +deprecated. Use an explicit list instead. + +Scales +~~~~~~ +Passing unsupported keyword arguments to `.ScaleBase`, and its subclasses +`.LinearScale` and `.SymmetricalLogScale`, is deprecated and will raise a +`TypeError` in 3.3. + +If extra keyword arguments are passed to `.LogScale`, `TypeError` will now be +raised instead of `ValueError`. + +Testing +~~~~~~~ +The ``matplotlib.testing.disable_internet`` module is deprecated. Use (for +example) pytest-remotedata_ instead. + +.. _pytest-remotedata: https://pypi.org/project/pytest-remotedata/ + +Support in `matplotlib.testing` for nose-based tests is deprecated (a +deprecation is emitted if using e.g. the decorators from that module while +both 1) matplotlib's conftests have not been called and 2) nose is in +``sys.modules``). + +``testing.is_called_from_pytest`` is deprecated. + +During the deprecation period, to force the generation of nose base tests, +import nose first. + +The *switch_backend_warn* parameter to ``matplotlib.test`` has no effect and is +deprecated. + +``testing.jpl_units.UnitDbl.UnitDbl.checkUnits`` is deprecated. + +``DivergingNorm`` renamed to ``TwoSlopeNorm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``DivergingNorm`` was a misleading name; although the norm was +developed with the idea that it would likely be used with diverging +colormaps, the word 'diverging' does not describe or evoke the norm's +mapping function. Since that function is monotonic, continuous, and +piece-wise linear with two segments, the norm has been renamed to +`.TwoSlopeNorm` + +Misc +~~~~ +``matplotlib.get_home`` is deprecated (use e.g. ``os.path.expanduser("~")``) +instead. + +``matplotlib.compare_versions`` is deprecated (use comparison of +``distutils.version.LooseVersion``\s instead). + +``matplotlib.checkdep_ps_distiller`` is deprecated. + +``matplotlib.figure.AxesStack`` is considered private API and will be removed +from the public API in future versions. + +``BboxBase.is_unit`` is deprecated (check the Bbox extents if needed). + +``Affine2DBase.matrix_from_values(...)`` is deprecated. Use (for example) +``Affine2D.from_values(...).get_matrix()`` instead. + +``style.core.is_style_file`` and ``style.core.iter_style_files`` +are deprecated. + +The ``datapath`` rcParam +~~~~~~~~~~~~~~~~~~~~~~~~ +Use `.get_data_path` instead. (The rcParam is deprecated because it cannot be +meaningfully set by an end user.) The rcParam had no effect from 3.2.0, but +was deprecated only in 3.2.1. In 3.2.1+ if ``'datapath'`` is set in a +``matplotlibrc`` file it will be respected, but this behavior will be removed in 3.3. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/development.rst b/doc/api/prev_api_changes/api_changes_3.2.0/development.rst new file mode 100644 index 000000000000..9af7fb8fb561 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.2.0/development.rst @@ -0,0 +1,31 @@ +Development changes +------------------- + +Windows build +~~~~~~~~~~~~~ +Previously, when building the ``matplotlib._png`` extension, the build +script would add "png" and "z" to the extensions ``.libraries`` attribute (if +pkg-config information is not available, which is in particular the case on +Windows). + +In particular, this implies that the Windows build would look up files named +``png.lib`` and ``z.lib``; but neither libpng upstream nor zlib upstream +provides these files by default. (On Linux, this would look up ``libpng.so`` +and ``libz.so``, which are indeed standard names.) + +Instead, on Windows, we now look up ``libpng16.lib`` and ``zlib.lib``, which +*are* the upstream names for the shared libraries (as of libpng 1.6.x). + +For a statically-linked build, the upstream names are ``libpng16_static.lib`` +and ``zlibstatic.lib``; one still needs to manually rename them if such a build +is desired. + +Packaging DLLs +~~~~~~~~~~~~~~ +Previously, it was possible to package Windows DLLs into the Matplotlib +wheel (or sdist) by copying them into the source tree and setting the +``package_data.dlls`` entry in ``setup.cfg``. + +DLLs copied in the source tree are now always packaged; the +``package_data.dlls`` entry has no effect anymore. If you do not want to +include the DLLs, don't copy them into the source tree. diff --git a/doc/api/prev_api_changes/api_changes_3.2.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.2.0/removals.rst new file mode 100644 index 000000000000..53d76d667509 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.2.0/removals.rst @@ -0,0 +1,83 @@ +Removals +-------- +The ``matplotlib.testing.determinism`` module, which exposes no public API, has +been deleted. + +The following API elements have been removed: + +- ``backend_gtk3.PIXELS_PER_INCH`` +- ``backend_pgf.re_escapetext``, ``backend_pgf.re_mathdefault``. +- the ``matplotlib.backends.tkagg``, ``matplotlib.backends.windowing``, + ``matplotlib.backends.wx_compat``, and ``matplotlib.compat.subprocess`` + modules +- ``RcParams.msg_depr``, ``RcParams.msg_depr_ignore``, + ``RcParams.msg_depr_set``, ``RcParams.msg_obsolete``, + ``RcParams.msg_backend_obsolete`` +- ``afm.parse_afm`` (use ``afm.AFM instead``) +- ``axes.Axes.mouseover_set`` +- ``backend_cairo.ArrayWrapper``, ``backend_cairo.RendererCairo.convert_path`` +- ``backend_gtk3.FileChooserDialog.sorted_filetypes`` (use + ``sorted(self.filetypes.items())`` instead) +- ``backend_pgf.get_texcommand`` +- ``backend_pdf.PdfFile.texFontMap`` +- ``backend_ps.get_bbox`` +- ``backend_qt.FigureCanvasQt.keyAutoRepeat`` (use + ``event.guiEvent.isAutoRepeat`` instead), ``backend_qt.error_msg_qt``, + ``backend_qt.exception_handler`` +- ``backend_wx.FigureCanvasWx.macros`` +- ``backends.pylab_setup`` +- ``cbook.Bunch`` (use ``types.SimpleNamespace`` instead), ``cbook.Locked``, + ``cbook.unicode_safe``, ``cbook.is_numlike`` (use + ``isinstance(..., numbers.Number)`` instead), ``cbook.mkdirs`` (use + ``os.makedirs(..., exist_ok=True)`` instead), ``cbook.GetRealpathAndStat`` + (use ``cbook.get_realpath_and_stat`` instead), + ``cbook.listFiles`` +- ``container.Container.set_remove_method`` +- ``contour.ContourLabeler.cl``, ``contour.ContourLabeler.cl_xy``, + ``contour.ContourLabeler.cl_cvalues`` (use ``labelTexts``, ``labelXYs``, + ``labelCValues`` instead) +- ``dates.DateFormatter.strftime``, ``dates.DateFormatter.strftime_pre_1900`` +- ``font_manager.TempCache``, ``font_manager.FontManager.ttffiles``, + ``font_manager.FontManager.afmfiles`` +- ``mathtext.unichr_safe`` (use ``chr`` instead) +- ``patches.YAArrow`` (use ``patches.FancyArrowPatch`` instead) +- ``sphinxext.plot_directive.remove_coding`` +- ``table.Table.get_child_artists`` +- ``testing.compare.compare_float``, ``testing.decorators.CleanupTest``, + ``testing.decorators.ImageComparisonTest``, + ``testing.decorators.skip_if_command_unavailable``, + support for nose-based tests +- ``text.Annotation.arrow`` (use ``text.Annotation.arrow_patch`` instead) +- ``textpath.TextToPath.tex_font_map`` +- ``ticker.Base``, ``ticker.closeto``, ``ticker.nearest_long`` +- ``axes_grid1.axes_divider.LocatableAxesBase``, + ``axes_grid1.axes_divider.locatable_axes_factory``, + ``axes_grid1.axes_divider.Axes`` (use ``axes_grid1.mpl_axes.Axes`` instead), + ``axes_grid1.axes_divider.LocatableAxes`` (use ``axes_grid1.mpl_axes.Axes`` + instead) +- ``axisartist.axes_divider.Axes``, ``axisartist.axes_divider.LocatableAxes`` + (use ``axisartist.axislines.Axes`` instead) +- the *normed* keyword argument to ``hist`` (use *density* instead) +- passing ``(verts, 0)`` or ``(..., 3)`` when specifying a marker to specify a + path or a circle, respectively (instead, use ``verts`` or ``"o"``, + respectively) +- the ``examples.directory`` rcParam + +The following members of ``matplotlib.backends.backend_pdf.PdfFile`` were removed: + +- ``nextObject`` +- ``nextFont`` +- ``nextAlphaState`` +- ``nextHatch`` +- ``nextImage`` +- ``alphaStateObject`` + +The ``required_interactive_framework`` attribute of backend modules introduced +in Matplotlib 3.0 has been moved to the ``FigureCanvas`` class, in order to +let it be inherited by third-party canvas subclasses and to make it easier to +know what interactive framework is required by a canvas class. + +``backend_qt4.FigureCanvasQT5``, which is an alias for +``backend_qt5.FigureCanvasQT`` (but only exists under that name in +``backend_qt4``), has been removed. + diff --git a/doc/api/prev_api_changes/api_changes_3.3.0.rst b/doc/api/prev_api_changes/api_changes_3.3.0.rst new file mode 100644 index 000000000000..bbe676a4ec52 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.3.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.3.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.3.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.3.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.3.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst new file mode 100644 index 000000000000..26f5c704476a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.0/behaviour.rst @@ -0,0 +1,341 @@ +Behaviour changes +----------------- + +``Formatter.fix_minus`` +~~~~~~~~~~~~~~~~~~~~~~~ +`.Formatter.fix_minus` now performs hyphen-to-unicode-minus replacement +whenever :rc:`axes.unicode_minus` is True; i.e. its behavior matches the one +of ``ScalarFormatter.fix_minus`` (`.ScalarFormatter` now just inherits that +implementation). + +This replacement is now used by the ``format_data_short`` method of the various +builtin formatter classes, which affects the cursor value in the GUI toolbars. + +``FigureCanvasBase`` now always has a ``manager`` attribute, which may be None +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, it did not necessarily have such an attribute. A check for +``hasattr(figure.canvas, "manager")`` should now be replaced by +``figure.canvas.manager is not None`` (or ``getattr(figure.canvas, "manager", None) is not None`` +for back-compatibility). + +`.cbook.CallbackRegistry` now propagates exceptions when no GUI event loop is running +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.cbook.CallbackRegistry` now defaults to propagating exceptions thrown by +callbacks when no interactive GUI event loop is running. If a GUI event loop +*is* running, `.cbook.CallbackRegistry` still defaults to just printing a +traceback, as unhandled exceptions can make the program completely ``abort()`` +in that case. + +``Axes.locator_params()`` validates ``axis`` parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.axes.Axes.locator_params` used to accept any value for ``axis`` and silently +did nothing, when passed an unsupported value. It now raises a ``ValueError``. + +``Axis.set_tick_params()`` validates ``which`` parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.Axis.set_tick_params` (and the higher level `.axes.Axes.tick_params` and +`.pyplot.tick_params`) used to accept any value for ``which`` and silently +did nothing, when passed an unsupported value. It now raises a ``ValueError``. + +``Axis.set_ticklabels()`` must match ``FixedLocator.locs`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If an axis is using a `.ticker.FixedLocator`, typically set by a call to +`.Axis.set_ticks`, then the number of ticklabels supplied must match the +number of locations available (``FixedFormattor.locs``). If not, a +``ValueError`` is raised. + +``backend_pgf.LatexManager.latex`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``backend_pgf.LatexManager.latex`` is now created with ``encoding="utf-8"``, so +its ``stdin``, ``stdout``, and ``stderr`` attributes are utf8-encoded. + +``pyplot.xticks()`` and ``pyplot.yticks()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, passing labels without passing the ticks to either `.pyplot.xticks` +and `.pyplot.yticks` would result in:: + + TypeError: object of type 'NoneType' has no len() + +It now raises a ``TypeError`` with a proper description of the error. + +Setting the same property under multiple aliases now raises a TypeError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, calling e.g. ``plot(..., color=somecolor, c=othercolor)`` would +emit a warning because ``color`` and ``c`` actually map to the same Artist +property. This now raises a TypeError. + +`.FileMovieWriter` temporary frames directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.FileMovieWriter` now defaults to writing temporary frames in a temporary +directory, which is always cleared at exit. In order to keep the individual +frames saved on the filesystem, pass an explicit *frame_prefix*. + +`.Axes.plot` no longer accepts *x* and *y* being both 2D and with different numbers of columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, calling `.Axes.plot` e.g. with *x* of shape ``(n, 3)`` and *y* of +shape ``(n, 2)`` would plot the first column of *x* against the first column +of *y*, the second column of *x* against the second column of *y*, **and** the +first column of *x* against the third column of *y*. This now raises an error +instead. + +`.Text.update_from` now copies usetex state from the source Text +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.Axes.stem` now defaults to ``use_line_collection=True`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This creates the stem plot as a `.LineCollection` rather than individual +`.Line2D` objects, greatly improving performance. + +rcParams color validator is now stricter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, rcParams entries whose values were color-like accepted "spurious" +extra letters or characters in the "middle" of the string, e.g. ``"(0, 1a, '0.5')"`` +would be interpreted as ``(0, 1, 0.5)``. These extra characters (including the +internal quotes) now cause a ValueError to be raised. + +`.SymLogNorm` now has a *base* parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, `.SymLogNorm` had no *base* keyword argument, and +defaulted to ``base=np.e`` whereas the documentation said it was +``base=10``. In preparation to make the default 10, calling +`.SymLogNorm` without the new *base* keyword argument emits a +deprecation warning. + + +`~.Axes.errorbar` now color cycles when only errorbar color is set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously setting the *ecolor* would turn off automatic color cycling for the plot, leading to the +the lines and markers defaulting to whatever the first color in the color cycle was in the case of +multiple plot calls. + +`.rcsetup.validate_color_for_prop_cycle` now always raises TypeError for bytes input +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It previously raised `TypeError`, **except** when the input was of the form +``b"C[number]"`` in which case it raised a ValueError. + +`.FigureCanvasPS.print_ps` and `.FigureCanvasPS.print_eps` no longer apply edgecolor and facecolor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These methods now assume that the figure edge and facecolor have been correctly +applied by `.FigureCanvasBase.print_figure`, as they are normally called +through it. + +This behavior is consistent with other figure saving methods +(`.FigureCanvasAgg.print_png`, `.FigureCanvasPdf.print_pdf`, +`.FigureCanvasSVG.print_svg`). + +`.pyplot.subplot()` now raises TypeError when given an incorrect number of arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This is consistent with other signature mismatch errors. Previously a +ValueError was raised. + +Shortcut for closing all figures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Shortcuts for closing all figures now also work for the classic toolbar. +There is no default shortcut any more because unintentionally closing all figures by a key press +might happen too easily. You can configure the shortcut yourself +using :rc:`keymap.quit_all`. + +Autoscale for arrow +~~~~~~~~~~~~~~~~~~~ +Calling ax.arrow() will now autoscale the axes. + +``set_tick_params(label1On=False)`` now also makes the offset text (if any) invisible +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... because the offset text can rarely be interpreted without tick labels +anyways. + +`.Axes.annotate` and `.pyplot.annotate` parameter name changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The parameter ``s`` to `.Axes.annotate` and `.pyplot.annotate` is renamed to +``text``, matching `.Annotation`. + +The old parameter name remains supported, but +support for it will be dropped in a future Matplotlib release. + +`.font_manager.json_dump` now locks the font manager dump file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... to prevent multiple processes from writing to it at the same time. + +`.pyplot.rgrids` and `.pyplot.thetagrids` now act as setters also when called with only kwargs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, keyword arguments were silently ignored when no positional +arguments were given. + +`.Axis.get_minorticklabels` and `.Axis.get_majorticklabels` now returns plain list +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, `.Axis.get_minorticklabels` and `.Axis.get_majorticklabels` returns +silent_list. Their return type is now changed to normal list. +`.get_xminorticklabels`, `.get_yminorticklabels`, `.get_zminorticklabels`, +`.Axis.get_ticklabels`, `.get_xmajorticklabels`, `.get_ymajorticklabels` and +`.get_zmajorticklabels` methods will be affected by this change. + +Default slider formatter +~~~~~~~~~~~~~~~~~~~~~~~~ +The default method used to format `.Slider` values has been changed to use a +`.ScalarFormatter` adapted the slider values limits. This should ensure that +values are displayed with an appropriate number of significant digits even if +they are much smaller or much bigger than 1. To restore the old behavior, +explicitly pass a "%1.2f" as the *valfmt* parameter to `.Slider`. + +Add *normalize* keyword argument to ``Axes.pie`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``pie()`` used to draw a partial pie if the sum of the values was < 1. This behavior +is deprecated and will change to always normalizing the values to a full pie by default. +If you want to draw a partial pie, please pass ``normalize=False`` explicitly. + +``table.CustomCell`` is now an alias for `.table.Cell` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +All the functionality of ``CustomCell`` has been moved to its base class +`~.table.Cell`. + +wx Timer interval +~~~~~~~~~~~~~~~~~ +Setting the timer interval on a not-yet-started ``TimerWx`` won't start it +anymore. + +"step"-type histograms default to the zorder of `.Line2D` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This ensures that they go above gridlines by default. The old ``zorder`` can +be kept by passing it as a keyword argument to `.Axes.hist`. + +`.Legend` and `.OffsetBox` visibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`.Legend` and `.OffsetBox` subclasses (`.PaddedBox`, `.AnchoredOffsetbox`, and +`.AnnotationBbox`) no longer directly keep track of the visibility of their +underlying `.Patch` artist, but instead pass that flag down to the `.Patch`. + +`.Legend` and `.Table` no longer allow invalid locations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This affects legends produced on an Axes (`.Axes.legend` and `.pyplot.legend`) +and on a Figure (`.Figure.legend` and `.pyplot.figlegend`). Figure legends also +no longer accept the unsupported ``'best'`` location. Previously, invalid Axes +locations would use ``'best'`` and invalid Figure locations would used ``'upper +right'``. + +Passing Line2D's *drawstyle* together with *linestyle* is removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of ``plt.plot(..., linestyle="steps--")``, use ``plt.plot(..., +linestyle="--", drawstyle="steps")``. ``ds`` is also an alias for +``drawstyle``. + +Upper case color strings +~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for passing single-letter colors (one of "rgbcmykw") as UPPERCASE +characters is removed; these colors are now case-sensitive (lowercase). + +tight/constrained_layout no longer worry about titles that are too wide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*tight_layout* and *constrained_layout* shrink axes to accommodate +"decorations" on the axes. However, if an xlabel or title is too long in the +x direction, making the axes smaller in the x-direction doesn't help. The +behavior of both has been changed to ignore the width of the title and +xlabel and the height of the ylabel in the layout logic. + +This also means there is a new keyword argument for `.axes.Axes.get_tightbbox` +and `.axis.Axis.get_tightbbox`: ``for_layout_only``, which defaults to *False*, +but if *True* returns a bounding box using the rules above. + +:rc:`savefig.facecolor` and :rc:`savefig.edgecolor` now default to "auto" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This newly allowed value for :rc:`savefig.facecolor` and :rc:`savefig.edgecolor`, +as well as the *facecolor* and *edgecolor* parameters to `.Figure.savefig`, means +"use whatever facecolor and edgecolor the figure current has". + +When using a single dataset, `.Axes.hist` no longer wraps the added artist in a `.silent_list` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When `.Axes.hist` is called with a single dataset, it adds to the axes either +a `.BarContainer` object (when ``histtype="bar"`` or ``"barstacked"``), or a +`.Polygon` object (when ``histype="step"`` or ``"stepfilled"``) -- the latter +being wrapped in a list-of-one-element. Previously, either artist would be +wrapped in a `.silent_list`. This is no longer the case: the `.BarContainer` is +now returned as is (this is an API breaking change if you were directly relying +on the concrete `list` API; however, `.BarContainer` inherits from `tuple` so +most common operations remain available), and the list-of-one `.Polygon` is +returned as is. This makes the `repr` of the returned artist more accurate: it +is now :: + + # "bar", "barstacked" + [] # "step", "stepfilled" + +instead of :: + + # "bar", "barstacked" + # "step", "stepfilled" + +When `.Axes.hist` is called with multiple artists, it still wraps its return +value in a `.silent_list`, but uses more accurate type information :: + + # "bar", "barstacked" + # "step", "stepfilled" + +instead of :: + + # "bar", "barstacked" + # "step", "stepfilled" + +Qt and wx backends no longer create a status bar by default +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The coordinates information is now displayed in the toolbar, consistently with +the other backends. This is intended to simplify embedding of Matplotlib in +larger GUIs, where Matplotlib may control the toolbar but not the status bar. + +:rc:`text.hinting` now supports names mapping to FreeType flags +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:rc:`text.hinting` now supports the values "default", "no_autohint", +"force_autohint", and "no_hinting", which directly map to the FreeType flags +FT_LOAD_DEFAULT, etc. The old synonyms (respectively "either", "native", +"auto", and "none") are still supported, but their use is discouraged. To get +normalized values, use `.backend_agg.get_hinting_flag`, which returns integer +flag values. + +`.cbook.get_sample_data` auto-loads numpy arrays +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When `.cbook.get_sample_data` is used to load a npy or npz file and the +keyword-only parameter ``np_load`` is True, the file is automatically loaded +using `numpy.load`. ``np_load`` defaults to False for backwards compatibility, +but will become True in a later release. + +``get_text_width_height_descent`` now checks ``ismath`` rather than :rc:`text.usetex` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... to determine whether a string should be passed to the usetex machinery or +not. This allows single strings to be marked as not-usetex even when the +rcParam is True. + +`.Axes.vlines`, `.Axes.hlines`, `.pyplot.vlines` and `.pyplot.hlines` *colors* parameter default change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *colors* parameter will now default to :rc:`lines.color`, while previously it defaulted to 'k'. + +Aggressively autoscale clim in ``ScalerMappable`` classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Previously some plotting methods would defer autoscaling until the +first draw if only one of the *vmin* or *vmax* keyword arguments were +passed (`.Axes.scatter`, `.Axes.hexbin`, `.Axes.imshow`, +`.Axes.pcolorfast`) but would scale based on the passed data if +neither was passed (independent of the *norm* keyword arguments). +Other methods (`.Axes.pcolor`, `.Axes.pcolormesh`) always autoscaled +base on the initial data. + +All of the plotting methods now resolve the unset *vmin* or *vmax* +at the initial call time using the data passed in. + +If you were relying on exactly one of the *vmin* or *vmax* remaining +unset between the time when the method is called and the first time +the figure is rendered you get back the old behavior by manually setting +the relevant limit back to `None` :: + + cm_obj.norm.vmin = None + # or + cm_obj.norm.vmax = None + +which will be resolved during the draw process. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst new file mode 100644 index 000000000000..76c43b12aaaa --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.0/deprecations.rst @@ -0,0 +1,622 @@ +Deprecations +------------ + +``figure.add_axes()`` without arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Calling ``fig.add_axes()`` with no arguments currently does nothing. This call +will raise an error in the future. Adding a free-floating axes needs a position +rectangle. If you want a figure-filling single axes, use ``add_subplot()`` +instead. + +``backend_wx.DEBUG_MSG`` +~~~~~~~~~~~~~~~~~~~~~~~~ +``backend_wx.DEBUG_MSG`` is deprecated. The wx backends now use regular +logging. + +``Colorbar.config_axis()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +``Colorbar.config_axis()`` is considered internal. Its use is deprecated. + +``NonUniformImage.is_grayscale`` and ``PcolorImage.is_grayscale`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated, for consistency with ``AxesImage.is_grayscale``, +which was removed back in Matplotlib 2.0.0. (Note that previously, these +attributes were only available *after rendering the image*). + +``den`` parameter and attribute to :mod:`mpl_toolkits.axisartist.angle_helper` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For all locator classes defined in :mod:`mpl_toolkits.axisartist.angle_helper`, +the ``den`` parameter has been renamed to ``nbins``, and the ``den`` attribute +deprecated in favor of its (preexisting) synonym ``nbins``, for consistency +with locator classes defined in :mod:`matplotlib.ticker`. + +``backend_pgf.LatexManager.latex_stdin_utf8`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``backend_pgf.LatexManager.latex`` is now created with ``encoding="utf-8"``, so +its ``stdin`` attribute is already utf8-encoded; the ``latex_stdin_utf8`` +attribute is thus deprecated. + +Flags containing "U" passed to `.cbook.to_filehandle` and `.cbook.open_file_cm` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Please remove "U" from flags passed to `.cbook.to_filehandle` and +`.cbook.open_file_cm`. This is consistent with their removal from `open` in +Python 3.9. + +PDF and PS character tracking internals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``used_characters`` attribute and ``track_characters`` and +``merge_used_characters`` methods of `.RendererPdf`, `.PdfFile`, and +`.RendererPS` are deprecated. + +Case-insensitive capstyles and joinstyles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Please pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", +"round", "projecting") as lowercase. + +Passing raw data to ``register_cmap()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing raw data via parameters *data* and *lut* to ``matplotlib.cm.register_cmap()`` is +deprecated. Instead, explicitly create a `.LinearSegmentedColormap` and pass +it via the *cmap* parameter: +``register_cmap(cmap=LinearSegmentedColormap(name, data, lut))``. + +``DateFormatter.illegal_s`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This attribute is unused and deprecated. + +``widgets.TextBox.params_to_disable`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This attribute is deprecated. + +Revert deprecation \*min, \*max keyword arguments to ``set_x/y/zlim_3d()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These keyword arguments were deprecated in 3.0, alongside with the respective +parameters in ``set_xlim()`` / ``set_ylim()``. The deprecations of the 2D +versions were already reverted in 3.1. + +``cbook.local_over_kwdict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This function is deprecated. Use `.cbook.normalize_kwargs` instead. + +Passing both singular and plural *colors*, *linewidths*, *linestyles* to `.Axes.eventplot` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing e.g. both *linewidth* and *linewidths* will raise a TypeError in the +future. + +Setting ``text.latex.preamble`` or ``pdf.preamble`` rcParams to non-strings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These rcParams should be set to string values. Support for None (meaning the +empty string) and lists of strings (implicitly joined with newlines) is +deprecated. + +Parameters *norm* and *vmin*/*vmax* should not be used simultaneously +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing parameters *norm* and *vmin*/*vmax* simultaneously to functions using +colormapping such as ``scatter()`` and ``imshow()`` is deprecated. +Instead of ``norm=LogNorm(), vmin=min_val, vmax=max_val`` pass +``norm=LogNorm(min_val, max_val)``. *vmin* and *vmax* should only be used +without setting *norm*. + +Effectless parameters of `.Figure.colorbar` and `matplotlib.colorbar.Colorbar` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *cmap* and *norm* parameters of `.Figure.colorbar` and +`matplotlib.colorbar.Colorbar` have no effect because they are always +overridden by the mappable's colormap and norm; they are thus deprecated. +Likewise, passing the *alpha*, *boundaries*, *values*, *extend*, or *filled* +parameters with a `.ContourSet` mappable, or the *alpha* parameter with an +`.Artist` mappable, is deprecated, as the mappable would likewise override +them. + +``args_key`` and ``exec_key`` attributes of builtin `.MovieWriter`\s +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated. + +Unused parameters +~~~~~~~~~~~~~~~~~ +The following parameters do not have any effect and are deprecated: + +- arbitrary keyword arguments to ``StreamplotSet`` +- parameter *quantize* of `.Path.cleaned()` +- parameter *s* of `.AnnotationBbox.get_fontsize()` +- parameter *label* of `.Tick` + +Passing *props* to `.Shadow` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The parameter *props* of `.Shadow` is deprecated. Use keyword arguments +instead. + +``Axes.update_datalim_bounds`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. Use +``ax.dataLim.set(Bbox.union([ax.dataLim, bounds]))`` instead. + +``{,Symmetrical}LogScale.{,Inverted}LogTransform`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``LogScale.LogTransform``, ``LogScale.InvertedLogTransform``, +``SymmetricalScale.SymmetricalTransform`` and +``SymmetricalScale.InvertedSymmetricalTransform`` are deprecated. Directly +access the transform classes from the :mod:`.scale` module. + +``TexManager.cachedir``, ``TexManager.rgba_arrayd`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use `matplotlib.get_cachedir()` instead for the former; there is no replacement +for the latter. + +Setting `.Line2D`\'s pickradius via `.Line2D.set_picker` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting a `.Line2D`\'s pickradius (i.e. the tolerance for pick events +and containment checks) via `.Line2D.set_picker` is deprecated. Use +`.Line2D.set_pickradius` instead. + +`.Line2D.set_picker` no longer sets the artist's custom-contain() check. + +``Artist.set_contains``, ``Artist.get_contains`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting a custom method overriding `.Artist.contains` is deprecated. +There is no replacement, but you may still customize pick events using +`.Artist.set_picker`. + +`~matplotlib.colorbar.Colorbar` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``on_mappable_changed`` and ``update_bruteforce`` methods of +`~matplotlib.colorbar.Colorbar` are deprecated; both can be replaced by calls +to `~matplotlib.colorbar.Colorbar.update_normal`. + +``OldScalarFormatter``, ``IndexFormatter`` and ``IndexDateFormatter`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These formatters are deprecated. Their functionality can be implemented using +e.g. `.FuncFormatter`. + +``OldAutoLocator`` +~~~~~~~~~~~~~~~~~~ +This ticker is deprecated. + +*required*, *forbidden* and *allowed* parameters of `.cbook.normalize_kwargs` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These parameters are deprecated. + +The ``TTFPATH`` and ``AFMPATH`` environment variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for the (undocumented) ``TTFPATH`` and ``AFMPATH`` environment +variables is deprecated. Additional fonts may be registered using +``matplotlib.font_manager.fontManager.addfont()``. + +``matplotlib.compat`` +~~~~~~~~~~~~~~~~~~~~~ +This module is deprecated. + +``matplotlib.backends.qt_editor.formsubplottool`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This module is deprecated. Use ``matplotlib.backends.backend_qt5.SubplotToolQt`` +instead. + +AVConv animation writer deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``AVConvBase``, ``AVConvWriter`` and ``AVConvFileWriter`` classes, and the +associated ``animation.avconv_path`` and ``animation.avconv_args`` rcParams are +deprecated. + +Debian 8 (2015, EOL 06/2020) and Ubuntu 14.04 (EOL 04/2019) were the +last versions of Debian and Ubuntu to ship avconv. It remains possible +to force the use of avconv by using the ffmpeg-based writers with +:rc:`animation.ffmpeg_path` set to "avconv". + +log/symlog scale base, ticks, and nonpos specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +`~.Axes.semilogx`, `~.Axes.semilogy`, `~.Axes.loglog`, `.LogScale`, and +`.SymmetricalLogScale` used to take keyword arguments that depends on the axis +orientation ("basex" vs "basey", "subsx" vs "subsy", "nonposx" vs "nonposy"); +these parameter names are now deprecated in favor of "base", "subs", +"nonpositive". This deprecation also affects e.g. ``ax.set_yscale("log", +basey=...)`` which must now be spelled ``ax.set_yscale("log", base=...)``. + +The change from "nonpos" to "nonpositive" also affects `~.scale.LogTransform`, +`~.scale.InvertedLogTransform`, `~.scale.SymmetricalLogTransform`, etc. + +To use *different* bases for the x-axis and y-axis of a `~.Axes.loglog` plot, +use e.g. ``ax.set_xscale("log", base=10); ax.set_yscale("log", base=2)``. + +``DraggableBase.artist_picker`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. If you previously reimplemented it in a subclass, +set the artist's picker instead with `.Artist.set_picker`. + +*clear_temp* parameter and attribute of `.FileMovieWriter` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *clear_temp* parameter and attribute of `.FileMovieWriter` is +deprecated. In the future, files placed in a temporary directory (using +``frame_prefix=None``, the default) will be cleared; files placed elsewhere +will not. + +Deprecated rcParams validators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The following validators, defined in `.rcsetup`, are deprecated: +``validate_fontset``, ``validate_mathtext_default``, ``validate_alignment``, +``validate_svg_fonttype``, ``validate_pgf_texsystem``, +``validate_movie_frame_fmt``, ``validate_axis_locator``, +``validate_movie_html_fmt``, ``validate_grid_axis``, +``validate_axes_titlelocation``, ``validate_toolbar``, +``validate_ps_papersize``, ``validate_legend_loc``, +``validate_bool_maybe_none``, ``validate_hinting``, +``validate_movie_writer``, ``validate_webagg_address``, +``validate_nseq_float``, ``validate_nseq_int``. +To test whether an rcParam value would be acceptable, one can test e.g. ``rc = +RcParams(); rc[k] = v`` raises an exception. + +Stricter rcParam validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:rc:`axes.axisbelow` currently normalizes all strings starting with "line" +(case-insensitive) to the option "line". This is deprecated; in a future +version only the exact string "line" (case-sensitive) will be supported. + +``add_subplot()`` validates its inputs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In particular, for ``add_subplot(rows, cols, index)``, all parameters must +be integral. Previously strings and floats were accepted and converted to +int. This will now emit a deprecation warning. + +Toggling axes navigation from the keyboard using "a" and digit keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Axes navigation can still be toggled programmatically using +`.Axes.set_navigate`. + +The following related APIs are also deprecated: +``backend_tools.ToolEnableAllNavigation``, +``backend_tools.ToolEnableNavigation``, and ``rcParams["keymap.all_axes"]``. + +``matplotlib.test(recursionlimit=...)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *recursionlimit* parameter of ``matplotlib.test`` is deprecated. + +mathtext glues +~~~~~~~~~~~~~~ +The *copy* parameter of ``mathtext.Glue`` is deprecated (the underlying glue +spec is now immutable). ``mathtext.GlueSpec`` is deprecated. + +Signatures of `.Artist.draw` and `matplotlib.axes.Axes.draw` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *inframe* parameter to `matplotlib.axes.Axes.draw` is deprecated. Use +`.Axes.redraw_in_frame` instead. + +Not passing the *renderer* parameter to `matplotlib.axes.Axes.draw` is +deprecated. Use ``axes.draw_artist(axes)`` instead. + +These changes make the signature of the ``draw`` (``artist.draw(renderer)``) +method consistent across all artists; thus, additional parameters to +`.Artist.draw` are deprecated. + +``DraggableBase.on_motion_blit`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. `.DraggableBase.on_motion` now handles both the +blitting and the non-blitting cases. + +Passing the dash offset as None +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Fine control of dash patterns can be achieved by passing an ``(offset, +(on-length, off-length, on-length, off-length, ...))`` pair as the linestyle +property of `.Line2D` and `.LineCollection`. Previously, certain APIs would +accept ``offset = None`` as a synonym for ``offset = 0``, but this was never +universally implemented, e.g. for vector output. Support for ``offset = None`` +is deprecated, set the offset to 0 instead. + +``RendererCairo.fontweights``, ``RendererCairo.fontangles`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated. + +``autofmt_xdate(which=None)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This is deprecated, use its more explicit synonym, ``which="major"``, instead. + +JPEG options +~~~~~~~~~~~~ +The *quality*, *optimize*, and *progressive* keyword arguments to +`~.Figure.savefig`, which were only used when saving to JPEG, are deprecated. +The ``savefig.jpeg_quality`` rcParam is likewise deprecated. + +Such options should now be directly passed to Pillow using +``savefig(..., pil_kwargs={"quality": ..., "optimize": ..., "progressive": ...})``. + +``dviread.Encoding`` +~~~~~~~~~~~~~~~~~~~~ +This class was (mostly) broken and is deprecated. + +Axis and Locator ``pan`` and ``zoom`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The unused ``pan`` and ``zoom`` methods of `~.axis.Axis` and `~.ticker.Locator` +are deprecated. Panning and zooming are now implemented using the +``start_pan``, ``drag_pan``, and ``end_pan`` methods of `~.axes.Axes`. + +Passing None to various Axes subclass factories +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for passing ``None`` as base class to ``axes.subplot_class_factory``, +``axes_grid1.parasite_axes.host_axes_class_factory``, +``axes_grid1.parasite_axes.host_subplot_class_factory``, +``axes_grid1.parasite_axes.parasite_axes_class_factory``, and +``axes_grid1.parasite_axes.parasite_axes_auxtrans_class_factory`` is deprecated. +Explicitly pass the correct base ``Axes`` class instead. + +``axes_rgb`` +~~~~~~~~~~~~ +In :mod:`mpl_toolkits.axes_grid1.axes_rgb`, ``imshow_rgb`` is deprecated (use +``ax.imshow(np.dstack([r, g, b]))`` instead); ``RGBAxesBase`` is deprecated +(use ``RGBAxes`` instead); ``RGBAxes.add_RGB_to_figure`` is deprecated (it was +an internal helper). + +``Substitution.from_params`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. If needed, directly assign to the ``params`` +attribute of the Substitution object. + +PGF backend cleanups +~~~~~~~~~~~~~~~~~~~~ +The *dummy* parameter of `.RendererPgf` is deprecated. + +``GraphicsContextPgf`` is deprecated (use `.GraphicsContextBase` instead). + +``set_factor`` method of :mod:`mpl_toolkits.axisartist` locators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``set_factor`` method of :mod:`mpl_toolkits.axisartist` locators (which are +different from "standard" Matplotlib tick locators) is deprecated. + +`.widgets.SubplotTool` callbacks and axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``funcleft``, ``funcright``, ``funcbottom``, ``functop``, ``funcwspace``, +and ``funchspace`` methods of `.widgets.SubplotTool` are deprecated. + +The ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and +``axhspace`` attributes of `.widgets.SubplotTool` are deprecated. Access the +``ax`` attribute of the corresponding slider, if needed. + +mathtext ``Glue`` helper classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``Fil``, ``Fill``, ``Filll``, ``NegFil``, ``NegFill``, ``NegFilll``, and +``SsGlue`` classes in the :mod:`matplotlib.mathtext` module are deprecated. +As an alternative, directly construct glue instances with ``Glue("fil")``, etc. + +NavigationToolbar2._init_toolbar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Overriding this method to initialize third-party toolbars is deprecated. +Instead, the toolbar should be initialized in the ``__init__`` method of the +subclass (which should call the base-class' ``__init__`` as appropriate). To +keep back-compatibility with earlier versions of Matplotlib (which *required* +``_init_toolbar`` to be overridden), a fully empty implementation (``def +_init_toolbar(self): pass``) may be kept and will not trigger the deprecation +warning. + +NavigationToolbar2QT.parent and .basedir +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated. In order to access the parent window, use +``toolbar.canvas.parent()``. Once the deprecation period is elapsed, it will +also be accessible as ``toolbar.parent()``. The base directory to the icons +is ``os.path.join(mpl.get_data_path(), "images")``. + +NavigationToolbar2QT.ctx +~~~~~~~~~~~~~~~~~~~~~~~~ +This attribute is deprecated. + +NavigationToolbar2Wx attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``prevZoomRect``, ``retinaFix``, ``savedRetinaImage``, ``wxoverlay``, +``zoomAxes``, ``zoomStartX``, and ``zoomStartY`` attributes are deprecated. + +NavigationToolbar2.press and .release +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These methods were called when pressing or releasing a mouse button, +but *only* when an interactive pan or zoom was occurring (contrary to +what the docs stated). They are deprecated; if you write a backend +which needs to customize such events, please directly override +``press_pan``/``press_zoom``/``release_pan``/``release_zoom`` instead. + +FigureCanvasGTK3._renderer_init +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Overriding this method to initialize renderers for GTK3 canvases is deprecated. +Instead, the renderer should be initialized in the ``__init__`` method of the +subclass (which should call the base-class' ``__init__`` as appropriate). To +keep back-compatibility with earlier versions of Matplotlib (which *required* +``_renderer_init`` to be overridden), a fully empty implementation (``def +_renderer_init(self): pass``) may be kept and will not trigger the deprecation +warning. + +Path helpers in :mod:`.bezier` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``bezier.make_path_regular`` is deprecated. Use ``Path.cleaned()`` (or +``Path.cleaned(curves=True)``, etc.) instead (but note that these methods add a +``STOP`` code at the end of the path). + +``bezier.concatenate_paths`` is deprecated. Use ``Path.make_compound_path()`` +instead. + +``animation.html_args`` rcParam +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The unused ``animation.html_args`` rcParam and ``animation.HTMLWriter.args_key`` +attribute are deprecated. + +``text.latex.preview`` rcParam +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This rcParam, which controlled the use of the preview.sty LaTeX package to +align TeX string baselines, is deprecated, as Matplotlib's own dvi parser now +computes baselines just as well as preview.sty. + +``SubplotSpec.get_rows_columns`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. Use the ``GridSpec.nrows``, ``GridSpec.ncols``, +``SubplotSpec.rowspan``, and ``SubplotSpec.colspan`` properties instead. + +Qt4-based backends +~~~~~~~~~~~~~~~~~~ +The qt4agg and qt4cairo backends are deprecated. Qt4 has reached its +end-of-life in 2015 and there are no releases for recent versions of Python. +Please consider switching to Qt5. + +*fontdict* and *minor* parameters of `.Axes.set_xticklabels` and `.Axes.set_yticklabels` will become keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All parameters of `.Figure.subplots` except *nrows* and *ncols* will become keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This avoids typing e.g. ``subplots(1, 1, 1)`` when meaning ``subplot(1, 1, 1)``, +but actually getting ``subplots(1, 1, sharex=1)``. + +``RendererWx.get_gc`` +~~~~~~~~~~~~~~~~~~~~~ +This method is deprecated. Access the ``gc`` attribute directly instead. + +*add_all* parameter in ``axes_grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *add_all* parameter of `.axes_grid1.axes_grid.Grid`, +`.axes_grid1.axes_grid.ImageGrid`, `.axes_grid1.axes_rgb.make_rgb_axes` and +`.axes_grid1.axes_rgb.RGBAxes` is deprecated. Axes are now always added to the +parent figure, though they can be later removed with ``ax.remove()``. + +``BboxBase.inverse_transformed`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``.BboxBase.inverse_transformed`` is deprecated (call `.BboxBase.transformed` +on the `~.Transform.inverted()` transform instead). + +*orientation* of ``eventplot()`` and `.EventCollection` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting the *orientation* of an ``eventplot()`` or `.EventCollection` to "none" +or None is deprecated; set it to "horizontal" instead. Moreover, the two +orientations ("horizontal" and "vertical") will become case-sensitive in the +future. + +*minor* kwarg to `.Axis.get_ticklocs` will become keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing this argument positionally is deprecated. + +Case-insensitive properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Normalization of upper or mixed-case property names to lowercase in +`.Artist.set` and `.Artist.update` is deprecated. In the future, property +names will be passed as is, allowing one to pass names such as *patchA* or +*UVC*. + +``ContourSet.ax``, ``Quiver.ax`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated in favor of ``ContourSet.axes`` and +``Quiver.axes``, for consistency with other artists. + +``Locator.refresh()`` and associated methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``Locator.refresh()`` is deprecated. This method was called at certain places +to let locators update their internal state, typically based on the axis +limits. Locators should now always consult the axis limits when called, if +needed. + +The associated helper methods ``NavigationToolbar2.draw()`` and +``ToolViewsPositions.refresh_locators()`` are deprecated, and should be +replaced by calls to ``draw_idle()`` on the corresponding canvas. + +`.ScalarMappable` checkers +~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``add_checker`` and ``check_update`` methods and ``update_dict`` attribute +of `.ScalarMappable` are deprecated. + +`.pyplot.tight_layout` and ``ColorbarBase`` parameters will become keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +All parameters of `.pyplot.tight_layout` and all parameters of ``ColorbarBase`` +except for the first (*ax*) will become keyword-only, consistently with +`.Figure.tight_layout` and ``Colorbar``, respectively. + +`.Axes.pie` radius and startangle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing ``None`` as either the ``radius`` or ``startangle`` of an `.Axes.pie` +is deprecated; use the explicit defaults of 1 and 0, respectively, instead. + +``AxisArtist.dpi_transform`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. Scale ``Figure.dpi_scale_trans`` by 1/72 to achieve the +same effect. + +``offset_position`` property of `.Collection` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``offset_position`` property of `.Collection` is deprecated. In the +future, `.Collection`\s will always behave as if ``offset_position`` is set to +"screen" (the default). + +Support for passing ``offset_position="data"`` to the ``draw_path_collection`` +of all renderer classes is deprecated. + +`.transforms.AffineDeltaTransform` can be used as a replacement. This API is +experimental and may change in the future. + +``testing.compare.make_external_conversion_command`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. + +``epoch2num`` and ``num2epoch`` are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These are unused and can be easily reproduced by other date tools. +`.get_epoch` will return Matplotlib's epoch. + +``axes_grid1.CbarAxes`` attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``cbid`` and ``locator`` attribute are deprecated. Use +``mappable.colorbar_cid`` and ``colorbar.locator``, as for standard colorbars. + +``qt_compat.is_pyqt5`` +~~~~~~~~~~~~~~~~~~~~~~ +This function is deprecated in prevision of the future release of PyQt6. The +Qt version can be checked using ``QtCore.qVersion()``. + +Reordering of parameters by `.Artist.set` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In a future version, ``Artist.set`` will apply artist properties in the order +in which they are given. This only affects the interaction between the +*color*, *edgecolor*, *facecolor*, and, for `.Collection`\s, *alpha* +properties: the *color* property now needs to be passed first in order not to +override the other properties. This is consistent with e.g. `.Artist.update`, +which did not reorder the properties passed to it. + +Passing multiple keys as a single comma-separated string or multiple arguments to `.ToolManager.update_keymap` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This is deprecated; pass keys as a list of strings instead. + +Statusbar classes and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``statusbar`` attribute of `.FigureManagerBase`, ``StatusbarBase`` and all +its subclasses, and ``StatusBarWx``, are deprecated, as messages are now +displayed in the toolbar instead. + +``ismath`` parameter of ``draw_tex`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``ismath`` parameter of the ``draw_tex`` method of all renderer classes is +deprecated (as a call to ``draw_tex`` -- not to be confused with ``draw_text``! +-- means that the entire string should be passed to the ``usetex`` machinery +anyways). Likewise, the text machinery will no longer pass the ``ismath`` +parameter when calling ``draw_tex`` (this should only matter for backend +implementers). + +Passing ``ismath="TeX!"`` to `.RendererAgg.get_text_width_height_descent` is +deprecated. Pass ``ismath="TeX"`` instead, consistently with other low-level +APIs which support the values True, False, and "TeX" for ``ismath``. + +``matplotlib.ttconv`` +~~~~~~~~~~~~~~~~~~~~~ +This module is deprecated. + + +Stricter PDF metadata keys in PGF +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Saving metadata in PDF with the PGF backend currently normalizes all keys to +lowercase, unlike the PDF backend, which only accepts the canonical case. This +is deprecated; in a future version, only the canonically cased keys listed in +the PDF specification (and the `~.backend_pgf.PdfPages` documentation) will be +accepted. + + +Qt modifier keys +~~~~~~~~~~~~~~~~ +The ``MODIFIER_KEYS``, ``SUPER``, ``ALT``, ``CTRL``, and ``SHIFT`` +global variables of the ``matplotlib.backends.backend_qt4agg``, +``matplotlib.backends.backend_qt4cairo``, +:mod:`matplotlib.backends.backend_qt5agg` and +:mod:`matplotlib.backends.backend_qt5cairo` modules are deprecated. + +``TexManager`` +~~~~~~~~~~~~~~ + +The ``TexManager.serif``, ``TexManager.sans_serif``, +``TexManager.cursive`` and ``TexManager.monospace`` attributes are +deprecated. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/development.rst b/doc/api/prev_api_changes/api_changes_3.3.0/development.rst new file mode 100644 index 000000000000..9fa11e8a484a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.0/development.rst @@ -0,0 +1,16 @@ +Development changes +------------------- + +Matplotlib now requires numpy>=1.15 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Matplotlib now uses Pillow to save and read pngs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The builtin png encoder and decoder has been removed, and Pillow is now a +dependency. Note that when reading 16-bit RGB(A) images, Pillow truncates them +to 8-bit precision, whereas the old builtin decoder kept the full precision. + +The deprecated wx backend (not wxagg!) now always uses wx's builtin jpeg and +tiff support rather than relying on Pillow for writing these formats; this +behavior is consistent with wx's png output. diff --git a/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst new file mode 100644 index 000000000000..36b63c6dcfc8 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.0/removals.rst @@ -0,0 +1,246 @@ +Removals +-------- +The following deprecated APIs have been removed: + +Modules +~~~~~~~ +- ``backends.qt_editor.formlayout`` (use the formlayout module available on + PyPI instead). + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- ``artist.Artist.aname`` property (no replacement) + +- ``axis.Axis.iter_ticks`` (no replacement) + +- Support for custom backends that do not provide a + ``backend_bases.GraphicsContextBase.set_hatch_color`` method +- ``backend_bases.RendererBase.strip_math()`` + (use ``cbook.strip_math()`` instead) + +- ``backend_wx.debug_on_error()`` (no replacement) +- ``backend_wx.raise_msg_to_str()`` (no replacement) +- ``backend_wx.fake_stderr`` (no replacement) +- ``backend_wx.MenuButtonWx`` (no replacement) +- ``backend_wx.PrintoutWx`` (no replacement) +- ``_backend_tk.NavigationToolbar2Tk.set_active()`` (no replacement) + +- ``backend_ps.PsBackendHelper.gs_exe`` property (no replacement) +- ``backend_ps.PsBackendHelper.gs_version`` property (no replacement) +- ``backend_ps.PsBackendHelper.supports_ps2write`` property (no replacement) +- ``backend_ps.RendererPS.afmfontd`` property (no replacement) +- ``backend_ps.GraphicsContextPS.shouldstroke`` property (no replacement) + +- ``backend_gtk3.FileChooserDialog`` (no replacement) +- ``backend_gtk3.SaveFigureGTK3.get_filechooser()`` (no replacement) +- ``backend_gtk3.NavigationToolbar2GTK3.get_filechooser()`` (no replacement) + +- ``backend_gtk3cairo.FigureManagerGTK3Cairo`` + (use ``backend_gtk3.FigureManagerGTK3`` instead) + +- ``backend_pdf.RendererPdf.afm_font_cache`` property (no replacement) + +- ``backend_pgf.LatexManagerFactory`` (no replacement) + +- ``backend_qt5.NavigationToolbar2QT.buttons`` property (no replacement) +- ``backend_qt5.NavigationToolbar2QT.adj_window`` property (no replacement) + +- ``bezier.find_r_to_boundary_of_closedpath()`` (no replacement) + +- ``cbook.dedent()`` (use `inspect.cleandoc` instead) +- ``cbook.get_label()`` (no replacement) +- ``cbook.is_hashable()`` (use ``isinstance(..., collections.abc.Hashable)`` + instead) +- ``cbook.iterable()`` (use ``numpy.iterable()`` instead) +- ``cbook.safezip()`` (no replacement) + +- ``colorbar.ColorbarBase.get_cmap`` (use ``ScalarMappable.get_cmap`` instead) +- ``colorbar.ColorbarBase.set_cmap`` (use ``ScalarMappable.set_cmap`` instead) +- ``colorbar.ColorbarBase.get_clim`` (use ``ScalarMappable.get_clim`` instead) +- ``colorbar.ColorbarBase.set_clim`` (use ``ScalarMappable.set_clim`` instead) +- ``colorbar.ColorbarBase.set_norm`` (use ``ScalarMappable.set_norm`` instead) + +- ``dates.seconds()`` (no replacement) +- ``dates.minutes()`` (no replacement) +- ``dates.hours()`` (no replacement) +- ``dates.weeks()`` (no replacement) +- ``dates.strpdate2num`` and ``dates.bytespdate2num`` (use `time.strptime` or + `dateutil.parser.parse` or `.dates.datestr2num` instead) + +- ``docstring.Appender`` (no replacement) +- ``docstring.dedent()`` (use `inspect.getdoc` instead) +- ``docstring.copy_dedent()`` + (use ``docstring.copy()`` and `inspect.getdoc` instead) + +- ``font_manager.OSXInstalledFonts()`` (no replacement) + +- ``image.BboxImage.interp_at_native`` property (no replacement) + +- ``lines.Line2D.verticalOffset`` property (no replacement) + +- ``matplotlib.checkdep_dvipng`` (no replacement) +- ``matplotlib.checkdep_ghostscript`` (no replacement) +- ``matplotlib.checkdep_pdftops`` (no replacement) +- ``matplotlib.checkdep_inkscape`` (no replacement) +- ``matplotlib.get_py2exe_datafiles`` (no replacement) +- ``matplotlib.tk_window_focus`` (use ``rcParams['tk.window_focus']`` instead) + +- ``mlab.demean()`` (use ``mlab.detrend_mean()`` instead) + +- ``path.get_paths_extents()`` + (use ``path.get_path_collection_extents()`` instead) +- ``path.Path.has_nonfinite()`` (use ``not np.isfinite(self.vertices).all()`` + instead) + +- ``projections.process_projection_requirements()`` (no replacement) + +- ``pyplot.plotfile()`` (Instead, load the data using + `pandas.read_csv` or `numpy.loadtxt` or similar and use regular pyplot + functions to plot the loaded data.) + +- ``quiver.Quiver.color()`` (use ``Quiver.get_facecolor()`` instead) +- ``quiver.Quiver.keyvec`` property (no replacement) +- ``quiver.Quiver.keytext`` property (no replacement) + +- ``rcsetup.validate_qt4()`` (no replacement) +- ``rcsetup.validate_qt5()`` (no replacement) +- ``rcsetup.validate_verbose()`` (no replacement) +- ``rcsetup.ValidateInterval`` (no replacement) + +- ``scale.LogTransformBase`` (use ``scale.LogTransform`` instead) +- ``scale.InvertedLogTransformBase`` (use ``scale.InvertedLogTransform`` instead) +- ``scale.Log10Transform`` (use ``scale.LogTransform`` instead) +- ``scale.InvertedLog10Transform`` (use ``scale.InvertedLogTransform`` instead) +- ``scale.Log2Transform`` (use ``scale.LogTransform`` instead) +- ``scale.InvertedLog2Transform`` (use ``scale.InvertedLogTransform`` instead) +- ``scale.NaturalLogTransform`` (use ``scale.LogTransform`` instead) +- ``scale.InvertedNaturalLogTransform`` (use ``scale.InvertedLogTransform`` instead) +- ``scale.get_scale_docs()`` (no replacement) + +- ``sphinxext.plot_directive.plot_directive()`` + (use the class ``PlotDirective`` instead) +- ``sphinxext.mathmpl.math_directive()`` + (use the class ``MathDirective`` instead) + +- ``spines.Spine.is_frame_like()`` (no replacement) + +- ``testing.decorators.switch_backend()`` (use ``@pytest.mark.backend`` + decorator instead) + +- ``text.Text.is_math_text()`` (use ``cbook.is_math_text()`` instead) +- ``text.TextWithDash()`` (use ``text.Annotation`` instead) +- ``textpath.TextPath.is_math_text()`` (use ``cbook.is_math_text()`` instead) +- ``textpath.TextPath.text_get_vertices_codes()`` + (use ``textpath.text_to_path.get_text_path()`` instead) + +- ``textpath.TextToPath.glyph_to_path()`` (use ``font.get_path()`` and manual + translation of the vertices instead) + +- ``ticker.OldScalarFormatter.pprint_val()`` (no replacement) +- ``ticker.ScalarFormatter.pprint_val()`` (no replacement) +- ``ticker.LogFormatter.pprint_val()`` (no replacement) +- ``ticker.decade_down()`` (no replacement) +- ``ticker.decade_up()`` (no replacement) +- ``Tick`` properties ``gridOn``, ``tick1On``, ``tick2On``, ``label1On``, + ``label2On`` (use ``set_visible()`` / ``get_visible()`` on ``Tick.gridline``, + ``Tick.tick1line``, ``Tick.tick2line``, ``Tick.label1``, ``Tick.label2`` + instead) + +- ``widgets.SpanSelector.buttonDown`` property (no replacement) + +- ``mplot3d.proj3d.line2d()`` (no replacement) +- ``mplot3d.proj3d.line2d_dist()`` (no replacement) +- ``mplot3d.proj3d.line2d_seg_dist()`` (no replacement) +- ``mplot3d.proj3d.mod()`` (use `numpy.linalg.norm` instead) +- ``mplot3d.proj3d.proj_transform_vec()`` (no replacement) +- ``mplot3d.proj3d.proj_transform_vec_clip()`` (no replacement) +- ``mplot3d.proj3d.vec_pad_ones()`` (no replacement) +- ``mplot3d.proj3d.proj_trans_clip_points()`` (no replacement) + +- ``mplot3d.art3d.norm_angle()`` (no replacement) +- ``mplot3d.art3d.norm_text_angle()`` (no replacement) +- ``mplot3d.art3d.path_to_3d_segment()`` (no replacement) +- ``mplot3d.art3d.paths_to_3d_segments()`` (no replacement) +- ``mplot3d.art3d.path_to_3d_segment_with_codes()`` (no replacement) +- ``mplot3d.art3d.paths_to_3d_segments_with_codes()`` (no replacement) +- ``mplot3d.art3d.get_patch_verts()`` (no replacement) +- ``mplot3d.art3d.get_colors()`` (no replacement) +- ``mplot3d.art3d.zalpha()`` (no replacement) + +- ``mplot3d.axis3d.get_flip_min_max()`` (no replacement) +- ``mplot3d.axis3d.Axis.get_tick_positions()`` (no replacement) + +- ``axisartist.axis_artist.UnimplementedException`` (no replacement) +- ``axisartist.axislines.SimpleChainedObjects`` + (use ``axis_grid1.mpl_axes.SimpleChainedObjects`` instead) +- ``axisartist.axislines.Axes.AxisDict`` + (use ``axis_grid1.mpl_axes.Axes.AxisDict`` instead) + +Arguments +~~~~~~~~~ +- ``Axes.text()`` / ``pyplot.text()`` do not support the parameter ``withdash`` + anymore. Use ``Axes.annotate()`` and ``pyplot.annotate()`` instead. +- The first parameter of `matplotlib.use` has been renamed from ``arg`` to + ``backend`` (only relevant if you pass by keyword). +- The parameter ``warn`` of `matplotlib.use` has been removed. A failure to + switch the backend will now always raise an ``ImportError`` if ``force`` is + set; catch that error if necessary. +- All parameters of `matplotlib.use` except the first one are now keyword-only. +- The unused parameters ``shape`` and ``imlim`` of `~.axes.Axes.imshow()` are + now removed. All parameters beyond ``extent`` are now keyword-only. +- The unused parameter ``interp_at_native`` of `.BboxImage` has been removed. +- The parameter ``usetex`` of `.TextToPath.get_text_path` has been removed. + Use ``ismath='TeX'`` instead. +- The parameter ``block`` of ``show()`` is now keyword-only, and arbitrary + arguments or keyword arguments are no longer accepted. +- The parameter ``frameon`` of `.Figure.savefig` has been removed. Use + ``facecolor="none"`` to get a transparent background. +- Passing a ``wx.EvtHandler`` as the first argument to ``backend_wx.TimerWx`` + is not supported anymore; the signature of ``TimerWx`` is now consistent with + `.TimerBase`. +- The ``manage_xticks`` parameter of `~.Axes.boxplot` and `~.Axes.bxp` has been + renamed to ``manage_ticks``. +- The ``normed`` parameter of `~.Axes.hist2d` has been renamed to ``density``. +- The ``s`` parameter of `.Annotation` has been renamed to ``text``. +- For all functions in `.bezier` that supported a ``tolerance`` parameter, this + parameter has been renamed to ``tolerance``. +- ``axis("normal")`` is not supported anymore. Use the equivalent + ``axis("auto")`` instead. +- ``axis()`` does not accept arbitrary keyword arguments anymore. +- ``Axis.set_ticklabels()`` does not accept arbitrary positional arguments + other than ``ticklabels``. +- ``mpl_toolkits.mplot3d.art3d.Poly3DCollection.set_zsort`` does not accept + the value ``True`` anymore. Pass the equivalent value 'average' instead. +- `.AnchoredText` no longer accepts ``horizontalalignment`` or + ``verticalalignment`` keyword arguments. +- `.ConnectionPatch` no longer accepts the ``arrow_transmuter`` and + ``connector`` keyword arguments, which did nothing since 3.0. +- `.FancyArrowPatch` no longer accepts the ``arrow_transmuter`` and + ``connector`` keyword arguments, which did nothing since 3.0. +- `.TextPath` no longer accepts arbitrary positional or keyword arguments. +- `.MaxNLocator.set_params()` no longer accepts arbitrary keyword arguments. +- `~.Axes.pie` no longer accepts and squeezes non-1D inputs; pass 1D input to + the ``x`` argument. +- Passing (n, 1)-shaped error arrays to `.Axes.errorbar()` is no longer + supported; pass a 1D array instead. + +rcParams +~~~~~~~~ +- The ``text.latex.unicode`` rcParam has been removed, with no replacement. + Matplotlib now always supports unicode in usetex. +- The ``savefig.frameon`` rcParam has been removed. Set + :rc:`savefig.facecolor` to "none" to get a transparent background. +- The ``pgf.debug``, ``verbose.fileo`` and ``verbose.verbose.level`` rcParams, + which had no effect, have been removed. +- Support for setting :rc:`mathtext.default` to "circled" has been removed. + +Environment variables +~~~~~~~~~~~~~~~~~~~~~ +- ``MATPLOTLIBDATA`` (no replacement). + +mathtext +~~~~~~~~ +- The ``\stackrel`` command (which behaved differently from its LaTeX version) + has been removed. Use ``\genfrac`` instead. +- The ``\mathcircled`` command has been removed. Directly use Unicode + characters, such as ``'\N{CIRCLED LATIN CAPITAL LETTER A}'``, instead. diff --git a/doc/api/prev_api_changes/api_changes_3.3.1.rst b/doc/api/prev_api_changes/api_changes_3.3.1.rst new file mode 100644 index 000000000000..3eda8a9a3a1a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.3.1.rst @@ -0,0 +1,22 @@ +API Changes for 3.3.1 +===================== + +Deprecations +------------ + +Reverted deprecation of ``num2epoch`` and ``epoch2num`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These two functions were deprecated in 3.3.0, and did not return +an accurate Matplotlib datenum relative to the new Matplotlib epoch +handling (`~.dates.get_epoch` and :rc:`date.epoch`). This version +reverts the deprecation. + +Functions ``epoch2num`` and ``dates.julian2num`` use ``date.epoch`` rcParam +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now ``epoch2num`` and (undocumented) ``julian2num`` return floating point +days since `~.dates.get_epoch` as set by :rc:`date.epoch`, instead of +floating point days since the old epoch of "0000-12-31T00:00:00". If +needed, you can translate from the new to old values as +``old = new + mdates.date2num(np.datetime64('0000-12-31'))`` diff --git a/doc/api/prev_api_changes/api_changes_3.4.0.rst b/doc/api/prev_api_changes/api_changes_3.4.0.rst new file mode 100644 index 000000000000..309c1c677e1c --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.4.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.4.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.4.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.4.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.4.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst new file mode 100644 index 000000000000..e35301c11986 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.0/behaviour.rst @@ -0,0 +1,353 @@ +Behaviour changes +----------------- + +Constrained layout rewrite +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The layout manager ``constrained_layout`` was re-written with different outer +constraints that should be more robust to complicated subplot layouts. +User-facing changes are: + +- some poorly constrained layouts will have different width/height plots than + before. +- colorbars now respect the ``anchor`` keyword argument of + `matplotlib.colorbar.make_axes` +- colorbars are wider. +- colorbars in different rows or columns line up more robustly. +- *hspace* and *wspace* options to `.Figure.set_constrained_layout_pads` were + twice as wide as the docs said they should be. So these now follow the docs. + +This feature will remain "experimental" until the new changes have been used +enough by users, so we anticipate version 3.5 or 3.6. On the other hand, +``constrained_layout`` is extensively tested and used in examples in the +library, so using it should be safe, but layouts may not be exactly the same as +more development takes place. + +Details of using ``constrained_layout``, and its algorithm are available at +:ref:`constrainedlayout_guide` + +``plt.subplot`` re-selection without keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The purpose of `.pyplot.subplot` is to facilitate creating and re-selecting +Axes in a Figure when working strictly in the implicit pyplot API. When +creating new Axes it is possible to select the projection (e.g. polar, 3D, or +various cartographic projections) as well as to pass additional keyword +arguments through to the Axes-subclass that is created. + +The first time `.pyplot.subplot` is called for a given position in the Axes +grid it always creates and returns a new Axes with the passed arguments and +projection (defaulting to rectilinear). On subsequent calls to +`.pyplot.subplot` we have to determine if an existing Axes has a) equivalent +parameters, in which case it should be selected as the current Axes and +returned, or b) different parameters, in which case a new Axes is created and +the existing Axes is removed. This leaves the question of what is "equivalent +parameters". + +Previously it was the case that an existing Axes subclass, except for Axes3D, +would be considered equivalent to a 2D rectilinear Axes, despite having +different projections, if the keyword arguments (other than *projection*) +matched. Thus:: + + ax1 = plt.subplot(1, 1, 1, projection='polar') + ax2 = plt.subplots(1, 1, 1) + ax1 is ax2 + +We are embracing this long standing behavior to ensure that in the case when no +keyword arguments (of any sort) are passed to `.pyplot.subplot` any existing +Axes is returned, without consideration for keywords or projection used to +initially create it. This will cause a change in behavior when additional +keywords were passed to the original Axes:: + + ax1 = plt.subplot(111, projection='polar', theta_offset=.75) + ax2 = plt.subplots(1, 1, 1) + ax1 is ax2 # new behavior + # ax1 is not ax2 # old behavior, made a new axes + + ax1 = plt.subplot(111, label='test') + ax2 = plt.subplots(1, 1, 1) + ax1 is ax2 # new behavior + # ax1 is not ax2 # old behavior, made a new axes + +For the same reason, if there was an existing Axes that was not rectilinear, +passing ``projection='rectilinear'`` would reuse the existing Axes :: + + ax1 = plt.subplot(projection='polar') + ax2 = plt.subplot(projection='rectilinear') + ax1 is not ax2 # new behavior, makes new Axes + # ax1 is ax2 # old behavior + +contrary to the user's request. + +Previously Axes3D could not be re-selected with `.pyplot.subplot` due to an +unrelated bug (also fixed in Matplotlib 3.4). While Axes3D are now consistent +with all other projections there is a change in behavior for :: + + plt.subplot(projection='3d') # create a 3D Axes + + plt.subplot() # now returns existing 3D Axes, but + # previously created new 2D Axes + + plt.subplot(projection='rectilinear') # to get a new 2D Axes + +``ioff`` and ``ion`` can be used as context managers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.pyplot.ion` and `.pyplot.ioff` may now be used as context managers to create +a context with interactive mode on or off, respectively. The old behavior of +calling these functions is maintained. To use the new functionality call as:: + + with plt.ioff(): + # non-interactive code + +Locators and formatters must be in the class hierarchy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Axis locators and formatters must now be subclasses of +`~matplotlib.ticker.Locator` and `~matplotlib.ticker.Formatter` respectively. + +Date locator for DAILY interval now returns middle of month +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `matplotlib.dates.AutoDateLocator` has a default of +``interval_multiples=True`` that attempts to align ticks with the start of +meaningful intervals like the start of the month, or start of the day, etc. +That lead to approximately 140-day intervals being mapped to the first and 22nd +of the month. This has now been changed so that it chooses the first and 15th +of the month, which is probably what most people want. + +``ScalarFormatter`` *useLocale* option obeys grouping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When the `~.ScalarFormatter` option *useLocale* is enabled (or +:rc:`axes.formatter.use_locale` is *True*) and the configured locale uses +grouping, a separator will be added as described in `locale.format_string`. + +``Axes.errorbar`` cycles non-color properties correctly +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `.Axes.errorbar` incorrectly skipped the Axes property cycle if a +color was explicitly specified, even if the property cycler was for other +properties (such as line style). Now, `.Axes.errorbar` will advance the Axes +property cycle as done for `.Axes.plot`, i.e., as long as all properties in the +cycler are not explicitly passed. + +pyplot.specgram always uses origin='upper' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously if :rc:`image.origin` was set to something other than ``'upper'`` or +if the *origin* keyword argument was passed with a value other than +``'upper'``, the spectrogram itself would flip, but the Axes would remain +oriented for an origin value of ``'upper'``, so that the resulting plot was +incorrectly labelled. + +Now, the *origin* keyword argument is not supported and the ``image.origin`` +rcParam is ignored. The function `matplotlib.pyplot.specgram` is forced to use +``origin='upper'``, so that the Axes are correct for the plotted spectrogram. + +xunits=None and yunits=None passed as keyword arguments are treated as "no action" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Many (but not all) of the methods on `~.axes.Axes` take the (undocumented) +keyword arguments *xunits* and *yunits* that will update the units on the given +Axis by calling `.Axis.set_units` and `.Axis.update_units`. + +Previously if *None* was passed it would clear the value stored in +``.Axis.units`` which will in turn break converters which rely on the value in +``.Axis.units`` to work properly (notably `.StrCategoryConverter`). + +This changes the semantics of ``ax.meth(..., xunits=None, yunits=None)`` from +"please clear the units" to "do the default thing as if they had not been +passed" which is consistent with the standard behavior of Matplotlib keyword +arguments. + +If you were relying on passing ``xunits=None`` to plotting methods to clear the +``.Axes.units`` attribute, directly call `.Axis.set_units` (and +`.Axis.update_units` if you also require the converter to be updated). + +Annotations with ``annotation_clip`` no longer affect ``tight_layout`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, `.text.Annotation.get_tightbbox` always returned the full +`.text.Annotation.get_window_extent` of the object, independent of the value of +``annotation_clip``. `.text.Annotation.get_tightbbox` now correctly takes this +extra clipping box into account, meaning that `~.text.Annotation`\s that are +not drawn because of ``annotation_clip`` will not count towards the Axes +bounding box calculations, such as those done by `~.pyplot.tight_layout`. + +This is now consistent with the API described in `~.artist.Artist`, which +specifies that ``get_window_extent`` should return the full extents and +``get_tightbbox`` should "account for any clipping". + +Parasite Axes pcolor and pcolormesh now defaults to placing grid edges at integers, not half-integers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is consistent with `~.Axes.pcolor` and `~.Axes.pcolormesh`. + +``Colorbar`` outline is now a ``Spine`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The outline of `~matplotlib.colorbar.Colorbar` is now a `.Spine` and drawn as +one, instead of a `.Polygon` drawn as an artist. This ensures it will always be +drawn after (i.e., on top of) all artists, consistent with Spines on normal +Axes. + +``Colorbar.dividers`` changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This attribute is now always a `.LineCollection` -- an empty one if +``drawedges`` is *False*. Its default colors and linewidth +(:rc:`axes.edgecolor`, :rc:`axes.linewidth`) are now resolved at instantiation +time, not at draw time. + +Raise or warn on registering a colormap twice +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using ``matplotlib.cm.register_cmap`` to register a user provided or +third-party colormap it will now raise a `ValueError` if trying to over-write +one of the built in colormaps and warn if trying to over write a user +registered colormap. This may raise for user-registered colormaps in the +future. + +Consecutive rasterized draws now merged +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tracking of depth of raster draws has moved from +`.backend_mixed.MixedModeRenderer.start_rasterizing` and +`.backend_mixed.MixedModeRenderer.stop_rasterizing` into +`.artist.allow_rasterization`. This means the start and stop functions are only +called when the rasterization actually needs to be started and stopped. + +The output of vector backends will change in the case that rasterized elements +are merged. This should not change the appearance of outputs. + +The renders in 3rd party backends are now expected to have +``self._raster_depth`` and ``self._rasterizing`` initialized to ``0`` and +*False* respectively. + +Consistent behavior of ``draw_if_interactive()`` across backends +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.pyplot.draw_if_interactive` no longer shows the window (if it was previously +unshown) on the Tk and nbAgg backends, consistently with all other backends. + +The Artist property *rasterized* cannot be *None* anymore +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now a boolean only. Before the default was *None* and +`.Artist.set_rasterized` was documented to accept *None*. However, *None* did +not have a special meaning and was treated as *False*. + +Canvas's callback registry now stored on Figure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The canonical location of the `~.cbook.CallbackRegistry` used to handle +Figure/Canvas events has been moved from the Canvas to the Figure. This change +should be transparent to almost all users, however if you are swapping +switching the Figure out from on top of a Canvas or visa versa you may see a +change in behavior. + +Harmonized key event data across backends +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The different backends with key translation support, now handle "Shift" as a +sometimes modifier, where the ``'shift+'`` prefix won't be added if a key +translation was made. + +In the Qt5 backend, the ``matplotlib.backends.backend_qt5.SPECIAL_KEYS`` +dictionary contains keys that do *not* return their unicode name instead they +have manually specified names. The name for ``QtCore.Qt.Key_Meta`` has changed +to ``'meta'`` to be consistent with the other GUI backends. + +The WebAgg backend now handles key translations correctly on non-US keyboard +layouts. + +In the GTK and Tk backends, the handling of non-ASCII keypresses (as reported +in the KeyEvent passed to ``key_press_event``-handlers) now correctly reports +Unicode characters (e.g., €), and better respects NumLock on the numpad. + +In the GTK and Tk backends, the following key names have changed; the new names +are consistent with those reported by the Qt backends: + +- The "Break/Pause" key (keysym 0xff13) is now reported as ``"pause"`` instead + of ``"break"`` (this is also consistent with the X key name). +- The numpad "delete" key is now reported as ``"delete"`` instead of ``"dec"``. + +WebAgg backend no longer reports a middle click as a right click +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously when using the WebAgg backend the event passed to a callback by +``fig.canvas.mpl_connect('mouse_button_event', callback)`` on a middle click +would report `.MouseButton.RIGHT` instead of `.MouseButton.MIDDLE`. + +ID attribute of XML tags in SVG files now based on SHA256 rather than MD5 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Matplotlib generates unique ID attributes for various tags in SVG files. +Matplotlib previously generated these unique IDs using the first 10 characters +of an MD5 hash. The MD5 hashing algorithm is not available in Python on systems +with Federal Information Processing Standards (FIPS) enabled. Matplotlib now +uses the first 10 characters of an SHA256 hash instead. SVG files that would +otherwise match those saved with earlier versions of matplotlib, will have +different ID attributes. + +``RendererPS.set_font`` is no longer a no-op in AFM mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.RendererPS.set_font` now sets the current PostScript font in all cases. + +Autoscaling in Axes3D +~~~~~~~~~~~~~~~~~~~~~ + +In Matplotlib 3.2.0, autoscaling was made lazier for 2D Axes, i.e., limits +would only be recomputed when actually rendering the canvas, or when the user +queries the Axes limits. This performance improvement is now extended to +`.Axes3D`. This also fixes some issues with autoscaling being triggered +unexpectedly in Axes3D. + +Please see :ref:`the API change for 2D Axes ` +for further details. + +Axes3D automatically adding itself to Figure is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New `.Axes3D` objects previously added themselves to figures when they were +created, unlike all other Axes classes, which lead to them being added twice if +``fig.add_subplot(111, projection='3d')`` was called. + +This behavior is now deprecated and will warn. The new keyword argument +*auto_add_to_figure* controls the behavior and can be used to suppress the +warning. The default value will change to *False* in Matplotlib 3.5, and any +non-*False* value will be an error in Matplotlib 3.6. + +In the future, `.Axes3D` will need to be explicitly added to the figure :: + + fig = Figure() + # create Axes3D + ax = Axes3d(fig) + # add to Figure + fig.add_axes(ax) + +as needs to be done for other `.axes.Axes` sub-classes. Or, a 3D projection can +be made via:: + + fig.add_subplot(projection='3d') + +``mplot3d.art3d.get_dir_vector`` always returns NumPy arrays +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For consistency, `~.mplot3d.art3d.get_dir_vector` now always returns NumPy +arrays, even if the input is a 3-element iterable. + +Changed cursive and fantasy font definitions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Comic Sans and Comic Neue fonts were moved from the default +:rc:`font.fantasy` list to the default :rc:`font.cursive` setting, in +accordance with the CSS font families example_ and in order to provide a +cursive font present in Microsoft's Core Fonts set. + +.. _example: https://www.w3.org/Style/Examples/007/fonts.en.html + +docstring.Substitution now always dedents docstrings before string interpolation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst new file mode 100644 index 000000000000..9e09f3febe64 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.0/deprecations.rst @@ -0,0 +1,348 @@ +Deprecations +------------ + +Extra parameters to Axes constructor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Parameters of the Axes constructor other than *fig* and *rect* will become +keyword-only in a future version. + +``pyplot.gca`` and ``Figure.gca`` keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing keyword arguments to `.pyplot.gca` or `.figure.Figure.gca` will not be +supported in a future release. + +``Axis.cla``, ``RadialAxis.cla``, ``ThetaAxis.cla`` and ``Spine.cla`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These methods are deprecated in favor of the respective ``clear()`` methods. + +Invalid hatch pattern characters are no longer ignored +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When specifying hatching patterns, characters that are not recognized will +raise a deprecation warning. In the future, this will become a hard error. + +``imread`` reading from URLs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing a URL to `~.pyplot.imread()` is deprecated. Please open the URL for +reading and directly use the Pillow API +(``PIL.Image.open(urllib.request.urlopen(url))``, or +``PIL.Image.open(io.BytesIO(requests.get(url).content))``) instead. + +Subplot-related attributes and methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some ``SubplotBase`` methods and attributes have been deprecated and/or moved +to `.SubplotSpec`: + +- ``get_geometry`` (use ``SubplotBase.get_subplotspec`` instead), +- ``change_geometry`` (use ``SubplotBase.set_subplotspec`` instead), +- ``is_first_row``, ``is_last_row``, ``is_first_col``, ``is_last_col`` (use the + corresponding methods on the `.SubplotSpec` instance instead), +- ``update_params`` (now a no-op), +- ``figbox`` (use ``ax.get_subplotspec().get_geometry(ax.figure)`` instead to + recompute the geometry, or ``ax.get_position()`` to read its current value), +- ``numRows``, ``numCols`` (use the ``nrows`` and ``ncols`` attribute on the + `.GridSpec` instead). + +Likewise, the ``get_geometry``, ``change_geometry``, ``update_params``, and +``figbox`` methods/attributes of `.SubplotDivider` have been deprecated, with +similar replacements. + +``is_url`` and ``URL_REGEX`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated. (They were previously defined in the toplevel +:mod:`matplotlib` module.) + +``matplotlib.style.core`` deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``STYLE_FILE_PATTERN``, ``load_base_library``, and ``iter_user_libraries`` are +deprecated. + +``dpi_cor`` property of `.FancyArrowPatch` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This parameter is considered internal and deprecated. + +Passing ``boxstyle="custom", bbox_transmuter=...`` to ``FancyBboxPatch`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to use a custom boxstyle, directly pass it as the *boxstyle* argument +to `.FancyBboxPatch`. This was previously already possible, and is consistent +with custom arrow styles and connection styles. + +BoxStyles are now called without passing the *mutation_aspect* parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mutation aspect is now handled by the artist itself. Hence the +*mutation_aspect* parameter of ``BoxStyle._Base.__call__`` is deprecated, and +custom boxstyles should be implemented to not require this parameter (it can be +left as a parameter defaulting to 1 for back-compatibility). + +``ContourLabeler.get_label_coords`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is considered an internal helper. + +Line2D and Patch no longer duplicate ``validJoin`` and ``validCap`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Validation of joinstyle and capstyles is now centralized in ``rcsetup``. + +Setting a Line2D's pickradius via ``set_picker`` is undeprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This cancels the deprecation introduced in Matplotlib 3.3.0. + +``MarkerStyle`` is considered immutable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``MarkerStyle.set_fillstyle()`` and ``MarkerStyle.set_marker()`` are +deprecated. Create a new ``MarkerStyle`` with the respective parameters +instead. + +``MovieWriter.cleanup`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cleanup logic is now fully implemented in `.MovieWriter.finish`. Third-party +movie writers should likewise move the relevant cleanup logic there, as +overridden ``cleanup``\s will no longer be called in the future. + +*minimumdescent* parameter/property of ``TextArea`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.offsetbox.TextArea` has behaved as if *minimumdescent* was always True +(regardless of the value to which it was set) since Matplotlib 1.3, so the +parameter/property is deprecated. + +``colorbar`` now warns when the mappable's Axes is different from the current Axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, `.Figure.colorbar` and `.pyplot.colorbar` steal space by default +from the current Axes to place the colorbar. In a future version, they will +steal space from the mappable's Axes instead. In preparation for this change, +`.Figure.colorbar` and `.pyplot.colorbar` now emits a warning when the current +Axes is not the same as the mappable's Axes. + +Colorbar docstrings +~~~~~~~~~~~~~~~~~~~ + +The following globals in :mod:`matplotlib.colorbar` are deprecated: +``colorbar_doc``, ``colormap_kw_doc``, ``make_axes_kw_doc``. + +``ColorbarPatch`` and ``colorbar_factory`` are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +All the relevant functionality has been moved to the +`~matplotlib.colorbar.Colorbar` class. + +Backend deprecations +~~~~~~~~~~~~~~~~~~~~ + +- ``FigureCanvasBase.get_window_title`` and + ``FigureCanvasBase.set_window_title`` are deprecated. Use the corresponding + methods on the FigureManager if using pyplot, or GUI-specific methods if + embedding. +- The *resize_callback* parameter to ``FigureCanvasTk`` was never used + internally and is deprecated. Tk-level custom event handlers for resize + events can be added to a ``FigureCanvasTk`` using e.g. + ``get_tk_widget().bind('', ..., True)``. +- The ``key_press`` and ``button_press`` methods of `.FigureManagerBase`, which + incorrectly did nothing when using ``toolmanager``, are deprecated in favor + of directly passing the event to the `.CallbackRegistry` via + ``self.canvas.callbacks.process(event.name, event)``. +- ``RendererAgg.get_content_extents`` and + ``RendererAgg.tostring_rgba_minimized`` are deprecated. +- ``backend_pgf.TmpDirCleaner`` is deprecated, with no replacement. +- ``GraphicsContextPS`` is deprecated. The PostScript backend now uses + `.GraphicsContextBase`. + +wx backend cleanups +~~~~~~~~~~~~~~~~~~~ + +The *origin* parameter to ``_FigureCanvasWxBase.gui_repaint`` is deprecated +with no replacement; ``gui_repaint`` now automatically detects the case where +it is used with the wx renderer. + +The ``NavigationToolbar2Wx.get_canvas`` method is deprecated; directly +instantiate a canvas (``FigureCanvasWxAgg(frame, -1, figure)``) if needed. + +Unused positional parameters to ``print_`` methods are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +None of the ``print_`` methods implemented by canvas subclasses used +positional arguments other that the first (the output filename or file-like), +so these extra parameters are deprecated. + +The *dpi* parameter of ``FigureCanvas.print_foo`` printers is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `~.Figure.savefig` machinery already took care of setting the figure DPI +to the desired value, so ``print_foo`` can directly read it from there. Not +passing *dpi* to ``print_foo`` allows clearer detection of unused parameters +passed to `~.Figure.savefig`. + +Passing `bytes` to ``FT2Font.set_text`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated, pass `str` instead. + +``ps.useafm`` deprecated for mathtext +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Outputting mathtext using only standard PostScript fonts has likely been broken +for a while (issue `#18722 +`_). In Matplotlib 3.5, +the setting :rc:`ps.useafm` will have no effect on mathtext. + +``MathTextParser("bitmap")`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The associated APIs ``MathtextBackendBitmap``, ``MathTextParser.to_mask``, +``MathTextParser.to_rgba``, ``MathTextParser.to_png``, and +``MathTextParser.get_depth`` are likewise deprecated. + +To convert a text string to an image, either directly draw the text to an +empty `.Figure` and save the figure using a tight bbox, as demonstrated in +:doc:`/gallery/text_labels_and_annotations/mathtext_asarray`, or use +`.mathtext.math_to_image`. + +When using `.math_to_image`, text color can be set with e.g.:: + + with plt.rc_context({"text.color": "tab:blue"}): + mathtext.math_to_image(text, filename) + +and an RGBA array can be obtained with e.g.:: + + from io import BytesIO + buf = BytesIO() + mathtext.math_to_image(text, buf, format="png") + buf.seek(0) + rgba = plt.imread(buf) + +Deprecation of mathtext internals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following API elements previously exposed by the :mod:`.mathtext` module +are considered to be implementation details and public access to them is +deprecated: + +- ``Fonts`` and all its subclasses, +- ``FontConstantsBase`` and all its subclasses, +- ``Node`` and all its subclasses, +- ``Ship``, ``ship``, +- ``Error``, +- ``Parser``, +- ``SHRINK_FACTOR``, ``GROW_FACTOR``, +- ``NUM_SIZE_LEVELS``, +- ``latex_to_bakoma``, ``latex_to_cmex``, ``latex_to_standard``, +- ``stix_virtual_fonts``, +- ``tex2uni``. + +Deprecation of various mathtext helpers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``MathtextBackendPdf``, ``MathtextBackendPs``, ``MathtextBackendSvg``, +and ``MathtextBackendCairo`` classes from the :mod:`.mathtext` module, as +well as the corresponding ``.mathtext_parser`` attributes on ``RendererPdf``, +``RendererPS``, ``RendererSVG``, and ``RendererCairo``, are deprecated. The +``MathtextBackendPath`` class can be used to obtain a list of glyphs and +rectangles in a mathtext expression, and renderer-specific logic should be +directly implemented in the renderer. + +``StandardPsFonts.pswriter`` is unused and deprecated. + +Widget class internals +~~~~~~~~~~~~~~~~~~~~~~ + +Several `.widgets.Widget` class internals have been privatized and deprecated: + +- ``AxesWidget.cids`` +- ``Button.cnt`` and ``Button.observers`` +- ``CheckButtons.cnt`` and ``CheckButtons.observers`` +- ``RadioButtons.cnt`` and ``RadioButtons.observers`` +- ``Slider.cnt`` and ``Slider.observers`` +- ``TextBox.cnt``, ``TextBox.change_observers`` and + ``TextBox.submit_observers`` + +3D properties on renderers +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The properties of the 3D Axes that were placed on the Renderer during draw are +now deprecated: + +- ``renderer.M`` +- ``renderer.eye`` +- ``renderer.vvec`` +- ``renderer.get_axis_position`` + +These attributes are all available via `.Axes3D`, which can be accessed via +``self.axes`` on all `.Artist`\s. + +*renderer* argument of ``do_3d_projection`` method for ``Collection3D``/``Patch3D`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *renderer* argument for the ``do_3d_projection`` method on ``Collection3D`` +and ``Patch3D`` is no longer necessary, and passing it during draw is +deprecated. + +*project* argument of ``draw`` method for ``Line3DCollection`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *project* argument for the ``draw`` method on ``Line3DCollection`` is +deprecated. Call `.Line3DCollection.do_3d_projection` explicitly instead. + +Extra positional parameters to ``plot_surface`` and ``plot_wireframe`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional parameters to `~.axes3d.Axes3D.plot_surface` and +`~.axes3d.Axes3D.plot_wireframe` other than ``X``, ``Y``, and ``Z`` are +deprecated. Pass additional artist properties as keyword arguments instead. + +``ParasiteAxesAuxTransBase`` class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The functionality of that mixin class has been moved to the base +``ParasiteAxesBase`` class. Thus, ``ParasiteAxesAuxTransBase``, +``ParasiteAxesAuxTrans``, and ``parasite_axes_auxtrans_class_factory`` are +deprecated. + +In general, it is suggested to use ``HostAxes.get_aux_axes`` to create +parasite Axes, as this saves the need of manually appending the parasite +to ``host.parasites`` and makes sure that their ``remove()`` method works +properly. + +``AxisArtist.ZORDER`` attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use ``AxisArtist.zorder`` instead. + +``GridHelperBase`` invalidation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``GridHelperBase.invalidate``, ``GridHelperBase.valid``, and +``axislines.Axes.invalidate_grid_helper`` methods are considered internal +and deprecated. + +``sphinext.plot_directive.align`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. Use ``docutils.parsers.rst.directives.images.Image.align`` +instead. + +Deprecation-related functionality is considered internal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The module ``matplotlib.cbook.deprecation`` is considered internal and will be +removed from the public API. This also holds for deprecation-related re-imports +in ``matplotlib.cbook``, i.e. ``matplotlib.cbook.deprecated()``, +``matplotlib.cbook.warn_deprecated()``, +``matplotlib.cbook.MatplotlibDeprecationWarning`` and +``matplotlib.cbook.mplDeprecation``. + +If needed, external users may import ``MatplotlibDeprecationWarning`` directly +from the ``matplotlib`` namespace. ``mplDeprecation`` is only an alias of +``MatplotlibDeprecationWarning`` and should not be used anymore. diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/development.rst b/doc/api/prev_api_changes/api_changes_3.4.0/development.rst new file mode 100644 index 000000000000..982046c3869e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.0/development.rst @@ -0,0 +1,49 @@ +Development changes +------------------- + +Increase to minimum supported versions of Python and dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.4, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.3 | min in mpl3.4 | ++============+=================+===============+ +| Python | 3.6 | 3.7 | ++------------+-----------------+---------------+ +| dateutil | 2.1 | 2.7 | ++------------+-----------------+---------------+ +| numpy | 1.15 | 1.16 | ++------------+-----------------+---------------+ +| pyparsing | 2.0.3 | 2.2.1 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + +Qhull downloaded at build-or-sdist time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Much like FreeType, Qhull is now downloaded at build time, or upon creation of +the sdist. To link against system Qhull, set the ``system_qhull`` option to +`True` in the :file:`setup.cfg` file. Note that Matplotlib now requires the +re-entrant version of Qhull (``qhull_r``). + +``FigureBase`` class added, and ``Figure`` class made a child +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The new subfigure feature motivated some re-organization of the +`.figure.Figure` class, so that the new `.figure.SubFigure` class could have +all the capabilities of a figure. + +The `.figure.Figure` class is now a subclass of `.figure.FigureBase`, where +`.figure.FigureBase` contains figure-level artist addition routines, and the +`.figure.Figure` subclass just contains features that are unique to the outer +figure. + +Note that there is a new *transSubfigure* transform associated with the +subfigure. This transform also exists for a `.Figure` instance, and is equal +to *transFigure* in that case, so code that uses the transform stack that wants +to place objects on either the parent figure or one of the subfigures should +use *transSubfigure*. diff --git a/doc/api/prev_api_changes/api_changes_3.4.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.4.0/removals.rst new file mode 100644 index 000000000000..1f558800bd8f --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.0/removals.rst @@ -0,0 +1,177 @@ +Removals +-------- +The following deprecated APIs have been removed: + +Removed behaviour +~~~~~~~~~~~~~~~~~ + +- The "smart bounds" functionality on `~.axis.Axis` and `.Spine` has been + deleted, and the related methods have been removed. +- Converting a string with single color characters (e.g. ``'cymk'``) in + `~.colors.to_rgba_array` is no longer supported. Instead, the colors can be + passed individually in a list (e.g. ``['c', 'y', 'm', 'k']``). +- Returning a factor equal to ``None`` from ``mpl_toolkits.axisartist`` + Locators (which are **not** the same as "standard" tick Locators), or passing + a factor equal to ``None`` to axisartist Formatters (which are **not** the + same as "standard" tick Formatters) is no longer supported. Pass a factor + equal to 1 instead. + +Modules +~~~~~~~ + +- The entire ``matplotlib.testing.disable_internet`` module has been removed. + The `pytest-remotedata package + `_ can be used instead. +- The ``mpl_toolkits.axes_grid1.colorbar`` module and its colorbar + implementation have been removed in favor of `matplotlib.colorbar`. + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- The `.animation.MovieWriterRegistry` methods ``.set_dirty()``, + ``.ensure_not_dirty()``, and ``.reset_available_writers()`` do nothing and + have been removed. The ``.avail()`` method has been removed; use ``.list()`` + instead to get a list of available writers. +- The ``matplotlib.artist.Artist.eventson`` and + ``matplotlib.container.Container.eventson`` attributes have no effect and + have been removed. +- ``matplotlib.axes.Axes.get_data_ratio_log`` has been removed. +- ``matplotlib.axes.SubplotBase.rowNum``; use + ``ax.get_subplotspec().rowspan.start`` instead. +- ``matplotlib.axes.SubplotBase.colNum``; use + ``ax.get_subplotspec().colspan.start`` instead. +- ``matplotlib.axis.Axis.set_smart_bounds`` and + ``matplotlib.axis.Axis.get_smart_bounds`` have been removed. +- ``matplotlib.colors.DivergingNorm`` has been renamed to + `~matplotlib.colors.TwoSlopeNorm`. +- ``matplotlib.figure.AxesStack`` has been removed. +- ``matplotlib.font_manager.JSONEncoder`` has been removed; use + `.font_manager.json_dump` to dump a `.FontManager` instance. +- The ``matplotlib.ft2font.FT2Image`` methods ``.as_array()``, + ``.as_rgba_str()``, ``.as_str()``, ``.get_height()`` and ``.get_width()`` + have been removed. Convert the ``FT2Image`` to a NumPy array with + ``np.asarray`` before processing it. +- ``matplotlib.quiver.QuiverKey.quiverkey_doc`` has been removed; use + ``matplotlib.quiver.QuiverKey.__init__.__doc__`` instead. +- ``matplotlib.spines.Spine.set_smart_bounds`` and + ``matplotlib.spines.Spine.get_smart_bounds`` have been removed. +- ``matplotlib.testing.jpl_units.UnitDbl.checkUnits`` has been removed; use + ``units not in self.allowed`` instead. +- The unused ``matplotlib.ticker.Locator.autoscale`` method has been removed + (pass the axis limits to `.Locator.view_limits` instead). The derived methods + ``Locator.autoscale``, ``AutoDateLocator.autoscale``, + ``RRuleLocator.autoscale``, ``RadialLocator.autoscale``, + ``ThetaLocator.autoscale``, and ``YearLocator.autoscale`` have also been + removed. +- ``matplotlib.transforms.BboxBase.is_unit`` has been removed; check the + `.Bbox` extents if needed. +- ``matplotlib.transforms.Affine2DBase.matrix_from_values(...)`` has been + removed; use (for example) ``Affine2D.from_values(...).get_matrix()`` + instead. + +* ``matplotlib.backend_bases.FigureCanvasBase.draw_cursor`` has been removed. +* ``matplotlib.backends.backend_gtk.ConfigureSubplotsGTK3.destroy`` and + ``matplotlib.backends.backend_gtk.ConfigureSubplotsGTK3.init_window`` methods + have been removed. +* ``matplotlib.backends.backend_gtk.ConfigureSubplotsGTK3.window`` property has + been removed. +* ``matplotlib.backends.backend_macosx.FigureCanvasMac.invalidate`` has been + removed. +* ``matplotlib.backends.backend_pgf.RendererPgf.latexManager`` has been removed. +* ``matplotlib.backends.backend_wx.FigureFrameWx.statusbar``, + ``matplotlib.backends.backend_wx.NavigationToolbar2Wx.set_status_bar``, and + ``matplotlib.backends.backend_wx.NavigationToolbar2Wx.statbar`` have been + removed. The status bar can be retrieved by calling standard wx methods + (``frame.GetStatusBar()`` and + ``toolbar.GetTopLevelParent().GetStatusBar()``). +* ``matplotlib.backends.backend_wx.ConfigureSubplotsWx.configure_subplots`` and + ``matplotlib.backends.backend_wx.ConfigureSubplotsWx.get_canvas`` have been + removed. + + +- ``mpl_toolkits.axisartist.grid_finder.GridFinderBase`` has been removed; use + `.GridFinder` instead. +- ``mpl_toolkits.axisartist.axis_artist.BezierPath`` has been removed; use + `.patches.PathPatch` instead. + +Functions +~~~~~~~~~ + +- ``matplotlib.backends.backend_pgf.repl_escapetext`` and + ``matplotlib.backends.backend_pgf.repl_mathdefault`` have been removed. +- ``matplotlib.checkdep_ps_distiller`` has been removed. +- ``matplotlib.cm.revcmap`` has been removed; use `.Colormap.reversed` + instead. +- ``matplotlib.colors.makeMappingArray`` has been removed. +- ``matplotlib.compare_versions`` has been removed; use comparison of + ``distutils.version.LooseVersion``\s instead. +- ``matplotlib.dates.mx2num`` has been removed. +- ``matplotlib.font_manager.createFontList`` has been removed; + `.font_manager.FontManager.addfont` is now available to register a font at a + given path. +- ``matplotlib.get_home`` has been removed; use standard library instead. +- ``matplotlib.mlab.apply_window`` and ``matplotlib.mlab.stride_repeat`` have + been removed. +- ``matplotlib.rcsetup.update_savefig_format`` has been removed; this just + replaced ``'auto'`` with ``'png'``, so do the same. +- ``matplotlib.rcsetup.validate_animation_writer_path`` has been removed. +- ``matplotlib.rcsetup.validate_path_exists`` has been removed; use + `os.path.exists` or `pathlib.Path.exists` instead. +- ``matplotlib.style.core.is_style_file`` and + ``matplotlib.style.core.iter_style_files`` have been removed. +- ``matplotlib.testing.is_called_from_pytest`` has been removed. +- ``mpl_toolkits.mplot3d.axes3d.unit_bbox`` has been removed; use `.Bbox.unit` + instead. + + +Arguments +~~~~~~~~~ + +- Passing more than one positional argument to `.axes.Axes.axis` will now + raise an error. +- Passing ``"range"`` to the *whis* parameter of `.Axes.boxplot` and + `.cbook.boxplot_stats` to mean "the whole data range" is no longer + supported. +- Passing scalars to the *where* parameter in `.axes.Axes.fill_between` and + `.axes.Axes.fill_betweenx` is no longer accepted and non-matching sizes now + raise a `ValueError`. +- The *verts* parameter to `.Axes.scatter` has been removed; use *marker* instead. +- The *minor* parameter in `.Axis.set_ticks` and ``SecondaryAxis.set_ticks`` is + now keyword-only. +- `.scale.ScaleBase`, `.scale.LinearScale` and `.scale.SymmetricalLogScale` now + error if any unexpected keyword arguments are passed to their constructors. +- The *renderer* parameter to `.Figure.tight_layout` has been removed; this + method now always uses the renderer instance cached on the `.Figure`. +- The *locator* parameter to + `mpl_toolkits.axes_grid1.axes_grid.CbarAxesBase.colorbar` has been removed in + favor of its synonym *ticks* (which already existed previously, + and is consistent with :mod:`matplotlib.colorbar`). +- The *switch_backend_warn* parameter to ``matplotlib.test`` has no effect and + has been removed. +- The *dryrun* parameter to the various ``FigureCanvas*.print_*`` methods has + been removed. + +rcParams +~~~~~~~~ + +- The ``datapath`` rcParam has been removed. Use `matplotlib.get_data_path` + instead. +- The ``mpl_toolkits.legacy_colorbar`` rcParam has no effect and has been + removed. +- Setting :rc:`boxplot.whiskers` to ``"range"`` is no longer valid; set it to + ``0, 100`` instead. +- Setting :rc:`savefig.format` to ``"auto"`` is no longer valid; use ``"png"`` + instead. +- Setting :rc:`text.hinting` to `False` or `True` is no longer valid; set it to + ``"auto"`` or ``"none"`` respectively. + +sample_data removals +~~~~~~~~~~~~~~~~~~~~ +The sample datasets listed below have been removed. Suggested replacements for +demonstration purposes are listed in parentheses. + +- ``None_vs_nearest-pdf.png``, +- ``aapl.npz`` (use ``goog.npz``), +- ``ada.png``, ``grace_hopper.png`` (use ``grace_hopper.jpg``), +- ``ct.raw.gz`` (use ``s1045.ima.gz``), +- ``damodata.csv`` (use ``msft.csv``). diff --git a/doc/api/prev_api_changes/api_changes_3.4.2.rst b/doc/api/prev_api_changes/api_changes_3.4.2.rst new file mode 100644 index 000000000000..34d760bf0941 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.4.2.rst @@ -0,0 +1,16 @@ +API Changes for 3.4.2 +===================== + +Behaviour changes +----------------- + +Rename first argument to ``subplot_mosaic`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both `.Figure.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the +first position argument renamed from *layout* to *mosaic*. This is because we +are considering to consolidate *constrained_layout* and *tight_layout* keyword +arguments in the Figure creation functions of `.pyplot` into a single *layout* +keyword argument which would collide. + +As this API is provisional, we are changing this with no deprecation period. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0.rst b/doc/api/prev_api_changes/api_changes_3.5.0.rst new file mode 100644 index 000000000000..890484bcd19a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.5.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.5.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.5.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst new file mode 100644 index 000000000000..25f761ae39fa --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/behaviour.rst @@ -0,0 +1,247 @@ +Behaviour changes +----------------- + +First argument to ``subplot_mosaic`` renamed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both `.Figure.subplot_mosaic`, and `.pyplot.subplot_mosaic` have had the +first positional argument renamed from *layout* to *mosaic*. As we have +consolidated the *constrained_layout* and *tight_layout* keyword arguments in +the Figure creation functions of `.pyplot` into a single *layout* keyword +argument, the original ``subplot_mosaic`` argument name would collide. + +As this API is provisional, we are changing this argument name with no +deprecation period. + +.. _Behavioural API Changes 3.5 - Axes children combined: + +``Axes`` children are no longer separated by type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `.axes.Axes` children were separated by `.Artist` type, into sublists +such as ``Axes.lines``. For methods that produced multiple elements (such as +`.Axes.errorbar`), though individual parts would have similar *zorder*, this +separation might cause them to be drawn at different times, causing +inconsistent results when overlapping other Artists. + +Now, the children are no longer separated by type, and the sublist properties +are generated dynamically when accessed. Consequently, Artists will now always +appear in the correct sublist; e.g., if `.axes.Axes.add_line` is called on a +`.Patch`, it will appear in the ``Axes.patches`` sublist, *not* ``Axes.lines``. +The ``Axes.add_*`` methods will now warn if passed an unexpected type. + +Modification of the following sublists is still accepted, but deprecated: + +* ``Axes.artists`` +* ``Axes.collections`` +* ``Axes.images`` +* ``Axes.lines`` +* ``Axes.patches`` +* ``Axes.tables`` +* ``Axes.texts`` + +To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the +corresponding ``Axes.add_*`` method. + +``MatplotlibDeprecationWarning`` now subclasses ``DeprecationWarning`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Historically, it has not been possible to filter +`~matplotlib.MatplotlibDeprecationWarning`\s by checking for +`DeprecationWarning`, since we subclass `UserWarning` directly. + +The decision to not subclass `DeprecationWarning` has to do with a decision +from core Python in the 2.x days to not show `DeprecationWarning`\s to users. +However, there is now a more sophisticated filter in place (see +https://www.python.org/dev/peps/pep-0565/). + +Users will now see `~matplotlib.MatplotlibDeprecationWarning` only during +interactive sessions, and these can be silenced by the standard mechanism: + +.. code:: python + + warnings.filterwarnings("ignore", category=DeprecationWarning) + +Library authors must now enable `DeprecationWarning`\s explicitly in order for +(non-interactive) CI/CD pipelines to report back these warnings, as is standard +for the rest of the Python ecosystem: + +.. code:: python + + warnings.filterwarnings("always", DeprecationWarning) + +``Artist.set`` applies artist properties in the order in which they are given +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The change only affects the interaction between the *color*, *edgecolor*, +*facecolor*, and (for `.Collection`\s) *alpha* properties: the *color* property +now needs to be passed first in order not to override the other properties. +This is consistent with e.g. `.Artist.update`, which did not reorder the +properties passed to it. + +``pcolor(mesh)`` shading defaults to auto +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *shading* keyword argument for `.Axes.pcolormesh` and `.Axes.pcolor` +default has been changed to 'auto'. + +Passing ``Z(M, N)``, ``x(N)``, ``y(M)`` to ``pcolormesh`` with +``shading='flat'`` will now raise a `TypeError`. Use ``shading='auto'`` or +``shading='nearest'`` for ``x`` and ``y`` to be treated as cell centers, or +drop the last column and row of ``Z`` to get the old behaviour with +``shading='flat'``. + +Colorbars now have pan and zoom functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Interactive plots with colorbars can now be zoomed and panned on the colorbar +axis. This adjusts the *vmin* and *vmax* of the `.ScalarMappable` associated +with the colorbar. This is currently only enabled for continuous norms. Norms +used with ``contourf`` and categoricals, such as `.BoundaryNorm` and `.NoNorm`, +have the interactive capability disabled by default. `cb.ax.set_navigate() +<.Axes.set_navigate>` can be used to set whether a colorbar axes is interactive +or not. + +Colorbar lines no longer clipped +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a colorbar has lines added to it (e.g. for contour lines), these will no +longer be clipped. This is an improvement for lines on the edge of the +colorbar, but could lead to lines off the colorbar if the limits of the +colorbar are changed. + +``Figure.suppressComposite`` now also controls compositing of Axes images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The output of ``NonUniformImage`` and ``PcolorImage`` has changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pixel-level differences may be observed in images generated using +`.NonUniformImage` or `.PcolorImage`, typically for pixels exactly at the +boundary between two data cells (no user-facing axes method currently generates +`.NonUniformImage`\s, and only `.pcolorfast` can generate `.PcolorImage`\s). +These artists are also now slower, normally by ~1.5x but sometimes more (in +particular for ``NonUniformImage(interpolation="bilinear")``. This slowdown +arises from fixing occasional floating point inaccuracies. + +Change of the (default) legend handler for ``Line2D`` instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default legend handler for Line2D instances (`.HandlerLine2D`) now +consistently exposes all the attributes and methods related to the line marker +(:ghissue:`11358`). This makes it easy to change the marker features after +instantiating a legend. + +.. code-block:: python + + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + + ax.plot([1, 3, 2], marker="s", label="Line", color="pink", mec="red", ms=8) + leg = ax.legend() + + leg.legendHandles[0].set_color("lightgray") + leg.legendHandles[0].set_mec("black") # marker edge color + +The former legend handler for Line2D objects has been renamed +`.HandlerLine2DCompound`. To revert to the previous behaviour, one can use + +.. code-block:: python + + import matplotlib.legend as mlegend + from matplotlib.legend_handler import HandlerLine2DCompound + from matplotlib.lines import Line2D + + mlegend.Legend.update_default_handler_map({Line2D: HandlerLine2DCompound()}) + +Setting ``Line2D`` marker edge/face color to *None* use rcParams +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Line2D.set_markeredgecolor(None)`` and ``Line2D.set_markerfacecolor(None)`` +now set the line property using the corresponding rcParam +(:rc:`lines.markeredgecolor` and :rc:`lines.markerfacecolor`). This is +consistent with other `.Line2D` property setters. + +Default theta tick locations for wedge polar plots have changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For polar plots that don't cover a full circle, the default theta tick +locations are now at multiples of 10°, 15°, 30°, 45°, 90°, rather than using +values that mostly make sense for linear plots (20°, 25°, etc.). + +``axvspan`` now plots full wedges in polar plots +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... rather than triangles. + +Convenience converter from ``Scale`` to ``Normalize`` now public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Downstream libraries can take advantage of `.colors.make_norm_from_scale` to +create a `~.colors.Normalize` subclass directly from an existing scale. +Usually norms have a scale, and the advantage of having a `~.scale.ScaleBase` +attached to a norm is to provide a scale, and associated tick locators and +formatters, for the colorbar. + +``ContourSet`` always use ``PathCollection`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to correct rendering issues with closed loops, the `.ContourSet` now +creates a `.PathCollection` instead of a `.LineCollection` for line contours. +This type matches the artist used for filled contours. + +This affects `.ContourSet` itself and its subclasses, `.QuadContourSet` +(returned by `.Axes.contour`), and `.TriContourSet` (returned by +`.Axes.tricontour`). + +``hatch.SmallFilledCircles`` inherits from ``hatch.Circles`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `.hatch.SmallFilledCircles` class now inherits from `.hatch.Circles` rather +than from `.hatch.SmallCircles`. + +hexbin with a log norm +~~~~~~~~~~~~~~~~~~~~~~ + +`~.axes.Axes.hexbin` no longer (incorrectly) adds 1 to every bin value if a log +norm is being used. + +Setting invalid ``rcParams["date.converter"]`` now raises ValueError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, invalid values passed to :rc:`date.converter` would be ignored with +a `UserWarning`, but now raise `ValueError`. + +``Text`` and ``TextBox`` added *parse_math* option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.Text` and `.TextBox` objects now allow a *parse_math* keyword-only argument +which controls whether math should be parsed from the displayed string. If +*True*, the string will be parsed as a math text object. If *False*, the string +will be considered a literal and no parsing will occur. + +For `.Text`, this argument defaults to *True*. For `.TextBox` this argument +defaults to *False*. + +``Type1Font`` objects now decrypt the encrypted part +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Type 1 fonts have a large part of their code encrypted as an obsolete +copy-protection measure. This part is now available decrypted as the +``decrypted`` attribute of ``matplotlib.type1font.Type1Font``. This decrypted +data is not yet parsed, but this is a prerequisite for implementing subsetting. + +3D contourf polygons placed between levels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The polygons used in a 3D `~.Axes3D.contourf` plot are now +placed halfway between the contour levels, as each polygon represents the +location of values that lie between two levels. + +``AxesDivider`` now defaults to rcParams-specified pads +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`.AxesDivider.append_axes`, ``AxesDivider.new_horizontal``, and +``AxesDivider.new_vertical`` now default to paddings specified by +:rc:`figure.subplot.wspace` and :rc:`figure.subplot.hspace` rather than zero. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst new file mode 100644 index 000000000000..04836687f76a --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/deprecations.rst @@ -0,0 +1,379 @@ +Deprecations +------------ + +Discouraged: ``Figure`` parameters *tight_layout* and *constrained_layout* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Figure`` parameters *tight_layout* and *constrained_layout* are +triggering competing layout mechanisms and thus should not be used together. + +To make the API clearer, we've merged them under the new parameter *layout* +with values 'constrained' (equal to ``constrained_layout=True``), 'tight' +(equal to ``tight_layout=True``). If given, *layout* takes precedence. + +The use of *tight_layout* and *constrained_layout* is discouraged in favor of +*layout*. However, these parameters will stay available for backward +compatibility. + +Modification of ``Axes`` children sublists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :ref:`Behavioural API Changes 3.5 - Axes children combined` for more +information; modification of the following sublists is deprecated: + +* ``Axes.artists`` +* ``Axes.collections`` +* ``Axes.images`` +* ``Axes.lines`` +* ``Axes.patches`` +* ``Axes.tables`` +* ``Axes.texts`` + +To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the +corresponding ``Axes.add_*`` method. + +Passing incorrect types to ``Axes.add_*`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following ``Axes.add_*`` methods will now warn if passed an unexpected +type. See their documentation for the types they expect. + +- `.Axes.add_collection` +- `.Axes.add_image` +- `.Axes.add_line` +- `.Axes.add_patch` +- `.Axes.add_table` + +Discouraged: ``plot_date`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The use of ``plot_date`` is discouraged. This method exists for historic +reasons and may be deprecated in the future. + +- ``datetime``-like data should directly be plotted using `~.Axes.plot`. +- If you need to plot plain numeric data as :ref:`date-format` or + need to set a timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` + before `~.Axes.plot`. See `.Axis.axis_date`. + +``epoch2num`` and ``num2epoch`` are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These methods convert from unix timestamps to matplotlib floats, but are not +used internally to matplotlib, and should not be needed by end users. To +convert a unix timestamp to datetime, simply use +`datetime.datetime.utcfromtimestamp`, or to use NumPy `~numpy.datetime64` +``dt = np.datetime64(e*1e6, 'us')``. + +Auto-removal of grids by `~.Axes.pcolor` and `~.Axes.pcolormesh` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.Axes.pcolor` and `~.Axes.pcolormesh` currently remove any visible axes major +grid. This behavior is deprecated; please explicitly call ``ax.grid(False)`` to +remove the grid. + +The first parameter of ``Axes.grid`` and ``Axis.grid`` has been renamed to *visible* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter was previously named *b*. This deprecation only matters if that +parameter was passed using a keyword argument, e.g. ``grid(b=False)``. + +Unification and cleanup of Selector widget API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The API for Selector widgets has been unified to use: + +- *props* for the properties of the Artist representing the selection. +- *handle_props* for the Artists representing handles for modifying the + selection. +- *grab_range* for the maximal tolerance to grab a handle with the mouse. + +Additionally, several internal parameters and attribute have been deprecated +with the intention of keeping them private. + +RectangleSelector and EllipseSelector +..................................... + +The *drawtype* keyword argument to `~matplotlib.widgets.RectangleSelector` is +deprecated. In the future the only behaviour will be the default behaviour of +``drawtype='box'``. + +Support for ``drawtype=line`` will be removed altogether as it is not clear +which points are within and outside a selector that is just a line. As a +result, the *lineprops* keyword argument to +`~matplotlib.widgets.RectangleSelector` is also deprecated. + +To retain the behaviour of ``drawtype='none'``, use ``rectprops={'visible': +False}`` to make the drawn `~matplotlib.patches.Rectangle` invisible. + +Cleaned up attributes and arguments are: + +- The ``active_handle`` attribute has been privatized and deprecated. +- The ``drawtype`` attribute has been privatized and deprecated. +- The ``eventpress`` attribute has been privatized and deprecated. +- The ``eventrelease`` attribute has been privatized and deprecated. +- The ``interactive`` attribute has been privatized and deprecated. +- The *marker_props* argument is deprecated, use *handle_props* instead. +- The *maxdist* argument is deprecated, use *grab_range* instead. +- The *rectprops* argument is deprecated, use *props* instead. +- The ``rectprops`` attribute has been privatized and deprecated. +- The ``state`` attribute has been privatized and deprecated. +- The ``to_draw`` attribute has been privatized and deprecated. + +PolygonSelector +............... + +- The *line* attribute is deprecated. If you want to change the selector artist + properties, use the ``set_props`` or ``set_handle_props`` methods. +- The *lineprops* argument is deprecated, use *props* instead. +- The *markerprops* argument is deprecated, use *handle_props* instead. +- The *maxdist* argument and attribute is deprecated, use *grab_range* instead. +- The *vertex_select_radius* argument and attribute is deprecated, use + *grab_range* instead. + +SpanSelector +............ + +- The ``active_handle`` attribute has been privatized and deprecated. +- The ``eventpress`` attribute has been privatized and deprecated. +- The ``eventrelease`` attribute has been privatized and deprecated. +- The *maxdist* argument and attribute is deprecated, use *grab_range* instead. +- The ``pressv`` attribute has been privatized and deprecated. +- The ``prev`` attribute has been privatized and deprecated. +- The ``rect`` attribute has been privatized and deprecated. +- The *rectprops* argument is deprecated, use *props* instead. +- The ``rectprops`` attribute has been privatized and deprecated. +- The *span_stays* argument is deprecated, use the *interactive* argument + instead. +- The ``span_stays`` attribute has been privatized and deprecated. +- The ``state`` attribute has been privatized and deprecated. + +LassoSelector +............. + +- The *lineprops* argument is deprecated, use *props* instead. +- The ``onpress`` and ``onrelease`` methods are deprecated. They are straight + aliases for ``press`` and ``release``. + +``ConversionInterface.convert`` no longer needs to accept unitless values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, custom subclasses of `.units.ConversionInterface` needed to +implement a ``convert`` method that not only accepted instances of the unit, +but also unitless values (which are passed through as is). This is no longer +the case (``convert`` is never called with a unitless value), and such support +in `.StrCategoryConverter` is deprecated. Likewise, the +``.ConversionInterface.is_numlike`` helper is deprecated. + +Consider calling `.Axis.convert_units` instead, which still supports unitless +values. + +Locator and Formatter wrapper methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``set_view_interval``, ``set_data_interval`` and ``set_bounds`` methods of +`.Locator`\s and `.Formatter`\s (and their common base class, TickHelper) are +deprecated. Directly manipulate the view and data intervals on the underlying +axis instead. + +Unused positional parameters to ``print_`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +None of the ``print_`` methods implemented by canvas subclasses used +positional arguments other that the first (the output filename or file-like), +so these extra parameters are deprecated. + +``QuadMesh`` signature +~~~~~~~~~~~~~~~~~~~~~~ + +The `.QuadMesh` signature :: + + def __init__(meshWidth, meshHeight, coordinates, + antialiased=True, shading='flat', **kwargs) + +is deprecated and replaced by the new signature :: + + def __init__(coordinates, *, antialiased=True, shading='flat', **kwargs) + +In particular: + +- The *coordinates* argument must now be a (M, N, 2) array-like. Previously, + the grid shape was separately specified as (*meshHeight* + 1, *meshWidth* + + 1) and *coordinates* could be an array-like of any shape with M * N * 2 + elements. +- All parameters except *coordinates* are keyword-only now. + +rcParams will no longer cast inputs to str +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After a deprecation period, rcParams that expect a (non-pathlike) str will no +longer cast non-str inputs using `str`. This will avoid confusing errors in +subsequent code if e.g. a list input gets implicitly cast to a str. + +Case-insensitive scales +~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, scales could be set case-insensitively (e.g., +``set_xscale("LoG")``). This is deprecated; all builtin scales use lowercase +names. + +Interactive cursor details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting a mouse cursor on a window has been moved from the toolbar to the +canvas. Consequently, several implementation details on toolbars and within +backends have been deprecated. + +``NavigationToolbar2.set_cursor`` and ``backend_tools.SetCursorBase.set_cursor`` +................................................................................ + +Instead, use the `.FigureCanvasBase.set_cursor` method on the canvas (available +as the ``canvas`` attribute on the toolbar or the Figure.) + +``backend_tools.SetCursorBase`` and subclasses +.............................................. + +``backend_tools.SetCursorBase`` was subclassed to provide backend-specific +implementations of ``set_cursor``. As that is now deprecated, the subclassing +is no longer necessary. Consequently, the following subclasses are also +deprecated: + +- ``matplotlib.backends.backend_gtk3.SetCursorGTK3`` +- ``matplotlib.backends.backend_qt5.SetCursorQt`` +- ``matplotlib.backends._backend_tk.SetCursorTk`` +- ``matplotlib.backends.backend_wx.SetCursorWx`` + +Instead, use the `.backend_tools.ToolSetCursor` class. + +``cursord`` in GTK, Qt, and wx backends +....................................... + +The ``backend_gtk3.cursord``, ``backend_qt.cursord``, and +``backend_wx.cursord`` dictionaries are deprecated. This makes the GTK module +importable on headless environments. + +Miscellaneous deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``is_url`` and ``URL_REGEX`` are deprecated. (They were previously defined in + the toplevel :mod:`matplotlib` module.) +- The ``ArrowStyle.beginarrow`` and ``ArrowStyle.endarrow`` attributes are + deprecated; use the ``arrow`` attribute to define the desired heads and tails + of the arrow. +- ``backend_pgf.LatexManager.str_cache`` is deprecated. +- ``backends.qt_compat.ETS`` and ``backends.qt_compat.QT_RC_MAJOR_VERSION`` are + deprecated, with no replacement. +- The ``blocking_input`` module has been deprecated. Instead, use + ``canvas.start_event_loop()`` and ``canvas.stop_event_loop()`` while + connecting event callbacks as needed. +- ``cbook.report_memory`` is deprecated; use ``psutil.virtual_memory`` instead. +- ``cm.LUTSIZE`` is deprecated. Use :rc:`image.lut` instead. This value only + affects colormap quantization levels for default colormaps generated at + module import time. +- ``Collection.__init__`` previously ignored *transOffset* without *offsets* also + being specified. In the future, *transOffset* will begin having an effect + regardless of *offsets*. In the meantime, if you wish to set *transOffset*, + call `.Collection.set_offset_transform` explicitly. +- ``Colorbar.patch`` is deprecated; this attribute is not correctly updated + anymore. +- ``ContourLabeler.get_label_width`` is deprecated. +- ``dviread.PsfontsMap`` now raises LookupError instead of KeyError for missing + fonts. +- ``Dvi.baseline`` is deprecated (with no replacement). +- The *format* parameter of ``dviread.find_tex_file`` is deprecated (with no + replacement). +- ``FancyArrowPatch.get_path_in_displaycoord`` and + ``ConnectionPatch.get_path_in_displaycoord`` are deprecated. The path in + display coordinates can still be obtained, as for other patches, using + ``patch.get_transform().transform_path(patch.get_path())``. +- The ``font_manager.win32InstalledFonts`` and + ``font_manager.get_fontconfig_fonts`` helper functions have been deprecated. +- All parameters of ``imshow`` starting from *aspect* will become keyword-only. +- ``QuadMesh.convert_mesh_to_paths`` and ``QuadMesh.convert_mesh_to_triangles`` + are deprecated. ``QuadMesh.get_paths()`` can be used as an alternative for + the former; there is no replacement for the latter. +- ``ScalarMappable.callbacksSM`` is deprecated. Use + ``ScalarMappable.callbacks`` instead. +- ``streamplot.get_integrator`` is deprecated. +- ``style.core.STYLE_FILE_PATTERN``, ``style.core.load_base_library``, and + ``style.core.iter_user_libraries`` are deprecated. +- ``SubplotParams.validate`` is deprecated. Use `.SubplotParams.update` to + change `.SubplotParams` while always keeping it in a valid state. +- The ``grey_arrayd``, ``font_family``, ``font_families``, and ``font_info`` + attributes of `.TexManager` are deprecated. +- ``Text.get_prop_tup`` is deprecated with no replacements (because the `.Text` + class cannot know whether a backend needs to update cache e.g. when the + text's color changes). +- ``Tick.apply_tickdir`` didn't actually update the tick markers on the + existing Line2D objects used to draw the ticks and is deprecated; use + `.Axis.set_tick_params` instead. +- ``tight_layout.auto_adjust_subplotpars`` is deprecated. + +- The ``grid_info`` attribute of ``axisartist`` classes has been deprecated. +- ``axisartist.clip_path`` is deprecated with no replacement. +- ``axes_grid1.axes_grid.CbarAxes`` and ``axes_grid1.axisartist.CbarAxes`` are + deprecated (they are now dynamically generated based on the owning axes + class). +- The ``axes_grid1.Divider.get_vsize_hsize`` and + ``axes_grid1.Grid.get_vsize_hsize`` methods are deprecated. Copy their + implementations if needed. +- ``AxesDivider.append_axes(..., add_to_figure=False)`` is deprecated. Use + ``ax.remove()`` to remove the Axes from the figure if needed. +- ``FixedAxisArtistHelper.change_tick_coord`` is deprecated with no + replacement. +- ``floating_axes.GridHelperCurveLinear.get_boundary`` is deprecated, with no + replacement. +- ``ParasiteAxesBase.get_images_artists`` has been deprecated. + +- The "units finalize" signal (previously emitted by Axis instances) is + deprecated. Connect to "units" instead. +- Passing formatting parameters positionally to ``stem()`` is deprecated + +``plot_directive`` deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``:encoding:`` option to ``.. plot`` directive has had no effect since +Matplotlib 1.3.1, and is now deprecated. + +The following helpers in `matplotlib.sphinxext.plot_directive` are deprecated: + +- ``unescape_doctest`` (use `doctest.script_from_examples` instead), +- ``split_code_at_show``, +- ``run_code``. + +Testing support +~~~~~~~~~~~~~~~ + +``matplotlib.test()`` is deprecated +................................... + +Run tests using ``pytest`` from the commandline instead. The variable +``matplotlib.default_test_modules`` is only used for ``matplotlib.test()`` and +is thus deprecated as well. + +To test an installed copy, be sure to specify both ``matplotlib`` and +``mpl_toolkits`` with ``--pyargs``:: + + pytest --pyargs matplotlib.tests mpl_toolkits.tests + +See :ref:`testing` for more details. + +Unused pytest fixtures and markers +.................................. + +The fixture ``matplotlib.testing.conftest.mpl_image_comparison_parameters`` is +not used internally by Matplotlib. If you use this please copy it into your +code base. + +The ``@pytest.mark.style`` marker is deprecated; use ``@mpl.style.context``, +which has the same effect. + +Support for ``nx1 = None`` or ``ny1 = None`` in ``AxesLocator`` and ``Divider.locate`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `.axes_grid1.axes_divider`, various internal APIs will stop supporting +passing ``nx1 = None`` or ``ny1 = None`` to mean ``nx + 1`` or ``ny + 1``, in +preparation for a possible future API which allows indexing and slicing of +dividers (possibly ``divider[a:b] == divider.new_locator(a, b)``, but also +``divider[a:] == divider.new_locator(a, )``). The user-facing +`.Divider.new_locator` API is unaffected -- it correctly normalizes ``nx1 = +None`` and ``ny1 = None`` as needed. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/development.rst b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst new file mode 100644 index 000000000000..b42e6eff3423 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/development.rst @@ -0,0 +1,82 @@ +Development changes +------------------- + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.5, the :ref:`minimum supported versions ` and +some :ref:`optional dependencies ` are being bumped: + ++---------------+---------------+---------------+ +| Dependency | min in mpl3.4 | min in mpl3.5 | ++===============+===============+===============+ +| NumPy | 1.16 | 1.17 | ++---------------+---------------+---------------+ +| Tk (optional) | 8.3 | 8.4 | ++---------------+---------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + +New wheel architectures +~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels have been added for: + +- Python 3.10 +- PyPy 3.7 +- macOS on Apple Silicon (both arm64 and universal2) + +New build dependencies +~~~~~~~~~~~~~~~~~~~~~~ + +Versioning has been switched from bundled versioneer to `setuptools-scm +`__ using the +``release-branch-semver`` version scheme. The latter is well-maintained, but +may require slight modification to packaging scripts. + +The `setuptools-scm-git-archive +`__ plugin is also used +for consistent version export. + +Data directory is no longer optional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Historically, the ``mpl-data`` directory has been optional (example files were +unnecessary, and fonts could be deleted if a suitable dependency on a system +font were provided). Though example files are still optional, they have been +substantially pared down, and we now consider the directory to be required. + +Specifically, the ``matplotlibrc`` file found there is used for runtime +verifications and must exist. Packagers may still symlink fonts to system +versions if needed. + +New runtime dependencies +~~~~~~~~~~~~~~~~~~~~~~~~ + +fontTools for type 42 subsetting +................................ + +A new dependency `fontTools `_ is integrated +into Matplotlib 3.5. It is designed to be used with PS/EPS and PDF documents; +and handles Type 42 font subsetting. + +Underscore support in LaTeX +........................... + +The `underscore `_ package is now a +requirement to improve support for underscores in LaTeX. + +This is consistent with our :ref:`min_deps_policy`. + +Matplotlib-specific build options moved from ``setup.cfg`` to ``mplsetup.cfg`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to avoid conflicting with the use of :file:`setup.cfg` by +``setuptools``, the Matplotlib-specific build options have moved from +``setup.cfg`` to ``mplsetup.cfg``. The :file:`setup.cfg.template` has been +correspondingly been renamed to :file:`mplsetup.cfg.template`. + +Note that the path to this configuration file can still be set via the ``MPLSETUPCFG`` +environment variable, which allows one to keep using the same file before and after this +change. diff --git a/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst new file mode 100644 index 000000000000..3acab92c3577 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.0/removals.rst @@ -0,0 +1,364 @@ +Removals +-------- + +The following deprecated APIs have been removed: + +Removed behaviour +~~~~~~~~~~~~~~~~~ + +Stricter validation of function parameters +.......................................... + +- Calling `.Figure.add_axes` with no arguments will raise an error. Adding a + free-floating axes needs a position rectangle. If you want a figure-filling + single axes, use `.Figure.add_subplot` instead. +- `.Figure.add_subplot` validates its inputs; in particular, for + ``add_subplot(rows, cols, index)``, all parameters must be integral. + Previously strings and floats were accepted and converted to int. +- Passing *None* as the *which* argument to ``autofmt_xdate`` is no longer + supported; use its more explicit synonym, ``which="major"``, instead. +- Setting the *orientation* of an ``eventplot()`` or `.EventCollection` to + "none" or *None* is no longer supported; set it to "horizontal" instead. + Moreover, the two orientations ("horizontal" and "vertical") are now + case-sensitive. +- Passing parameters *norm* and *vmin*/*vmax* simultaneously to functions using + colormapping such as ``scatter()`` and ``imshow()`` is no longer supported. + Instead of ``norm=LogNorm(), vmin=min_val, vmax=max_val`` pass + ``norm=LogNorm(min_val, max_val)``. *vmin* and *vmax* should only be used + without setting *norm*. +- Passing *None* as either the *radius* or *startangle* arguments of an + `.Axes.pie` is no longer accepted; use the explicit defaults of 1 and 0, + respectively, instead. +- Passing *None* as the *normalize* argument of `.Axes.pie` (the former + default) is no longer accepted, and the pie will always be normalized by + default. If you wish to plot an incomplete pie, explicitly pass + ``normalize=False``. +- Support for passing *None* to ``subplot_class_factory`` has been removed. + Explicitly pass in the base `~matplotlib.axes.Axes` class instead. +- Passing multiple keys as a single comma-separated string or multiple + arguments to `.ToolManager.update_keymap` is no longer supported; pass keys + as a list of strings instead. +- Passing the dash offset as *None* is no longer accepted, as this was never + universally implemented, e.g. for vector output. Set the offset to 0 instead. +- Setting a custom method overriding `.Artist.contains` using + ``Artist.set_contains`` has been removed, as has ``Artist.get_contains``. + There is no replacement, but you may still customize pick events using + `.Artist.set_picker`. +- `~.Axes.semilogx`, `~.Axes.semilogy`, `~.Axes.loglog`, `.LogScale`, and + `.SymmetricalLogScale` used to take keyword arguments that depends on the + axis orientation ("basex" vs "basey", "subsx" vs "subsy", "nonposx" vs + "nonposy"); these parameter names have been removed in favor of "base", + "subs", "nonpositive". This removal also affects e.g. ``ax.set_yscale("log", + basey=...)`` which must now be spelled ``ax.set_yscale("log", base=...)``. + + The change from "nonpos" to "nonpositive" also affects + `~.scale.LogTransform`, `~.scale.InvertedLogTransform`, + `~.scale.SymmetricalLogTransform`, etc. + + To use *different* bases for the x-axis and y-axis of a `~.Axes.loglog` plot, + use e.g. ``ax.set_xscale("log", base=10); ax.set_yscale("log", base=2)``. +- Passing *None*, or no argument, to ``parasite_axes_class_factory``, + ``parasite_axes_auxtrans_class_factory``, ``host_axes_class_factory`` is no + longer accepted; pass an explicit base class instead. + +Case-sensitivity is now enforced more +...................................... + +- Upper or mixed-case property names are no longer normalized to lowercase in + `.Artist.set` and `.Artist.update`. This allows one to pass names such as + *patchA* or *UVC*. +- Case-insensitive capstyles and joinstyles are no longer lower-cased; please + pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", "round", + "projecting") as lowercase. +- Saving metadata in PDF with the PGF backend no longer changes keys to + lowercase. Only the canonically cased keys listed in the PDF specification + (and the `~.backend_pgf.PdfPages` documentation) are accepted. + +No implicit initialization of ``Tick`` attributes +................................................. + +The `.Tick` constructor no longer initializes the attributes ``tick1line``, +``tick2line``, ``gridline``, ``label1``, and ``label2`` via ``_get_tick1line``, +``_get_tick2line``, ``_get_gridline``, ``_get_text1``, and ``_get_text2``. +Please directly set the attribute in the subclass' ``__init__`` instead. + +``NavigationToolbar2`` subclass changes +....................................... + +Overriding the ``_init_toolbar`` method of `.NavigationToolbar2` to initialize +third-party toolbars is no longer supported. Instead, the toolbar should be +initialized in the ``__init__`` method of the subclass (which should call the +base-class' ``__init__`` as appropriate). + +The ``press`` and ``release`` methods of `.NavigationToolbar2` were called when +pressing or releasing a mouse button, but *only* when an interactive pan or +zoom was occurring (contrary to what the docs stated). They are no longer +called; if you write a backend which needs to customize such events, please +directly override ``press_pan``/``press_zoom``/``release_pan``/``release_zoom`` +instead. + +Removal of old file mode flag +............................. + +Flags containing "U" passed to `.cbook.to_filehandle` and `.cbook.open_file_cm` +are no longer accepted. This is consistent with their removal from `open` in +Python 3.9. + +Keymaps toggling ``Axes.get_navigate`` have been removed +........................................................ + +This includes numeric key events and rcParams. + +The ``TTFPATH`` and ``AFMPATH`` environment variables +..................................................... + +Support for the (undocumented) ``TTFPATH`` and ``AFMPATH`` environment +variables has been removed. Register additional fonts using +``matplotlib.font_manager.fontManager.addfont()``. + +Modules +~~~~~~~ + +- ``matplotlib.backends.qt_editor.formsubplottool``; use + ``matplotlib.backends.backend_qt.SubplotToolQt`` instead. +- ``matplotlib.compat`` +- ``matplotlib.ttconv`` +- The Qt4-based backends, ``qt4agg`` and ``qt4cairo``, have been removed. Qt4 + has reached its end-of-life in 2015 and there are no releases of either PyQt4 + or PySide for recent versions of Python. Please use one of the Qt5 or Qt6 + backends. + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following module-level classes/variables have been removed: + +- ``backend_bases.StatusbarBase`` and all its subclasses, and ``StatusBarWx``; + messages are displayed in the toolbar +- ``backend_pgf.GraphicsContextPgf`` +- ``MODIFIER_KEYS``, ``SUPER``, ``ALT``, ``CTRL``, and ``SHIFT`` of + `matplotlib.backends.backend_qt5agg` and + `matplotlib.backends.backend_qt5cairo` +- ``backend_wx.DEBUG_MSG`` +- ``dviread.Encoding`` +- ``Fil``, ``Fill``, ``Filll``, ``NegFil``, ``NegFill``, ``NegFilll``, and + ``SsGlue`` from `.mathtext`; directly construct glue instances with + ``Glue("fil")``, etc. +- ``mathtext.GlueSpec`` +- ``OldScalarFormatter``, ``IndexFormatter`` and ``IndexDateFormatter``; use + `.FuncFormatter` instead +- ``OldAutoLocator`` +- ``AVConvBase``, ``AVConvWriter`` and ``AVConvFileWriter``. Debian 8 (2015, + EOL 06/2020) and Ubuntu 14.04 (EOL 04/2019) were the last versions of Debian + and Ubuntu to ship avconv. It remains possible to force the use of avconv by + using the FFmpeg-based writers with :rc:`animation.ffmpeg_path` set to + "avconv". +- ``matplotlib.axes._subplots._subplot_classes`` +- ``axes_grid1.axes_rgb.RGBAxesBase``; use ``RGBAxes`` instead + +The following class attributes have been removed: + +- ``backend_pgf.LatexManager.latex_stdin_utf8`` +- ``backend_pgf.PdfPages.metadata`` +- ``ContourSet.ax`` and ``Quiver.ax``; use ``ContourSet.axes`` or + ``Quiver.axes`` as with other artists +- ``DateFormatter.illegal_s`` +- ``dates.YearLocator.replaced``; `.YearLocator` is now a subclass of + `.RRuleLocator`, and the attribute ``YearLocator.replaced`` has been removed. + For tick locations that required modifying this, a custom rrule and + `.RRuleLocator` can be used instead. +- ``FigureManagerBase.statusbar``; messages are displayed in the toolbar +- ``FileMovieWriter.clear_temp`` +- ``mathtext.Glue.glue_subtype`` +- ``MovieWriter.args_key``, ``MovieWriter.exec_key``, and + ``HTMLWriter.args_key`` +- ``NavigationToolbar2QT.basedir``; the base directory to the icons is + ``os.path.join(mpl.get_data_path(), "images")`` +- ``NavigationToolbar2QT.ctx`` +- ``NavigationToolbar2QT.parent``; to access the parent window, use + ``toolbar.canvas.parent()`` or ``toolbar.parent()`` +- ``prevZoomRect``, ``retinaFix``, ``savedRetinaImage``, ``wxoverlay``, + ``zoomAxes``, ``zoomStartX``, and ``zoomStartY`` attributes of + ``NavigationToolbar2Wx`` +- ``NonUniformImage.is_grayscale``, ``PcolorImage.is_grayscale``, for + consistency with ``AxesImage.is_grayscale``. (Note that previously, these + attributes were only available *after rendering the image*). +- ``RendererCairo.fontweights``, ``RendererCairo.fontangles`` +- ``used_characters`` of `.RendererPdf`, `.PdfFile`, and `.RendererPS` +- ``LogScale.LogTransform``, ``LogScale.InvertedLogTransform``, + ``SymmetricalScale.SymmetricalTransform``, and + ``SymmetricalScale.InvertedSymmetricalTransform``; directly access the + transform classes from `matplotlib.scale` +- ``cachedir``, ``rgba_arrayd``, ``serif``, ``sans_serif``, ``cursive``, and + ``monospace`` attributes of `.TexManager` +- ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and + ``axhspace`` attributes of `.widgets.SubplotTool`; access the ``ax`` + attribute of the corresponding slider +- ``widgets.TextBox.params_to_disable`` +- ``angle_helper.LocatorBase.den``; it has been renamed to *nbins* +- ``axes_grid.CbarAxesBase.cbid`` and ``axes_grid.CbarAxesBase.locator``; use + ``mappable.colorbar_cid`` or ``colorbar.locator`` instead + +The following class methods have been removed: + +- ``Axes.update_datalim_bounds``; use ``ax.dataLim.set(Bbox.union([ax.dataLim, + bounds]))`` +- ``pan`` and ``zoom`` methods of `~.axis.Axis` and `~.ticker.Locator` have + been removed; panning and zooming are now implemented using the + ``start_pan``, ``drag_pan``, and ``end_pan`` methods of `~.axes.Axes` +- ``.BboxBase.inverse_transformed``; call `.BboxBase.transformed` on the + `~.Transform.inverted()` transform +- ``Collection.set_offset_position`` and ``Collection.get_offset_position`` + have been removed; the ``offset_position`` of the `.Collection` class is now + "screen" +- ``Colorbar.on_mappable_changed`` and ``Colorbar.update_bruteforce``; use + ``Colorbar.update_normal()`` instead +- ``docstring.Substitution.from_params`` has been removed; directly assign to + ``params`` of ``docstring.Substitution`` instead +- ``DraggableBase.artist_picker``; set the artist's picker instead +- ``DraggableBase.on_motion_blit``; use `.DraggableBase.on_motion` instead +- ``FigureCanvasGTK3._renderer_init`` +- ``Locator.refresh()`` and the associated helper methods + ``NavigationToolbar2.draw()`` and ``ToolViewsPositions.refresh_locators()`` +- ``track_characters`` and ``merge_used_characters`` of `.RendererPdf`, + `.PdfFile`, and `.RendererPS` +- ``RendererWx.get_gc`` +- ``SubplotSpec.get_rows_columns``; use the ``GridSpec.nrows``, + ``GridSpec.ncols``, ``SubplotSpec.rowspan``, and ``SubplotSpec.colspan`` + properties instead. +- ``ScalarMappable.update_dict``, ``ScalarMappable.add_checker()``, and + ``ScalarMappable.check_update()``; register a callback in + ``ScalarMappable.callbacks`` to be notified of updates +- ``TexManager.make_tex_preview`` and ``TexManager.make_dvi_preview`` +- ``funcleft``, ``funcright``, ``funcbottom``, ``functop``, ``funcwspace``, and + ``funchspace`` methods of `.widgets.SubplotTool` + +- ``axes_grid1.axes_rgb.RGBAxes.add_RGB_to_figure`` +- ``axisartist.axis_artist.AxisArtist.dpi_transform`` +- ``axisartist.grid_finder.MaxNLocator.set_factor`` and + ``axisartist.grid_finder.FixedLocator.set_factor``; the factor is always 1 + now + +Functions +~~~~~~~~~ + +- ``bezier.make_path_regular`` has been removed; use ``Path.cleaned()`` (or + ``Path.cleaned(curves=True)``, etc.) instead, but note that these methods add + a ``STOP`` code at the end of the path. +- ``bezier.concatenate_paths`` has been removed; use + ``Path.make_compound_path()`` instead. +- ``cbook.local_over_kwdict`` has been removed; use `.cbook.normalize_kwargs` + instead. +- ``qt_compat.is_pyqt5`` has been removed due to the release of PyQt6. The Qt + version can be checked using ``QtCore.qVersion()``. +- ``testing.compare.make_external_conversion_command`` has been removed. +- ``axes_grid1.axes_rgb.imshow_rgb`` has been removed; use + ``imshow(np.dstack([r, g, b]))`` instead. + +Arguments +~~~~~~~~~ + +- The *s* parameter to `.Axes.annotate` and `.pyplot.annotate` is no longer + supported; use the new name *text*. +- The *inframe* parameter to `matplotlib.axes.Axes.draw` has been removed; use + `.Axes.redraw_in_frame` instead. +- The *required*, *forbidden* and *allowed* parameters of + `.cbook.normalize_kwargs` have been removed. +- The *ismath* parameter of the ``draw_tex`` method of all renderer classes has + been removed (as a call to ``draw_tex`` — not to be confused with + ``draw_text``! — means that the entire string should be passed to the + ``usetex`` machinery anyways). Likewise, the text machinery will no longer + pass the *ismath* parameter when calling ``draw_tex`` (this should only + matter for backend implementers). +- The *quality*, *optimize*, and *progressive* parameters of `.Figure.savefig` + (which only affected JPEG output) have been removed, as well as from the + corresponding ``print_jpg`` methods. JPEG output options can be set by + directly passing the relevant parameters in *pil_kwargs*. +- The *clear_temp* parameter of `.FileMovieWriter` has been removed; files + placed in a temporary directory (using ``frame_prefix=None``, the default) + will be cleared; files placed elsewhere will not. +- The *copy* parameter of ``mathtext.Glue`` has been removed. +- The *quantize* parameter of `.Path.cleaned()` has been removed. +- The *dummy* parameter of `.RendererPgf` has been removed. +- The *props* parameter of `.Shadow` has been removed; use keyword arguments + instead. +- The *recursionlimit* parameter of ``matplotlib.test`` has been removed. +- The *label* parameter of `.Tick` has no effect and has been removed. +- `~.ticker.MaxNLocator` no longer accepts a positional parameter and the + keyword argument *nbins* simultaneously because they specify the same + quantity. +- The *add_all* parameter to ``axes_grid.Grid``, ``axes_grid.ImageGrid``, + ``axes_rgb.make_rgb_axes``, and ``axes_rgb.RGBAxes`` have been removed; the + APIs always behave as if ``add_all=True``. +- The *den* parameter of ``axisartist.angle_helper.LocatorBase`` has been + removed; use *nbins* instead. + +- The *s* keyword argument to `.AnnotationBbox.get_fontsize` has no effect and + has been removed. +- The *offset_position* keyword argument of the `.Collection` class has been + removed; the ``offset_position`` now "screen". +- Arbitrary keyword arguments to ``StreamplotSet`` have no effect and have been + removed. + +- The *fontdict* and *minor* parameters of `.Axes.set_xticklabels` / + `.Axes.set_yticklabels` are now keyword-only. +- All parameters of `.Figure.subplots` except *nrows* and *ncols* are now + keyword-only; this avoids typing e.g. ``subplots(1, 1, 1)`` when meaning + ``subplot(1, 1, 1)``, but actually getting ``subplots(1, 1, sharex=1)``. +- All parameters of `.pyplot.tight_layout` are now keyword-only, to be + consistent with `.Figure.tight_layout`. +- ``ColorbarBase`` only takes a single positional argument now, the ``Axes`` to + create it in, with all other options required to be keyword arguments. The + warning for keyword arguments that were overridden by the mappable is now + removed. + +- Omitting the *renderer* parameter to `matplotlib.axes.Axes.draw` is no longer + supported; use ``axes.draw_artist(axes)`` instead. +- Passing ``ismath="TeX!"`` to `.RendererAgg.get_text_width_height_descent` is + no longer supported; pass ``ismath="TeX"`` instead, +- Changes to the signature of the `matplotlib.axes.Axes.draw` method make it + consistent with all other artists; thus additional parameters to + `.Artist.draw` have also been removed. + +rcParams +~~~~~~~~ + +- The ``animation.avconv_path`` and ``animation.avconv_args`` rcParams have + been removed. +- The ``animation.html_args`` rcParam has been removed. +- The ``keymap.all_axes`` rcParam has been removed. +- The ``mathtext.fallback_to_cm`` rcParam has been removed. Use + :rc:`mathtext.fallback` instead. +- The ``savefig.jpeg_quality`` rcParam has been removed. +- The ``text.latex.preview`` rcParam has been removed. +- The following deprecated rcParams validators, defined in `.rcsetup`, have + been removed: + + - ``validate_alignment`` + - ``validate_axes_titlelocation`` + - ``validate_axis_locator`` + - ``validate_bool_maybe_none`` + - ``validate_fontset`` + - ``validate_grid_axis`` + - ``validate_hinting`` + - ``validate_legend_loc`` + - ``validate_mathtext_default`` + - ``validate_movie_frame_fmt`` + - ``validate_movie_html_fmt`` + - ``validate_movie_writer`` + - ``validate_nseq_float`` + - ``validate_nseq_int`` + - ``validate_orientation`` + - ``validate_pgf_texsystem`` + - ``validate_ps_papersize`` + - ``validate_svg_fonttype`` + - ``validate_toolbar`` + - ``validate_webagg_address`` + +- Some rcParam validation has become stricter: + + - :rc:`axes.axisbelow` no longer accepts strings starting with "line" + (case-insensitive) as "line"; use "line" (case-sensitive) instead. + - :rc:`text.latex.preamble` and ``pdf.preamble`` no longer accept non-string values. + - All ``*.linestyle`` rcParams no longer accept ``offset = None``; set the + offset to 0 instead. diff --git a/doc/api/prev_api_changes/api_changes_3.5.2.rst b/doc/api/prev_api_changes/api_changes_3.5.2.rst new file mode 100644 index 000000000000..47b000de0350 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.2.rst @@ -0,0 +1,13 @@ +API Changes for 3.5.2 +===================== + +.. contents:: + :local: + :depth: 1 + +QuadMesh mouseover defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New in 3.5, `.QuadMesh.get_cursor_data` allows display of data values +under the cursor. However, this can be very slow for large meshes, so +by ``.QuadMesh.set_mouseover`` defaults to *False*. diff --git a/doc/api/prev_api_changes/api_changes_3.5.3.rst b/doc/api/prev_api_changes/api_changes_3.5.3.rst new file mode 100644 index 000000000000..03d1f476513e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.5.3.rst @@ -0,0 +1,13 @@ +API Changes for 3.5.3 +===================== + +.. contents:: + :local: + :depth: 1 + +Passing *linefmt* positionally is undeprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional use of all formatting parameters in `~.Axes.stem` has been +deprecated since Matplotlib 3.5. This deprecation is relaxed so that one can +still pass *linefmt* positionally, i.e. ``stem(x, y, 'r')``. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0.rst b/doc/api/prev_api_changes/api_changes_3.6.0.rst new file mode 100644 index 000000000000..1bba4506fd7d --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.6.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.6.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.6.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst new file mode 100644 index 000000000000..6ace010515fb --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst @@ -0,0 +1,248 @@ +Behaviour changes +----------------- + +``plt.get_cmap`` and ``matplotlib.cm.get_cmap`` return a copy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `~.pyplot.get_cmap` and ``matplotlib.cm.get_cmap`` returned a global version +of a `.Colormap`. This was prone to errors as modification of the colormap would +propagate from one location to another without warning. Now, a new copy of the colormap +is returned. + +Large ``imshow`` images are now downsampled +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When showing an image using `~matplotlib.axes.Axes.imshow` that has more than +:math:`2^{24}` columns or :math:`2^{23}` rows, the image will now be +downsampled to below this resolution before being resampled for display by the +AGG renderer. Previously such a large image would be shown incorrectly. To +prevent this downsampling and the warning it raises, manually downsample your +data before handing it to `~matplotlib.axes.Axes.imshow`. + +Default date limits changed to 1970-01-01 – 1970-01-02 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously the default limits for an empty axis set up for dates +(`.Axis.axis_date`) was 2000-01-01 to 2010-01-01. This has been changed to +1970-01-01 to 1970-01-02. With the default epoch, this makes the numeric limit +for date axes the same as for other axes (0.0-1.0), and users are less likely +to set a locator with far too many ticks. + +*markerfmt* argument to ``stem`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The behavior of the *markerfmt* parameter of `~.Axes.stem` has changed: + +- If *markerfmt* does not contain a color, the color is taken from *linefmt*. +- If *markerfmt* does not contain a marker, the default is 'o'. + +Before, *markerfmt* was passed unmodified to ``plot(..., fmt)``, which had a +number of unintended side-effects; e.g. only giving a color switched to a solid +line without markers. + +For a simple call ``stem(x, y)`` without parameters, the new rules still +reproduce the old behavior. + +``get_ticklabels`` now always populates labels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously `.Axis.get_ticklabels` (and `.Axes.get_xticklabels`, +`.Axes.get_yticklabels`) would only return empty strings unless a draw had +already been performed. Now the ticks and their labels are updated when the +labels are requested. + +Warning when scatter plot color settings discarded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When making an animation of a scatter plot, if you don't set *c* (the color +value parameter) when initializing the artist, the color settings are ignored. +`.Axes.scatter` now raises a warning if color-related settings are changed +without setting *c*. + +3D ``contourf`` polygons placed between levels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The polygons used in a 3D `~.Axes3D.contourf` plot are now placed halfway +between the contour levels, as each polygon represents the location of values +that lie between two levels. + +Axes title now avoids y-axis offset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, Axes titles could overlap the y-axis offset text, which is often in +the upper left corner of the axes. Now titles are moved above the offset text +if overlapping when automatic title positioning is in effect (i.e. if *y* in +`.Axes.set_title` is *None* and :rc:`axes.titley` is also *None*). + +Dotted operators gain extra space in mathtext +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In mathtext, ``\doteq \doteqdot \dotminus \dotplus \dots`` are now surrounded +by extra space because they are correctly treated as relational or binary +operators. + +*math* parameter of ``mathtext.get_unicode_index`` defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In math mode, ASCII hyphens (U+002D) are now replaced by Unicode minus signs +(U+2212) at the parsing stage. + +``ArtistList`` proxies copy contents on iteration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When iterating over the contents of the dynamically generated proxy lists for +the Artist-type accessors (see :ref:`Behavioural API Changes 3.5 - Axes +children combined`), a copy of the contents is made. This ensure that artists +can safely be added or removed from the Axes while iterating over their +children. + +This is a departure from the expected behavior of mutable iterable data types +in Python — iterating over a list while mutating it has surprising consequences +and dictionaries will error if they change size during iteration. Because all +of the accessors are filtered views of the same underlying list, it is possible +for seemingly unrelated changes, such as removing a Line, to affect the +iteration over any of the other accessors. In this case, we have opted to make +a copy of the relevant children before yielding them to the user. + +This change is also consistent with our plan to make these accessors immutable +in Matplotlib 3.7. + +``AxesImage`` string representation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The string representation of `.AxesImage` changes from stating the position in +the figure ``"AxesImage(80,52.8;496x369.6)"`` to giving the number of pixels +``"AxesImage(size=(300, 200))"``. + +Improved autoscaling for Bézier curves +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bézier curves are now autoscaled to their extents - previously they were +autoscaled to their ends and control points, which in some cases led to +unnecessarily large limits. + +``QuadMesh`` mouseover defaults to False +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New in 3.5, `.QuadMesh.get_cursor_data` allows display of data values under the +cursor. However, this can be very slow for large meshes, so mouseover now +defaults to *False*. + +Changed pgf backend document class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The pgf backend now uses the ``article`` document class as basis for +compilation. + +``MathtextBackendAgg.get_results`` no longer returns ``used_characters`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The last item (``used_characters``) in the tuple returned by +``MathtextBackendAgg.get_results`` has been removed. In order to unpack this +tuple in a backward and forward-compatible way, use e.g. ``ox, oy, width, +height, descent, image, *_ = parse(...)``, which will ignore +``used_characters`` if it was present. + +``Type1Font`` objects include more properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``matplotlib._type1font.Type1Font.prop`` dictionary now includes more keys, +such as ``CharStrings`` and ``Subrs``. The value of the ``Encoding`` key is now +a dictionary mapping codes to glyph names. The +``matplotlib._type1font.Type1Font.transform`` method now correctly removes +``UniqueID`` properties from the font. + +``rcParams.copy()`` returns ``RcParams`` rather than ``dict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Returning an `.RcParams` instance from `.RcParams.copy` makes the copy still +validate inputs, and additionally avoids emitting deprecation warnings when +using a previously copied instance to update the global instance (even if some +entries are deprecated). + +``rc_context`` no longer resets the value of ``'backend'`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`matplotlib.rc_context` incorrectly reset the value of :rc:`backend` if backend +resolution was triggered in the context. This affected only the value. The +actual backend was not changed. Now, `matplotlib.rc_context` does not reset +:rc:`backend` anymore. + +Default ``rcParams["animation.convert_args"]`` changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It now defaults to ``["-layers", "OptimizePlus"]`` to try to generate smaller +GIFs. Set it back to an empty list to recover the previous behavior. + +Style file encoding now specified to be UTF-8 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It has been impossible to import Matplotlib with a non UTF-8 compatible locale +encoding because we read the style library at import time. This change is +formalizing and documenting the status quo so there is no deprecation period. + +MacOSX backend uses sRGB instead of GenericRGB color space +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MacOSX backend now display sRGB tagged image instead of GenericRGB which is an +older (now deprecated) Apple color space. This is the source color space used +by ColorSync to convert to the current display profile. + +Renderer optional for ``get_tightbbox`` and ``get_window_extent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `.Artist.get_tightbbox` and `.Artist.get_window_extent` methods no longer +require the *renderer* keyword argument, saving users from having to query it +from ``fig.canvas.get_renderer``. If the *renderer* keyword argument is not +supplied, these methods first check if there is a cached renderer from a +previous draw and use that. If there is no cached renderer, then the methods +will use ``fig.canvas.get_renderer()`` as a fallback. + +``FigureFrameWx`` constructor, subclasses, and ``get_canvas`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``FigureCanvasWx`` constructor gained a *canvas_class* keyword-only +parameter which specifies the canvas class that should be used. This parameter +will become required in the future. The ``get_canvas`` method, which was +previously used to customize canvas creation, is deprecated. The +``FigureFrameWxAgg`` and ``FigureFrameWxCairo`` subclasses, which overrode +``get_canvas``, are deprecated. + +``FigureFrameWx.sizer`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. The frame layout is no longer based on a sizer, as the +canvas is now the sole child widget; the toolbar is now a regular toolbar added +using ``SetToolBar``. + +Incompatible layout engines raise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You cannot switch between ``tight_layout`` and ``constrained_layout`` if a +colorbar has already been added to a figure. Invoking the incompatible layout +engine used to warn, but now raises with a `RuntimeError`. + +``CallbackRegistry`` raises on unknown signals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When Matplotlib instantiates a `.CallbackRegistry`, it now limits callbacks to +the signals that the registry knows about. In practice, this means that calling +`~.FigureCanvasBase.mpl_connect` with an invalid signal name now raises a +`ValueError`. + +Changed exception type for incorrect SVG date metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Providing date metadata with incorrect type to the SVG backend earlier resulted +in a `ValueError`. Now, a `TypeError` is raised instead. + +Specified exception types in ``Grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In a few cases an `Exception` was thrown when an incorrect argument value was +set in the `mpl_toolkits.axes_grid1.axes_grid.Grid` (= +``mpl_toolkits.axisartist.axes_grid.Grid``) constructor. These are replaced as +follows: + +* Providing an incorrect value for *ngrids* now raises a `ValueError` +* Providing an incorrect type for *rect* now raises a `TypeError` diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst new file mode 100644 index 000000000000..3a9e91e12289 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst @@ -0,0 +1,414 @@ +Deprecations +------------ + +Parameters to ``plt.figure()`` and the ``Figure`` constructor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All parameters to `.pyplot.figure` and the `.Figure` constructor, other than +*num*, *figsize*, and *dpi*, will become keyword-only after a deprecation +period. + +Deprecation aliases in cbook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The module ``matplotlib.cbook.deprecation`` was previously deprecated in +Matplotlib 3.4, along with deprecation-related API in ``matplotlib.cbook``. Due +to technical issues, ``matplotlib.cbook.MatplotlibDeprecationWarning`` and +``matplotlib.cbook.mplDeprecation`` did not raise deprecation warnings on use. +Changes in Python have now made it possible to warn when these aliases are +being used. + +In order to avoid downstream breakage, these aliases will now warn, and their +removal has been pushed from 3.6 to 3.8 to give time to notice said warnings. +As replacement, please use `matplotlib.MatplotlibDeprecationWarning`. + +``Axes`` subclasses should override ``clear`` instead of ``cla`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For clarity, `.axes.Axes.clear` is now preferred over `.Axes.cla`. However, for +backwards compatibility, the latter will remain as an alias for the former. + +For additional compatibility with third-party libraries, Matplotlib will +continue to call the ``cla`` method of any `~.axes.Axes` subclasses if they +define it. In the future, this will no longer occur, and Matplotlib will only +call the ``clear`` method in `~.axes.Axes` subclasses. + +It is recommended to define only the ``clear`` method when on Matplotlib 3.6, +and only ``cla`` for older versions. + +Pending deprecation top-level cmap registration and access functions in ``mpl.cm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As part of a `multi-step process +`_ we are refactoring +the global state for managing the registered colormaps. + +In Matplotlib 3.5 we added a `.ColormapRegistry` class and exposed an instance +at the top level as ``matplotlib.colormaps``. The existing top level functions +in `matplotlib.cm` (``get_cmap``, ``register_cmap``, ``unregister_cmap``) were +changed to be aliases around the same instance. + +In Matplotlib 3.6 we have marked those top level functions as pending +deprecation with the intention of deprecation in Matplotlib 3.7. The following +functions have been marked for pending deprecation: + +- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you + have a `str`. + + **Added 3.6.1** Use `matplotlib.cm.ColormapRegistry.get_cmap` if you + have a string, `None` or a `matplotlib.colors.Colormap` object that you want + to convert to a `matplotlib.colors.Colormap` instance. +- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead +- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister + <.ColormapRegistry.unregister>` instead +- ``matplotlib.pyplot.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead + +The `matplotlib.pyplot.get_cmap` function will stay available for backward +compatibility. + +Pending deprecation of layout methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The methods `~.Figure.set_tight_layout`, `~.Figure.set_constrained_layout`, are +discouraged, and now emit a `PendingDeprecationWarning` in favor of explicitly +referencing the layout engine via ``figure.set_layout_engine('tight')`` and +``figure.set_layout_engine('constrained')``. End users should not see the +warning, but library authors should adjust. + +The methods `~.Figure.set_constrained_layout_pads` and +`~.Figure.get_constrained_layout_pads` are will be deprecated in favor of +``figure.get_layout_engine().set()`` and ``figure.get_layout_engine().get()``, +and currently emit a `PendingDeprecationWarning`. + +seaborn styles renamed +~~~~~~~~~~~~~~~~~~~~~~ + +Matplotlib currently ships many style files inspired from the seaborn library +("seaborn", "seaborn-bright", "seaborn-colorblind", etc.) but they have gone +out of sync with the library itself since the release of seaborn 0.9. To +prevent confusion, the style files have been renamed "seaborn-v0_8", +"seaborn-v0_8-bright", "seaborn-v0_8-colorblind", etc. Users are encouraged to +directly use seaborn to access the up-to-date styles. + +Auto-removal of overlapping Axes by ``plt.subplot`` and ``plt.subplot2grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, `.pyplot.subplot` and `.pyplot.subplot2grid` would automatically +remove preexisting Axes that overlap with the newly added Axes. This behavior +was deemed confusing, and is now deprecated. Explicitly call ``ax.remove()`` on +Axes that need to be removed. + +Passing *linefmt* positionally to ``stem`` is undeprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional use of all formatting parameters in `~.Axes.stem` has been +deprecated since Matplotlib 3.5. This deprecation is relaxed so that one can +still pass *linefmt* positionally, i.e. ``stem(x, y, 'r')``. + +``stem(..., use_line_collection=False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated with no replacement. This was a compatibility fallback to a +former more inefficient representation of the stem lines. + +Positional / keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing all but the very few first arguments positionally in the constructors +of Artists is deprecated. Most arguments will become keyword-only in a future +version. + +Passing too many positional arguments to ``tripcolor`` is now deprecated (extra +arguments were previously silently ignored). + +Passing *emit* and *auto* parameters of ``set_xlim``, ``set_ylim``, +``set_zlim``, ``set_rlim`` positionally is deprecated; they will become +keyword-only in a future release. + +The *transOffset* parameter of `.Collection.set_offset_transform` and the +various ``create_collection`` methods of legend handlers has been renamed to +*offset_transform* (consistently with the property name). + +Calling ``MarkerStyle()`` with no arguments or ``MarkerStyle(None)`` is +deprecated; use ``MarkerStyle("")`` to construct an empty marker style. + +``Axes.get_window_extent`` / ``Figure.get_window_extent`` accept only +*renderer*. This aligns the API with the general `.Artist.get_window_extent` +API. All other parameters were ignored anyway. + +The *cleared* parameter of ``get_renderer``, which only existed for AGG-based +backends, has been deprecated. Use ``renderer.clear()`` instead to explicitly +clear the renderer buffer. + +Methods to set parameters in ``LogLocator`` and ``LogFormatter*`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `~.LogFormatter` and derived subclasses, the methods ``base`` and +``label_minor`` for setting the respective parameter are deprecated and +replaced by ``set_base`` and ``set_label_minor``, respectively. + +In `~.LogLocator`, the methods ``base`` and ``subs`` for setting the respective +parameter are deprecated. Instead, use ``set_params(base=..., subs=...)``. + +``Axes.get_renderer_cache`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The canvas now takes care of the renderer and whether to cache it or not. The +alternative is to call ``axes.figure.canvas.get_renderer()``. + +Groupers from ``get_shared_x_axes`` / ``get_shared_y_axes`` will be immutable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Modifications to the Groupers returned by ``get_shared_x_axes`` and +``get_shared_y_axes`` are deprecated. In the future, these methods will return +immutable views on the grouper structures. Note that previously, calling e.g. +``join()`` would already fail to set up the correct structures for sharing +axes; use `.Axes.sharex` or `.Axes.sharey` instead. + +Unused methods in ``Axis``, ``Tick``, ``XAxis``, and ``YAxis`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Tick.label`` has been pending deprecation since 3.1 and is now deprecated. +Use ``Tick.label1`` instead. + +The following methods are no longer used and deprecated without a replacement: + +- ``Axis.get_ticklabel_extents`` +- ``Tick.get_pad_pixels`` +- ``XAxis.get_text_heights`` +- ``YAxis.get_text_widths`` + +``mlab.stride_windows`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. Use ``np.lib.stride_tricks.sliding_window_view`` instead (or +``np.lib.stride_tricks.as_strided`` on NumPy < 1.20). + +Event handlers +~~~~~~~~~~~~~~ + +The ``draw_event``, ``resize_event``, ``close_event``, ``key_press_event``, +``key_release_event``, ``pick_event``, ``scroll_event``, +``button_press_event``, ``button_release_event``, ``motion_notify_event``, +``enter_notify_event`` and ``leave_notify_event`` methods of +`.FigureCanvasBase` are deprecated. They had inconsistent signatures across +backends, and made it difficult to improve event metadata. + +In order to trigger an event on a canvas, directly construct an `.Event` object +of the correct class and call ``canvas.callbacks.process(event.name, event)``. + +Widgets +~~~~~~~ + +All parameters to ``MultiCursor`` starting from *useblit* are becoming +keyword-only (passing them positionally is deprecated). + +The ``canvas`` and ``background`` attributes of ``MultiCursor`` are deprecated +with no replacement. + +The *visible* attribute of Selector widgets has been deprecated; use +``set_visible`` or ``get_visible`` instead. + +The *state_modifier_keys* attribute of Selector widgets has been privatized and +the modifier keys must be set when creating the widget. + +``Axes3D.dist`` +~~~~~~~~~~~~~~~ + +... has been privatized. Use the *zoom* keyword argument in +`.Axes3D.set_box_aspect` instead. + +3D Axis +~~~~~~~ + +The previous constructor of `.axis3d.Axis`, with signature ``(self, adir, +v_intervalx, d_intervalx, axes, *args, rotate_label=None, **kwargs)`` is +deprecated in favor of a new signature closer to the one of 2D Axis; it is now +``(self, axes, *, rotate_label=None, **kwargs)`` where ``kwargs`` are forwarded +to the 2D Axis constructor. The axis direction is now inferred from the axis +class' ``axis_name`` attribute (as in the 2D case); the ``adir`` attribute is +deprecated. + +The ``init3d`` method of 3D Axis is also deprecated; all the relevant +initialization is done as part of the constructor. + +The ``d_interval`` and ``v_interval`` attributes of 3D Axis are deprecated; use +``get_data_interval`` and ``get_view_interval`` instead. + +The ``w_xaxis``, ``w_yaxis``, and ``w_zaxis`` attributes of ``Axis3D`` have +been pending deprecation since 3.1. They are now deprecated. Instead use +``xaxis``, ``yaxis``, and ``zaxis``. + +``mplot3d.axis3d.Axis.set_pane_pos`` is deprecated. This is an internal method +where the provided values are overwritten during drawing. Hence, it does not +serve any purpose to be directly accessible. + +The two helper functions ``mplot3d.axis3d.move_from_center`` and +``mplot3d.axis3d.tick_update_position`` are considered internal and deprecated. +If these are required, please vendor the code from the corresponding private +methods ``_move_from_center`` and ``_tick_update_position``. + +``Figure.callbacks`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Figure ``callbacks`` property is deprecated. The only signal was +"dpi_changed", which can be replaced by connecting to the "resize_event" on the +canvas ``figure.canvas.mpl_connect("resize_event", func)`` instead. + +``FigureCanvas`` without a ``required_interactive_framework`` attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for such canvas classes is deprecated. Note that canvas classes which +inherit from ``FigureCanvasBase`` always have such an attribute. + +Backend-specific deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``backend_gtk3.FigureManagerGTK3Agg`` and + ``backend_gtk4.FigureManagerGTK4Agg``; directly use + ``backend_gtk3.FigureManagerGTK3`` and ``backend_gtk4.FigureManagerGTK4`` + instead. +- The *window* parameter to ``backend_gtk3.NavigationToolbar2GTK3`` had no + effect, and is now deprecated. +- ``backend_gtk3.NavigationToolbar2GTK3.win`` +- ``backend_gtk3.RendererGTK3Cairo`` and ``backend_gtk4.RendererGTK4Cairo``; + use `.RendererCairo` instead, which has gained the ``set_context`` method, + which also auto-infers the size of the underlying surface. +- ``backend_cairo.RendererCairo.set_ctx_from_surface`` and + ``backend_cairo.RendererCairo.set_width_height`` in favor of + `.RendererCairo.set_context`. +- ``backend_gtk3.error_msg_gtk`` +- ``backend_gtk3.icon_filename`` and ``backend_gtk3.window_icon`` +- ``backend_macosx.NavigationToolbar2Mac.prepare_configure_subplots`` has been + replaced by ``configure_subplots()``. +- ``backend_pdf.Name.hexify`` +- ``backend_pdf.Operator`` and ``backend_pdf.Op.op`` are deprecated in favor of + a single standard `enum.Enum` interface on `.backend_pdf.Op`. +- ``backend_pdf.fill``; vendor the code of the similarly named private + functions if you rely on these functions. +- ``backend_pgf.LatexManager.texcommand`` and + ``backend_pgf.LatexManager.latex_header`` +- ``backend_pgf.NO_ESCAPE`` +- ``backend_pgf.common_texification`` +- ``backend_pgf.get_fontspec`` +- ``backend_pgf.get_preamble`` +- ``backend_pgf.re_mathsep`` +- ``backend_pgf.writeln`` +- ``backend_ps.convert_psfrags`` +- ``backend_ps.quote_ps_string``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_qt.qApp``; use ``QtWidgets.QApplication.instance()`` instead. +- ``backend_svg.escape_attrib``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_cdata``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_comment``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.short_float_fmt``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.generate_transform`` and ``backend_svg.generate_css`` +- ``backend_tk.NavigationToolbar2Tk.lastrect`` and + ``backend_tk.RubberbandTk.lastrect`` +- ``backend_tk.NavigationToolbar2Tk.window``; use ``toolbar.master`` instead. +- ``backend_tools.ToolBase.destroy``; To run code upon tool removal, connect to + the ``tool_removed_event`` event. +- ``backend_wx.RendererWx.offset_text_height`` +- ``backend_wx.error_msg_wx`` + +- ``FigureCanvasBase.pick``; directly call `.Figure.pick`, which has taken over + the responsibility of checking the canvas widget lock as well. +- ``FigureCanvasBase.resize``, which has no effect; use + ``FigureManagerBase.resize`` instead. + +- ``FigureManagerMac.close`` + +- ``FigureFrameWx.sizer``; use ``frame.GetSizer()`` instead. +- ``FigureFrameWx.figmgr`` and ``FigureFrameWx.get_figure_manager``; use + ``frame.canvas.manager`` instead. +- ``FigureFrameWx.num``; use ``frame.canvas.manager.num`` instead. +- ``FigureFrameWx.toolbar``; use ``frame.GetToolBar()`` instead. +- ``FigureFrameWx.toolmanager``; use ``frame.canvas.manager.toolmanager`` + instead. + +Modules +~~~~~~~ + +The modules ``matplotlib.afm``, ``matplotlib.docstring``, +``matplotlib.fontconfig_pattern``, ``matplotlib.tight_bbox``, +``matplotlib.tight_layout``, and ``matplotlib.type1font`` are considered +internal and public access is deprecated. + +``checkdep_usetex`` deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This method was only intended to disable tests in case no latex install was +found. As such, it is considered to be private and for internal use only. + +Please vendor the code if you need this. + +``date_ticker_factory`` deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``date_ticker_factory`` method in the `matplotlib.dates` module is +deprecated. Instead use `~.AutoDateLocator` and `~.AutoDateFormatter` for a +more flexible and scalable locator and formatter. + +If you need the exact ``date_ticker_factory`` behavior, please copy the code. + +``dviread.find_tex_file`` will raise ``FileNotFoundError`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the future, ``dviread.find_tex_file`` will raise a `FileNotFoundError` for +missing files. Previously, it would return an empty string in such cases. +Raising an exception allows attaching a user-friendly message instead. During +the transition period, a warning is raised. + +``transforms.Affine2D.identity()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated in favor of directly calling the `.Affine2D` constructor with +no arguments. + +Deprecations in ``testing.decorators`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The unused class ``CleanupTestCase`` and decorator ``cleanup`` are deprecated +and will be removed. Vendor the code, including the private function +``_cleanup_cm``. + +The function ``check_freetype_version`` is considered internal and deprecated. +Vendor the code of the private function ``_check_freetype_version``. + +``text.get_rotation()`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated with no replacement. Copy the original implementation if +needed. + +Miscellaneous internals +~~~~~~~~~~~~~~~~~~~~~~~ + +- ``axes_grid1.axes_size.AddList``; use ``sum(sizes, start=Fixed(0))`` (for + example) to sum multiple size objects. +- ``axes_size.Padded``; use ``size + pad`` instead +- ``axes_size.SizeFromFunc``, ``axes_size.GetExtentHelper`` +- ``AxisArtistHelper.delta1`` and ``AxisArtistHelper.delta2`` +- ``axislines.GridHelperBase.new_gridlines`` and + ``axislines.Axes.new_gridlines`` +- ``cbook.maxdict``; use the standard library ``functools.lru_cache`` instead. +- ``_DummyAxis.dataLim`` and ``_DummyAxis.viewLim``; use + ``get_data_interval()``, ``set_data_interval()``, ``get_view_interval()``, + and ``set_view_interval()`` instead. +- ``GridSpecBase.get_grid_positions(..., raw=True)`` +- ``ImageMagickBase.delay`` and ``ImageMagickBase.output_args`` +- ``MathtextBackend``, ``MathtextBackendAgg``, ``MathtextBackendPath``, + ``MathTextWarning`` +- ``TexManager.get_font_config``; it previously returned an internal hashed key + for used for caching purposes. +- ``TextToPath.get_texmanager``; directly construct a `.texmanager.TexManager` + instead. +- ``ticker.is_close_to_int``; use ``math.isclose(x, round(x))`` instead. +- ``ticker.is_decade``; use ``y = numpy.log(x)/numpy.log(base); + numpy.isclose(y, numpy.round(y))`` instead. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/development.rst b/doc/api/prev_api_changes/api_changes_3.6.0/development.rst new file mode 100644 index 000000000000..fb9f1f3e21c5 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/development.rst @@ -0,0 +1,42 @@ +Development changes +------------------- + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.6, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.5 | min in mpl3.6 | ++============+=================+===============+ +| Python | 3.7 | 3.8 | ++------------+-----------------+---------------+ +| NumPy | 1.17 | 1.19 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + +Build setup options changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``gui_support.macosx`` setup option has been renamed to +``packages.macosx``. + +New wheel architectures +~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels have been added for: + +- Python 3.11 +- PyPy 3.8 and 3.9 + +Increase to required versions of documentation dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`sphinx`_ >= 3.0 and `numpydoc`_ >= 1.0 are now required for building the +documentation. + +.. _numpydoc: https://pypi.org/project/numpydoc/ +.. _sphinx: https://pypi.org/project/Sphinx/ diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst new file mode 100644 index 000000000000..1e128ef5e90d --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.0/removals.rst @@ -0,0 +1,222 @@ +Removals +-------- + +The following deprecated APIs have been removed: + +Removed behaviour +~~~~~~~~~~~~~~~~~ + +Stricter validation of function parameters +.......................................... + +- Unknown keyword arguments to `.Figure.savefig`, `.pyplot.savefig`, and the + ``FigureCanvas.print_*`` methods now raise a `TypeError`, instead of being + ignored. +- Extra parameters to the `~.axes.Axes` constructor, i.e., those other than + *fig* and *rect*, are now keyword only. +- Passing arguments not specifically listed in the signatures of + `.Axes3D.plot_surface` and `.Axes3D.plot_wireframe` is no longer supported; + pass any extra arguments as keyword arguments instead. +- Passing positional arguments to `.LineCollection` has been removed; use + specific keyword argument names now. + +``imread`` no longer accepts URLs +................................. + +Passing a URL to `~.pyplot.imread()` has been removed. Please open the URL for +reading and directly use the Pillow API (e.g., +``PIL.Image.open(urllib.request.urlopen(url))``, or +``PIL.Image.open(io.BytesIO(requests.get(url).content))``) instead. + +MarkerStyle is immutable +........................ + +The methods ``MarkerStyle.set_fillstyle`` and ``MarkerStyle.set_marker`` have +been removed. Create a new `.MarkerStyle` with the respective parameters +instead. + +Passing bytes to ``FT2Font.set_text`` +..................................... + +... is no longer supported. Pass `str` instead. + +Support for passing tool names to ``ToolManager.add_tool`` +.......................................................... + +... has been removed. The second parameter to `.ToolManager.add_tool` must now +always be a tool class. + +``backend_tools.ToolFullScreen`` now inherits from ``ToolBase``, not from ``ToolToggleBase`` +............................................................................................ + +`.ToolFullScreen` can only switch between the non-fullscreen and fullscreen +states, but not unconditionally put the window in a given state; hence the +``enable`` and ``disable`` methods were misleadingly named. Thus, the +`.ToolToggleBase`-related API (``enable``, ``disable``, etc.) was removed. + +``BoxStyle._Base`` and ``transmute`` method of box styles +......................................................... + +... have been removed. Box styles implemented as classes no longer need to +inherit from a base class. + +Loaded modules logging +...................... + +The list of currently loaded modules is no longer logged at the DEBUG level at +Matplotlib import time, because it can produce extensive output and make other +valuable DEBUG statements difficult to find. If you were relying on this +output, please arrange for your own logging (the built-in `sys.modules` can be +used to get the currently loaded modules). + +Modules +~~~~~~~ + +- The ``cbook.deprecation`` module has been removed from the public API as it + is considered internal. +- The ``mpl_toolkits.axes_grid`` module has been removed. All functionality from + ``mpl_toolkits.axes_grid`` can be found in either `mpl_toolkits.axes_grid1` + or `mpl_toolkits.axisartist`. Axes classes from ``mpl_toolkits.axes_grid`` + based on ``Axis`` from `mpl_toolkits.axisartist` can be found in + `mpl_toolkits.axisartist`. + +Classes, methods and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following module-level classes/variables have been removed: + +- ``cm.cmap_d`` +- ``colorbar.colorbar_doc``, ``colorbar.colorbar_kw_doc`` +- ``ColorbarPatch`` +- ``mathtext.Fonts`` and all its subclasses +- ``mathtext.FontConstantsBase`` and all its subclasses +- ``mathtext.latex_to_bakoma``, ``mathtext.latex_to_cmex``, + ``mathtext.latex_to_standard`` +- ``mathtext.MathtextBackendPdf``, ``mathtext.MathtextBackendPs``, + ``mathtext.MathtextBackendSvg``, ``mathtext.MathtextBackendCairo``; use + ``.MathtextBackendPath`` instead. +- ``mathtext.Node`` and all its subclasses +- ``mathtext.NUM_SIZE_LEVELS`` +- ``mathtext.Parser`` +- ``mathtext.Ship`` +- ``mathtext.SHRINK_FACTOR`` and ``mathtext.GROW_FACTOR`` +- ``mathtext.stix_virtual_fonts``, +- ``mathtext.tex2uni`` +- ``backend_pgf.TmpDirCleaner`` +- ``backend_ps.GraphicsContextPS``; use ``GraphicsContextBase`` instead. +- ``backend_wx.IDLE_DELAY`` +- ``axes_grid1.parasite_axes.ParasiteAxesAuxTransBase``; use + `.ParasiteAxesBase` instead. +- ``axes_grid1.parasite_axes.ParasiteAxesAuxTrans``; use `.ParasiteAxes` + instead. + +The following class attributes have been removed: + +- ``Line2D.validCap`` and ``Line2D.validJoin``; validation is centralized in + ``rcsetup``. +- ``Patch.validCap`` and ``Patch.validJoin``; validation is centralized in + ``rcsetup``. +- ``renderer.M``, ``renderer.eye``, ``renderer.vvec``, + ``renderer.get_axis_position`` placed on the Renderer during 3D Axes draw; + these attributes are all available via `.Axes3D`, which can be accessed via + ``self.axes`` on all `.Artist`\s. +- ``RendererPdf.mathtext_parser``, ``RendererPS.mathtext_parser``, + ``RendererSVG.mathtext_parser``, ``RendererCairo.mathtext_parser`` +- ``StandardPsFonts.pswriter`` +- ``Subplot.figbox``; use `.Axes.get_position` instead. +- ``Subplot.numRows``; ``ax.get_gridspec().nrows`` instead. +- ``Subplot.numCols``; ``ax.get_gridspec().ncols`` instead. +- ``SubplotDivider.figbox`` +- ``cids``, ``cnt``, ``observers``, ``change_observers``, and + ``submit_observers`` on all `.Widget`\s + +The following class methods have been removed: + +- ``Axis.cla()``; use `.Axis.clear` instead. +- ``RadialAxis.cla()`` and ``ThetaAxis.cla()``; use `.RadialAxis.clear` or + `.ThetaAxis.clear` instead. +- ``Spine.cla()``; use `.Spine.clear` instead. +- ``ContourLabeler.get_label_coords()``; there is no replacement as it was + considered an internal helper. +- ``FancyArrowPatch.get_dpi_cor`` and ``FancyArrowPatch.set_dpi_cor`` + +- ``FigureCanvas.get_window_title()`` and ``FigureCanvas.set_window_title()``; + use `.FigureManagerBase.get_window_title` or + `.FigureManagerBase.set_window_title` if using pyplot, or use GUI-specific + methods if embedding. +- ``FigureManager.key_press()`` and ``FigureManager.button_press()``; trigger + the events directly on the canvas using + ``canvas.callbacks.process(event.name, event)`` for key and button events. + +- ``RendererAgg.get_content_extents()`` and + ``RendererAgg.tostring_rgba_minimized()`` +- ``NavigationToolbar2Wx.get_canvas()`` + +- ``ParasiteAxesBase.update_viewlim()``; use ``ParasiteAxesBase.apply_aspect`` + instead. +- ``Subplot.get_geometry()``; use ``SubplotBase.get_subplotspec`` instead. +- ``Subplot.change_geometry()``; use ``SubplotBase.set_subplotspec`` instead. +- ``Subplot.update_params()``; this method did nothing. +- ``Subplot.is_first_row()``; use ``ax.get_subplotspec().is_first_row`` + instead. +- ``Subplot.is_first_col()``; use ``ax.get_subplotspec().is_first_col`` + instead. +- ``Subplot.is_last_row()``; use ``ax.get_subplotspec().is_last_row`` instead. +- ``Subplot.is_last_col()``; use ``ax.get_subplotspec().is_last_col`` instead. +- ``SubplotDivider.change_geometry()``; use `.SubplotDivider.set_subplotspec` + instead. +- ``SubplotDivider.get_geometry()``; use `.SubplotDivider.get_subplotspec` + instead. +- ``SubplotDivider.update_params()`` +- ``get_depth``, ``parse``, ``to_mask``, ``to_rgba``, and ``to_png`` of + `.MathTextParser`; use `.mathtext.math_to_image` instead. + +- ``MovieWriter.cleanup()``; the cleanup logic is instead fully implemented in + `.MovieWriter.finish` and ``cleanup`` is no longer called. + +Functions +~~~~~~~~~ + +The following functions have been removed; + +- ``backend_template.new_figure_manager()``, + ``backend_template.new_figure_manager_given_figure()``, and + ``backend_template.draw_if_interactive()`` have been removed, as part of the + introduction of the simplified backend API. +- Deprecation-related re-imports ``cbook.deprecated()``, and + ``cbook.warn_deprecated()``. +- ``colorbar.colorbar_factory()``; use `.Colorbar` instead. + ``colorbar.make_axes_kw_doc()`` +- ``mathtext.Error()`` +- ``mathtext.ship()`` +- ``mathtext.tex2uni()`` +- ``axes_grid1.parasite_axes.parasite_axes_auxtrans_class_factory()``; use + `.parasite_axes_class_factory` instead. +- ``sphinext.plot_directive.align()``; use + ``docutils.parsers.rst.directives.images.Image.align`` instead. + +Arguments +~~~~~~~~~ + +The following arguments have been removed: + +- *dpi* from ``print_ps()`` in the PS backend and ``print_pdf()`` in the PDF + backend. Instead, the methods will obtain the DPI from the ``savefig`` + machinery. +- *dpi_cor* from `~.FancyArrowPatch` +- *minimum_descent* from ``TextArea``; it is now effectively always True +- *origin* from ``FigureCanvasWx.gui_repaint()`` +- *project* from ``Line3DCollection.draw()`` +- *renderer* from `.Line3DCollection.do_3d_projection`, + `.Patch3D.do_3d_projection`, `.PathPatch3D.do_3d_projection`, + `.Path3DCollection.do_3d_projection`, `.Patch3DCollection.do_3d_projection`, + `.Poly3DCollection.do_3d_projection` +- *resize_callback* from the Tk backend; use + ``get_tk_widget().bind('', ..., True)`` instead. +- *return_all* from ``gridspec.get_position()`` +- Keyword arguments to ``gca()``; there is no replacement. + +rcParams +~~~~~~~~ + +The setting :rc:`ps.useafm` no longer has any effect on `matplotlib.mathtext`. diff --git a/doc/api/prev_api_changes/api_changes_3.6.1.rst b/doc/api/prev_api_changes/api_changes_3.6.1.rst new file mode 100644 index 000000000000..ad929d426885 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.6.1.rst @@ -0,0 +1,15 @@ +API Changes for 3.6.1 +===================== + +Deprecations +------------ + +Colorbars for orphaned mappables are deprecated, but no longer raise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before 3.6.0, Colorbars for mappables that do not have a parent Axes would +steal space from the current Axes. 3.6.0 raised an error on this, but without a +deprecation cycle. For 3.6.1 this is reverted; the current Axes is used, but a +deprecation warning is shown instead. In this undetermined case, users and +libraries should explicitly specify what Axes they want space to be stolen +from: ``fig.colorbar(mappable, ax=plt.gca())``. diff --git a/doc/api/prev_api_changes/api_changes_3.7.0.rst b/doc/api/prev_api_changes/api_changes_3.7.0.rst new file mode 100644 index 000000000000..932a4ba34452 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.7.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.7.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.7.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.7.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.7.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.7.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst new file mode 100644 index 000000000000..2409eb2a5dd0 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.7.0/behaviour.rst @@ -0,0 +1,135 @@ +Behaviour Changes +----------------- + +All Axes have ``get_subplotspec`` and ``get_gridspec`` methods now, which returns None for Axes not positioned via a gridspec +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, this method was only present for Axes positioned via a gridspec. +Following this change, checking ``hasattr(ax, "get_gridspec")`` should now be +replaced by ``ax.get_gridspec() is not None``. For compatibility with older +Matplotlib releases, one can also check +``hasattr(ax, "get_gridspec") and ax.get_gridspec() is not None``. + +``HostAxesBase.get_aux_axes`` now defaults to using the same base axes class as the host axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If using an ``mpl_toolkits.axisartist``-based host Axes, the parasite Axes will +also be based on ``mpl_toolkits.axisartist``. This behavior is consistent with +``HostAxesBase.twin``, ``HostAxesBase.twinx``, and ``HostAxesBase.twiny``. + +``plt.get_cmap`` and ``matplotlib.cm.get_cmap`` return a copy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Formerly, `~.pyplot.get_cmap` and ``matplotlib.cm.get_cmap`` returned a global version +of a `.Colormap`. This was prone to errors as modification of the colormap would +propagate from one location to another without warning. Now, a new copy of the colormap +is returned. + +``TrapezoidMapTriFinder`` uses different random number generator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The random number generator used to determine the order of insertion of +triangle edges in ``TrapezoidMapTriFinder`` has changed. This can result in a +different triangle index being returned for a point that lies exactly on an +edge between two triangles. This can also affect triangulation interpolation +and refinement algorithms that use ``TrapezoidMapTriFinder``. + +``FuncAnimation(save_count=None)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing ``save_count=None`` to `.FuncAnimation` no longer limits the number +of frames to 100. Make sure that it either can be inferred from *frames* +or provide an integer *save_count*. + +``CenteredNorm`` halfrange is not modified when vcenter changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, the **halfrange** would expand in proportion to the +amount that **vcenter** was moved away from either **vmin** or **vmax**. +Now, the halfrange remains fixed when vcenter is changed, and **vmin** and +**vmax** are updated based on the **vcenter** and **halfrange** values. + +For example, this is what the values were when changing vcenter previously. + +.. code-block:: python + + norm = CenteredNorm(vcenter=0, halfrange=1) + # Move vcenter up by one + norm.vcenter = 1 + # updates halfrange and vmax (vmin stays the same) + # norm.halfrange == 2, vmin == -1, vmax == 3 + +and now, with that same example + +.. code-block:: python + + norm = CenteredNorm(vcenter=0, halfrange=1) + norm.vcenter = 1 + # updates vmin and vmax (halfrange stays the same) + # norm.halfrange == 1, vmin == 0, vmax == 2 + +The **halfrange** can be set manually or ``norm.autoscale()`` +can be used to automatically set the limits after setting **vcenter**. + +``fig.subplot_mosaic`` no longer passes the ``gridspec_kw`` args to nested gridspecs. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For nested `.Figure.subplot_mosaic` layouts, it is almost always +inappropriate for *gridspec_kw* arguments to be passed to lower nest +levels, and these arguments are incompatible with the lower levels in +many cases. This dictionary is no longer passed to the inner +layouts. Users who need to modify *gridspec_kw* at multiple levels +should use `.Figure.subfigures` to get nesting, and construct the +inner layouts with `.Figure.subplots` or `.Figure.subplot_mosaic`. + +``HPacker`` alignment with **bottom** or **top** are now correct +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, the **bottom** and **top** alignments were swapped. +This has been corrected so that the alignments correspond appropriately. + +On Windows only fonts known to the registry will be discovered +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, Matplotlib would recursively walk user and system font directories +to discover fonts, however this lead to a number of undesirable behaviors +including finding deleted fonts. Now Matplotlib will only find fonts that are +known to the Windows registry. + +This means that any user installed fonts must go through the Windows font +installer rather than simply being copied to the correct folder. + +This only impacts the set of fonts Matplotlib will consider when using +`matplotlib.font_manager.findfont`. To use an arbitrary font, directly pass the +path to a font as shown in +:doc:`/gallery/text_labels_and_annotations/font_file`. + +``QuadMesh.set_array`` now always raises ``ValueError`` for inputs with incorrect shapes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It could previously also raise `TypeError` in some cases. + +``contour`` and ``contourf`` auto-select suitable levels when given boolean inputs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the height array given to `.Axes.contour` or `.Axes.contourf` is of bool +dtype and *levels* is not specified, *levels* now defaults to ``[0.5]`` for +`~.Axes.contour` and ``[0, 0.5, 1]`` for `.Axes.contourf`. + +``contour`` no longer warns if no contour lines are drawn. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This can occur if the user explicitly passes a ``levels`` array with no values +between ``z.min()`` and ``z.max()``; or if ``z`` has the same value everywhere. + +``AxesImage.set_extent`` now raises ``TypeError`` for unknown keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It previously raised a `ValueError`. + +Change of ``legend(loc="best")`` behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The algorithm of the auto-legend locator has been tweaked to better handle +non rectangular patches. Additional details on this change can be found in +:ghissue:`9580` and :ghissue:`9598`. diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst new file mode 100644 index 000000000000..55a0a7133c65 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst @@ -0,0 +1,291 @@ +Deprecations +------------ + +``Axes`` subclasses should override ``clear`` instead of ``cla`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For clarity, `.axes.Axes.clear` is now preferred over `.Axes.cla`. However, for +backwards compatibility, the latter will remain as an alias for the former. + +For additional compatibility with third-party libraries, Matplotlib will +continue to call the ``cla`` method of any `~.axes.Axes` subclasses if they +define it. In the future, this will no longer occur, and Matplotlib will only +call the ``clear`` method in `~.axes.Axes` subclasses. + +It is recommended to define only the ``clear`` method when on Matplotlib 3.6, +and only ``cla`` for older versions. + +rcParams type +~~~~~~~~~~~~~ + +Relying on ``rcParams`` being a ``dict`` subclass is deprecated. + +Nothing will change for regular users because ``rcParams`` will continue to +be dict-like (technically fulfill the ``MutableMapping`` interface). + +The `.RcParams` class does validation checking on calls to +``.RcParams.__getitem__`` and ``.RcParams.__setitem__``. However, there are rare +cases where we want to circumvent the validation logic and directly access the +underlying data values. Previously, this could be accomplished via a call to +the parent methods ``dict.__getitem__(rcParams, key)`` and +``dict.__setitem__(rcParams, key, val)``. + +Matplotlib 3.7 introduces ``rcParams._set(key, val)`` and +``rcParams._get(key)`` as a replacement to calling the parent methods. They are +intentionally marked private to discourage external use; However, if direct +`.RcParams` data access is needed, please switch from the dict functions to the +new ``_get()`` and ``_set()``. Even though marked private, we guarantee API +stability for these methods and they are subject to Matplotlib's API and +deprecation policy. + +Please notify the Matplotlib developers if you rely on ``rcParams`` being a +dict subclass in any other way, for which there is no migration path yet. + +Deprecation aliases in cbook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The module ``matplotlib.cbook.deprecation`` was previously deprecated in +Matplotlib 3.4, along with deprecation-related API in ``matplotlib.cbook``. Due +to technical issues, ``matplotlib.cbook.MatplotlibDeprecationWarning`` and +``matplotlib.cbook.mplDeprecation`` did not raise deprecation warnings on use. +Changes in Python have now made it possible to warn when these aliases are +being used. + +In order to avoid downstream breakage, these aliases will now warn, and their +removal has been pushed from 3.6 to 3.8 to give time to notice said warnings. +As replacement, please use `matplotlib.MatplotlibDeprecationWarning`. + +``draw_gouraud_triangle`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated as in most backends this is a redundant call. Use +`~.RendererBase.draw_gouraud_triangles` instead. A ``draw_gouraud_triangle`` +call in a custom `~matplotlib.artist.Artist` can readily be replaced as:: + + self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), + colors.reshape((1, 3, 4)), trans) + +A `~.RendererBase.draw_gouraud_triangles` method can be implemented from an +existing ``draw_gouraud_triangle`` method as:: + + transform = transform.frozen() + for tri, col in zip(triangles_array, colors_array): + self.draw_gouraud_triangle(gc, tri, col, transform) + +``matplotlib.pyplot.get_plot_commands`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is a pending deprecation. This is considered internal and no end-user +should need it. + +``matplotlib.tri`` submodules are deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``matplotlib.tri.*`` submodules are deprecated. All functionality is +available in ``matplotlib.tri`` directly and should be imported from there. + +Passing undefined *label_mode* to ``Grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. This includes `mpl_toolkits.axes_grid1.axes_grid.Grid`, +`mpl_toolkits.axes_grid1.axes_grid.AxesGrid`, and +`mpl_toolkits.axes_grid1.axes_grid.ImageGrid` as well as the corresponding +classes imported from ``mpl_toolkits.axisartist.axes_grid``. + +Pass ``label_mode='keep'`` instead to get the previous behavior of not modifying labels. + +Colorbars for orphaned mappables are deprecated, but no longer raise +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before 3.6.0, Colorbars for mappables that do not have a parent axes would +steal space from the current Axes. 3.6.0 raised an error on this, but without +a deprecation cycle. For 3.6.1 this is reverted, the current axes is used, +but a deprecation warning is shown instead. In this undetermined case users +and libraries should explicitly specify what axes they want space to be stolen +from: ``fig.colorbar(mappable, ax=plt.gca())``. + +``Animation`` attributes +~~~~~~~~~~~~~~~~~~~~~~~~ + +The attributes ``repeat`` of `.TimedAnimation` and subclasses and +``save_count`` of `.FuncAnimation` are considered private and deprecated. + +``contour.ClabelText`` and ``ContourLabeler.set_label_props`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated. + +Use ``Text(..., transform_rotates_text=True)`` as a replacement for +``contour.ClabelText(...)`` and ``text.set(text=text, color=color, +fontproperties=labeler.labelFontProps, clip_box=labeler.axes.bbox)`` as a +replacement for the ``ContourLabeler.set_label_props(label, text, color)``. + +``ContourLabeler`` attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``labelFontProps``, ``labelFontSizeList``, and ``labelTextsList`` +attributes of `.ContourLabeler` have been deprecated. Use the ``labelTexts`` +attribute and the font properties of the corresponding text objects instead. + +``backend_ps.PsBackendHelper`` and ``backend_ps.ps_backend_helper`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated with no replacement. + +``backend_webagg.ServerThread`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... with no replacement. + +``parse_fontconfig_pattern`` will no longer ignore unknown constant names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, in a fontconfig pattern like ``DejaVu Sans:foo``, the unknown +``foo`` constant name would be silently ignored. This now raises a warning, +and will become an error in the future. + +``BufferRegion.to_string`` and ``BufferRegion.to_string_argb`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated. Use ``np.asarray(buffer_region)`` to get an array view on +a buffer region without making a copy; to convert that view from RGBA (the +default) to ARGB, use ``np.take(..., [2, 1, 0, 3], axis=2)``. + +``num2julian``, ``julian2num`` and ``JULIAN_OFFSET`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... of the `.dates` module are deprecated without replacements. These are +undocumented and not exported. If you rely on these, please make a local copy. + +``unit_cube``, ``tunit_cube``, and ``tunit_edges`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... of `.Axes3D` are deprecated without replacements. If you rely on them, +please copy the code of the corresponding private function (name starting +with ``_``). + +Most arguments to widgets have been made keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing all but the very few first arguments positionally in the constructors +of Widgets is deprecated. Most arguments will become keyword-only in a future +version. + +``SimpleEvent`` +~~~~~~~~~~~~~~~ + +The ``SimpleEvent`` nested class (previously accessible via the public +subclasses of ``ConnectionStyle._Base``, such as `.ConnectionStyle.Arc`, has +been deprecated. + +``RadioButtons.circles`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. (RadioButtons now draws itself using `~.Axes.scatter`.) + +``CheckButtons.rectangles`` and ``CheckButtons.lines`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``CheckButtons.rectangles`` and ``CheckButtons.lines`` are deprecated. +(``CheckButtons`` now draws itself using `~.Axes.scatter`.) + +``OffsetBox.get_extent_offsets`` and ``OffsetBox.get_extent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated; these methods are also deprecated on all subclasses of +`.OffsetBox`. + +To get the offsetbox extents, instead of ``get_extent``, use +`.OffsetBox.get_bbox`, which directly returns a `.Bbox` instance. + +To also get the child offsets, instead of ``get_extent_offsets``, separately +call `~.OffsetBox.get_offset` on each children after triggering a draw. + +``legend.legendHandles`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +... was undocumented and has been renamed to ``legend_handles``. Using ``legendHandles`` is deprecated. + +``ticklabels`` parameter of `.Axis.set_ticklabels` renamed to ``labels`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``offsetbox.bbox_artist`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is deprecated. This is just a wrapper to call `.patches.bbox_artist` if a +flag is set in the file, so use that directly if you need the behavior. + +``Quiver.quiver_doc`` and ``Barbs.barbs_doc`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated. These are the doc-string and should not be accessible as +a named class member. + +Deprecate unused parameter *x* to ``TextBox.begin_typing`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This parameter was unused in the method, but was a required argument. + +Deprecation of top-level cmap registration and access functions in ``mpl.cm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As part of a `multi-step process +`_ we are refactoring +the global state for managing the registered colormaps. + +In Matplotlib 3.5 we added a `.ColormapRegistry` class and exposed an instance +at the top level as ``matplotlib.colormaps``. The existing top level functions +in `matplotlib.cm` (``get_cmap``, ``register_cmap``, ``unregister_cmap``) were +changed to be aliases around the same instance. In Matplotlib 3.6 we have +marked those top level functions as pending deprecation. + +In Matplotlib 3.7, the following functions have been marked for deprecation: + +- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you + have a `str`. + + **Added 3.6.1** Use `matplotlib.cm.ColormapRegistry.get_cmap` if you + have a string, `None` or a `matplotlib.colors.Colormap` object that you want + to convert to a `matplotlib.colors.Colormap` instance. +- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead +- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister + <.ColormapRegistry.unregister>` instead +- ``matplotlib.pyplot.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead + +The `matplotlib.pyplot.get_cmap` function will stay available for backward +compatibility. + +``BrokenBarHCollection`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It was just a thin wrapper inheriting from `.PolyCollection`; +`~.Axes.broken_barh` has now been changed to return a `.PolyCollection` +instead. + +The ``BrokenBarHCollection.span_where`` helper is likewise deprecated; for the +duration of the deprecation it has been moved to the parent `.PolyCollection` +class. Use `~.Axes.fill_between` as a replacement; see +:doc:`/gallery/lines_bars_and_markers/span_regions` for an example. + +Passing inconsistent ``loc`` and ``nth_coord`` to axisartist helpers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Trying to construct for example a "top y-axis" or a "left x-axis" is now +deprecated. + +``passthru_pt`` +~~~~~~~~~~~~~~~ + +This attribute of ``AxisArtistHelper``\s is deprecated. + +``axes3d.vvec``, ``axes3d.eye``, ``axes3d.sx``, and ``axes3d.sy`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated without replacement. + +``Line2D`` +~~~~~~~~~~ + +When creating a Line2D or using `.Line2D.set_xdata` and `.Line2D.set_ydata`, +passing x/y data as non sequence is deprecated. diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/development.rst b/doc/api/prev_api_changes/api_changes_3.7.0/development.rst new file mode 100644 index 000000000000..c2ae35970524 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.7.0/development.rst @@ -0,0 +1,49 @@ +Development changes +------------------- + + +Windows wheel runtime bundling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels built for Windows now bundle the MSVC runtime DLL ``msvcp140.dll``. This +enables importing Matplotlib on systems that do not have the runtime installed. + + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +For Matplotlib 3.7, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.6 | min in mpl3.7 | ++============+=================+===============+ +| NumPy | 1.19 | 1.20 | ++------------+-----------------+---------------+ +| pyparsing | 2.2.1 | 2.3.1 | ++------------+-----------------+---------------+ +| Qt | | 5.10 | ++------------+-----------------+---------------+ + +- There are no wheels or conda packages that support both Qt 5.9 (or older) and + Python 3.8 (or newer). + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + + +New dependencies +~~~~~~~~~~~~~~~~ + +* `importlib-resources `_ + (>= 3.2.0; only required on Python < 3.10) + +Maximum line length increased to 88 characters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The maximum line length for new contributions has been extended from 79 characters to +88 characters. +This change provides an extra 9 characters to allow code which is a single idea to fit +on fewer lines (often a single line). +The chosen length is the same as `black `_. diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst new file mode 100644 index 000000000000..56b3ad5c253e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.7.0/removals.rst @@ -0,0 +1,369 @@ +Removals +-------- + +``epoch2num`` and ``num2epoch`` are removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These methods convert from unix timestamps to matplotlib floats, but are not +used internally to Matplotlib, and should not be needed by end users. To +convert a unix timestamp to datetime, simply use +`datetime.datetime.fromtimestamp`, or to use NumPy `~numpy.datetime64` +``dt = np.datetime64(e*1e6, 'us')``. + +Locator and Formatter wrapper methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``set_view_interval``, ``set_data_interval`` and ``set_bounds`` methods of +`.Locator`\s and `.Formatter`\s (and their common base class, TickHelper) are +removed. Directly manipulate the view and data intervals on the underlying +axis instead. + +Interactive cursor details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting a mouse cursor on a window has been moved from the toolbar to the +canvas. Consequently, several implementation details on toolbars and within +backends have been removed. + +``NavigationToolbar2.set_cursor`` and ``backend_tools.SetCursorBase.set_cursor`` +................................................................................ + +Instead, use the `.FigureCanvasBase.set_cursor` method on the canvas (available +as the ``canvas`` attribute on the toolbar or the Figure.) + +``backend_tools.SetCursorBase`` and subclasses +.............................................. + +``backend_tools.SetCursorBase`` was subclassed to provide backend-specific +implementations of ``set_cursor``. As that is now removed, the subclassing +is no longer necessary. Consequently, the following subclasses are also +removed: + +- ``matplotlib.backends.backend_gtk3.SetCursorGTK3`` +- ``matplotlib.backends.backend_qt5.SetCursorQt`` +- ``matplotlib.backends._backend_tk.SetCursorTk`` +- ``matplotlib.backends.backend_wx.SetCursorWx`` + +Instead, use the `.backend_tools.ToolSetCursor` class. + +``cursord`` in GTK and wx backends +.................................. + +The ``backend_gtk3.cursord`` and ``backend_wx.cursord`` dictionaries are +removed. This makes the GTK module importable on headless environments. + +``auto_add_to_figure=True`` for ``Axes3D`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is no longer supported. Instead use ``fig.add_axes(ax)``. + +The first parameter of ``Axes.grid`` and ``Axis.grid`` has been renamed to *visible* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parameter was previously named *b*. This name change only matters if that +parameter was passed using a keyword argument, e.g. ``grid(b=False)``. + +Removal of deprecations in the Selector widget API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +RectangleSelector and EllipseSelector +..................................... + +The *drawtype* keyword argument to `~matplotlib.widgets.RectangleSelector` is +removed. From now on, the only behaviour will be ``drawtype='box'``. + +Support for ``drawtype=line`` is removed altogether. As a +result, the *lineprops* keyword argument to +`~matplotlib.widgets.RectangleSelector` is also removed. + +To retain the behaviour of ``drawtype='none'``, use ``rectprops={'visible': +False}`` to make the drawn `~matplotlib.patches.Rectangle` invisible. + +Cleaned up attributes and arguments are: + +- The ``active_handle`` attribute has been privatized and removed. +- The ``drawtype`` attribute has been privatized and removed. +- The ``eventpress`` attribute has been privatized and removed. +- The ``eventrelease`` attribute has been privatized and removed. +- The ``interactive`` attribute has been privatized and removed. +- The *marker_props* argument is removed, use *handle_props* instead. +- The *maxdist* argument is removed, use *grab_range* instead. +- The *rectprops* argument is removed, use *props* instead. +- The ``rectprops`` attribute has been privatized and removed. +- The ``state`` attribute has been privatized and removed. +- The ``to_draw`` attribute has been privatized and removed. + +PolygonSelector +............... + +- The *line* attribute is removed. If you want to change the selector artist + properties, use the ``set_props`` or ``set_handle_props`` methods. +- The *lineprops* argument is removed, use *props* instead. +- The *markerprops* argument is removed, use *handle_props* instead. +- The *maxdist* argument and attribute is removed, use *grab_range* instead. +- The *vertex_select_radius* argument and attribute is removed, use + *grab_range* instead. + +SpanSelector +............ + +- The ``active_handle`` attribute has been privatized and removed. +- The ``eventpress`` attribute has been privatized and removed. +- The ``eventrelease`` attribute has been privatized and removed. +- The ``pressv`` attribute has been privatized and removed. +- The ``prev`` attribute has been privatized and removed. +- The ``rect`` attribute has been privatized and removed. +- The *rectprops* parameter has been renamed to *props*. +- The ``rectprops`` attribute has been privatized and removed. +- The *span_stays* parameter has been renamed to *interactive*. +- The ``span_stays`` attribute has been privatized and removed. +- The ``state`` attribute has been privatized and removed. + +LassoSelector +............. + +- The *lineprops* argument is removed, use *props* instead. +- The ``onpress`` and ``onrelease`` methods are removed. They are straight + aliases for ``press`` and ``release``. +- The ``matplotlib.widgets.TextBox.DIST_FROM_LEFT`` attribute has been + removed. It was marked as private in 3.5. + +``backend_template.show`` +~~~~~~~~~~~~~~~~~~~~~~~~~ +... has been removed, in order to better demonstrate the new backend definition +API. + +Unused positional parameters to ``print_`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +None of the ``print_`` methods implemented by canvas subclasses used +positional arguments other that the first (the output filename or file-like), +so these extra parameters are removed. + +``QuadMesh`` signature +~~~~~~~~~~~~~~~~~~~~~~ + +The `.QuadMesh` signature :: + + def __init__(meshWidth, meshHeight, coordinates, + antialiased=True, shading='flat', **kwargs) + +is removed and replaced by the new signature :: + + def __init__(coordinates, *, antialiased=True, shading='flat', **kwargs) + +In particular: + +- The *coordinates* argument must now be a (M, N, 2) array-like. Previously, + the grid shape was separately specified as (*meshHeight* + 1, *meshWidth* + + 1) and *coordinates* could be an array-like of any shape with M * N * 2 + elements. +- All parameters except *coordinates* are keyword-only now. + +Expiration of ``FancyBboxPatch`` deprecations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `.FancyBboxPatch` constructor no longer accepts the *bbox_transmuter* +parameter, nor can the *boxstyle* parameter be set to "custom" -- instead, +directly set *boxstyle* to the relevant boxstyle instance. The +*mutation_scale* and *mutation_aspect* parameters have also become +keyword-only. + +The *mutation_aspect* parameter is now handled internally and no longer passed +to the boxstyle callables when mutating the patch path. + +Testing support +~~~~~~~~~~~~~~~ + +``matplotlib.test()`` has been removed +...................................... + +Run tests using ``pytest`` from the commandline instead. The variable +``matplotlib.default_test_modules`` was only used for ``matplotlib.test()`` and +is thus removed as well. + +To test an installed copy, be sure to specify both ``matplotlib`` and +``mpl_toolkits`` with ``--pyargs``:: + + pytest --pyargs matplotlib.tests mpl_toolkits.tests + +See :ref:`testing` for more details. + +Auto-removal of grids by `~.Axes.pcolor` and `~.Axes.pcolormesh` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`~.Axes.pcolor` and `~.Axes.pcolormesh` previously remove any visible axes +major grid. This behavior is removed; please explicitly call ``ax.grid(False)`` +to remove the grid. + +Modification of ``Axes`` children sublists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :ref:`Behavioural API Changes 3.5 - Axes children combined` for more +information; modification of the following sublists is no longer supported: + +* ``Axes.artists`` +* ``Axes.collections`` +* ``Axes.images`` +* ``Axes.lines`` +* ``Axes.patches`` +* ``Axes.tables`` +* ``Axes.texts`` + +To remove an Artist, use its `.Artist.remove` method. To add an Artist, use the +corresponding ``Axes.add_*`` method. + +Passing incorrect types to ``Axes.add_*`` methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following ``Axes.add_*`` methods will now raise if passed an unexpected +type. See their documentation for the types they expect. + +- `.Axes.add_collection` +- `.Axes.add_image` +- `.Axes.add_line` +- `.Axes.add_patch` +- `.Axes.add_table` + + +``ConversionInterface.convert`` no longer accepts unitless values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, custom subclasses of `.units.ConversionInterface` needed to +implement a ``convert`` method that not only accepted instances of the unit, +but also unitless values (which are passed through as is). This is no longer +the case (``convert`` is never called with a unitless value), and such support +in ``.StrCategoryConverter`` is removed. Likewise, the +``.ConversionInterface.is_numlike`` helper is removed. + +Consider calling `.Axis.convert_units` instead, which still supports unitless +values. + + +Normal list of `.Artist` objects now returned by `.HandlerLine2D.create_artists` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.5 and 3.6 a proxy list was returned that simulated the return +of `.HandlerLine2DCompound.create_artists`. Now a list containing only the +single artist is return. + + +rcParams will no longer cast inputs to str +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rcParams that expect a (non-pathlike) str no longer cast non-str inputs using +`str`. This will avoid confusing errors in subsequent code if e.g. a list input +gets implicitly cast to a str. + +Case-insensitive scales +~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, scales could be set case-insensitively (e.g., +``set_xscale("LoG")``). Now all builtin scales use lowercase names. + +Support for ``nx1 = None`` or ``ny1 = None`` in ``AxesLocator`` and ``Divider.locate`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `.axes_grid1.axes_divider`, various internal APIs no longer supports +passing ``nx1 = None`` or ``ny1 = None`` to mean ``nx + 1`` or ``ny + 1``, in +preparation for a possible future API which allows indexing and slicing of +dividers (possibly ``divider[a:b] == divider.new_locator(a, b)``, but also +``divider[a:] == divider.new_locator(a, )``). The user-facing +`.Divider.new_locator` API is unaffected -- it correctly normalizes ``nx1 = +None`` and ``ny1 = None`` as needed. + + +change signature of ``.FigureCanvasBase.enter_notify_event`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *xy* parameter is now required and keyword only. This was deprecated in +3.0 and originally slated to be removed in 3.5. + +``Colorbar`` tick update parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *update_ticks* parameter of `.Colorbar.set_ticks` and +`.Colorbar.set_ticklabels` was ignored since 3.5 and has been removed. + +plot directive removals +~~~~~~~~~~~~~~~~~~~~~~~ + +The public methods: + +- ``matplotlib.sphinxext.split_code_at_show`` +- ``matplotlib.sphinxext.unescape_doctest`` +- ``matplotlib.sphinxext.run_code`` + +have been removed. + +The deprecated *encoding* option to the plot directive has been removed. + +Miscellaneous removals +~~~~~~~~~~~~~~~~~~~~~~ + +- ``is_url`` and ``URL_REGEX`` are removed. (They were previously defined in + the toplevel :mod:`matplotlib` module.) +- The ``ArrowStyle.beginarrow`` and ``ArrowStyle.endarrow`` attributes are + removed; use the ``arrow`` attribute to define the desired heads and tails + of the arrow. +- ``backend_pgf.LatexManager.str_cache`` is removed. +- ``backends.qt_compat.ETS`` and ``backends.qt_compat.QT_RC_MAJOR_VERSION`` are + removed, with no replacement. +- The ``blocking_input`` module is removed. Instead, use + ``canvas.start_event_loop()`` and ``canvas.stop_event_loop()`` while + connecting event callbacks as needed. +- ``cbook.report_memory`` is removed; use ``psutil.virtual_memory`` instead. +- ``cm.LUTSIZE`` is removed. Use :rc:`image.lut` instead. This value only + affects colormap quantization levels for default colormaps generated at + module import time. +- ``Colorbar.patch`` is removed; this attribute was not correctly updated + anymore. +- ``ContourLabeler.get_label_width`` is removed. +- ``Dvi.baseline`` is removed (with no replacement). +- The *format* parameter of ``dviread.find_tex_file`` is removed (with no + replacement). +- ``FancyArrowPatch.get_path_in_displaycoord`` and + ``ConnectionPatch.get_path_in_displaycoord`` are removed. The path in + display coordinates can still be obtained, as for other patches, using + ``patch.get_transform().transform_path(patch.get_path())``. +- The ``font_manager.win32InstalledFonts`` and + ``font_manager.get_fontconfig_fonts`` helper functions are removed. +- All parameters of ``imshow`` starting from *aspect* are keyword-only. +- ``QuadMesh.convert_mesh_to_paths`` and ``QuadMesh.convert_mesh_to_triangles`` + are removed. ``QuadMesh.get_paths()`` can be used as an alternative for the + former; there is no replacement for the latter. +- ``ScalarMappable.callbacksSM`` is removed. Use + ``ScalarMappable.callbacks`` instead. +- ``streamplot.get_integrator`` is removed. +- ``style.core.STYLE_FILE_PATTERN``, ``style.core.load_base_library``, and + ``style.core.iter_user_libraries`` are removed. +- ``SubplotParams.validate`` is removed. Use `.SubplotParams.update` to + change `.SubplotParams` while always keeping it in a valid state. +- The ``grey_arrayd``, ``font_family``, ``font_families``, and ``font_info`` + attributes of `.TexManager` are removed. +- ``Text.get_prop_tup`` is removed with no replacements (because the `.Text` + class cannot know whether a backend needs to update cache e.g. when the + text's color changes). +- ``Tick.apply_tickdir`` didn't actually update the tick markers on the + existing Line2D objects used to draw the ticks and is removed; use + `.Axis.set_tick_params` instead. +- ``tight_layout.auto_adjust_subplotpars`` is removed. +- The ``grid_info`` attribute of ``axisartist`` classes has been removed. +- ``axes_grid1.axes_grid.CbarAxes`` and ``axisartist.axes_grid.CbarAxes`` are + removed (they are now dynamically generated based on the owning axes + class). +- The ``axes_grid1.Divider.get_vsize_hsize`` and + ``axes_grid1.Grid.get_vsize_hsize`` methods are removed. +- ``AxesDivider.append_axes(..., add_to_figure=False)`` is removed. Use + ``ax.remove()`` to remove the Axes from the figure if needed. +- ``FixedAxisArtistHelper.change_tick_coord`` is removed with no + replacement. +- ``floating_axes.GridHelperCurveLinear.get_boundary`` is removed with no + replacement. +- ``ParasiteAxesBase.get_images_artists`` is removed. +- The "units finalize" signal (previously emitted by Axis instances) is + removed. Connect to "units" instead. +- Passing formatting parameters positionally to ``stem()`` is no longer + possible. +- ``axisartist.clip_path`` is removed with no replacement. + diff --git a/doc/api/prev_api_changes/api_changes_3.8.0.rst b/doc/api/prev_api_changes/api_changes_3.8.0.rst new file mode 100644 index 000000000000..ab1b65c19bab --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.8.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.8.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.8.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.8.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.8.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst new file mode 100644 index 000000000000..8a21d5b4941e --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.0/behaviour.rst @@ -0,0 +1,192 @@ +Behaviour Changes +----------------- + +Tk backend respects file format selection when saving figures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When saving a figure from a Tkinter GUI to a filename without an +extension, the file format is now selected based on the value of +the dropdown menu, rather than defaulting to PNG. When the filename +contains an extension, or the OS automatically appends one, the +behavior remains unchanged. + +Placing of maximum and minimum minor ticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Calculation of minor tick locations has been corrected to make the maximum and +minimum minor ticks more consistent. In some cases this results in an extra +minor tick on an Axis. + +``hexbin`` now defaults to ``rcParams["patch.linewidth"]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default value of the *linewidths* argument of `.Axes.hexbin` has +been changed from ``1.0`` to :rc:`patch.linewidth`. This improves the +consistency with `.QuadMesh` in `.Axes.pcolormesh` and `.Axes.hist2d`. + +TwoSlopeNorm now auto-expands to always have two slopes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In the case where either ``vmin`` or ``vmax`` are not manually specified +to `~.TwoSlopeNorm`, and where the data it is scaling is all less than or +greater than the center point, the limits are now auto-expanded so there +are two symmetrically sized slopes either side of the center point. + +Previously ``vmin`` and ``vmax`` were clipped at the center point, which +caused issues when displaying color bars. + +This does not affect behaviour when ``vmin`` and ``vmax`` are manually +specified by the user. + +Event objects emitted for ``axes_leave_event`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``axes_leave_event`` now emits a synthetic `.LocationEvent`, instead of reusing +the last event object associated with a ``motion_notify_event``. + +Streamplot now draws streamlines as one piece if no width or no color variance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since there is no need to draw streamlines piece by piece if there is no color +change or width change, now streamplot will draw each streamline in one piece. + +The behavior for varying width or varying color is not changed, same logic is +used for these kinds of streamplots. + +``canvas`` argument now required for ``FigureFrameWx`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``FigureFrameWx`` now requires a keyword-only ``canvas`` argument +when it is constructed. + +``ContourSet`` is now a single Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prior to this release, `.ContourSet` (the object returned by `~.Axes.contour`) +was a custom object holding multiple `.Collection`\s (and not an `.Artist`) +-- one collection per level, each connected component of that level's contour +being an entry in the corresponding collection. + +`.ContourSet` is now instead a plain `.Collection` (and thus an `.Artist`). +The collection contains a single path per contour level; this path may be +non-continuous in case there are multiple connected components. + +Setting properties on the ContourSet can now usually be done using standard +collection setters (``cset.set_linewidth(3)`` to use the same linewidth +everywhere or ``cset.set_linewidth([1, 2, 3, ...])`` to set different +linewidths on each level) instead of having to go through the individual +sub-components (``cset.collections[0].set_linewidth(...)``). Note that +during the transition period, it remains possible to access the (deprecated) +``.collections`` attribute; this causes the ContourSet to modify itself to use +the old-style multi-Collection representation. + +``SubFigure`` default facecolor is now transparent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Subfigures default facecolor changed to ``"none"``. Previously the default was +the value of ``figure.facecolor``. + +Reject size related keyword arguments to MovieWriter *grab_frame* method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although we pass `.Figure.savefig` keyword arguments through the +`.AbstractMovieWriter.grab_frame` some of the arguments will result in invalid +output if passed. To be successfully stitched into a movie, each frame +must be exactly the same size, thus *bbox_inches* and *dpi* are excluded. +Additionally, the movie writers are opinionated about the format of each +frame, so the *format* argument is also excluded. Passing these +arguments will now raise `TypeError` for all writers (it already did so for some +arguments and some writers). The *bbox_inches* argument is already ignored (with +a warning) if passed to `.Animation.save`. + + +Additionally, if :rc:`savefig.bbox` is set to ``'tight'``, +`.AbstractMovieWriter.grab_frame` will now error. Previously this rcParam +would be temporarily overridden (with a warning) in `.Animation.save`, it is +now additionally overridden in `.AbstractMovieWriter.saving`. + +Changes of API after deprecation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `.dviread.find_tex_file` now raises `FileNotFoundError` when the requested filename is + not found. +- `.Figure.colorbar` now raises if *cax* is not given and it is unable to determine from + which Axes to steal space, i.e. if *ax* is also not given and *mappable* has not been + added to an Axes. +- `.pyplot.subplot` and `.pyplot.subplot2grid` no longer auto-remove preexisting + overlapping Axes; explicitly call ``Axes.remove`` as needed. + +Invalid types for Annotation xycoords now raise TypeError +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, a `RuntimeError` would be raised in some cases. + +Default antialiasing behavior changes for ``Text`` and ``Annotation`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``matplotlib.pyplot.annotate()`` and ``matplotlib.pyplot.text()`` now support parameter *antialiased* when initializing. +Examples: + +.. code-block:: python + + mpl.text.Text(.5, .5, "foo\nbar", antialiased=True) + plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True) + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), antialiased=False) + +See "What's New" for more details on usage. + +With this new feature, you may want to make sure that you are creating and saving/showing the figure under the same context:: + + # previously this was a no-op, now it is what works + with rccontext(text.antialiased=False): + fig, ax = plt.subplots() + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5)) + fig.savefig('/tmp/test.png') + + # previously this had an effect, now this is a no-op + fig, ax = plt.subplots() + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5)) + with rccontext(text.antialiased=False): + fig.savefig('/tmp/test.png') + +Also note that antialiasing for tick labels will be set with :rc:`text.antialiased` when they are created (usually when a ``Figure`` is created) - This means antialiasing for them can no longer be changed by modifying :rc:`text.antialiased`. + +``ScalarMappable.to_rgba()`` now respects the mask of RGB(A) arrays +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously, the mask was ignored. Now the alpha channel is set to 0 if any +component (R, G, B, or A) is masked. + +``Text.get_rotation_mode`` return value +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing ``None`` as ``rotation_mode`` to `.Text` (the default value) or passing it to +`.Text.set_rotation_mode` will make `.Text.get_rotation_mode` return ``"default"`` +instead of ``None``. The behaviour otherwise is the same. + +PostScript paper size adds option to use figure size +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :rc:`ps.papersize` rcParam can now be set to ``'figure'``, which will use +a paper size that corresponds exactly with the size of the figure that is being +saved. + +``hexbin`` *mincnt* parameter made consistently inclusive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, *mincnt* was inclusive with no *C* provided but exclusive when *C* is provided. +It is now inclusive of *mincnt* in both cases. + + +``matplotlib.mpl_toolkits`` is now an implicit namespace package +---------------------------------------------------------------- + +Following the deprecation of ``pkg_resources.declare_namespace`` in ``setuptools`` 67.3.0, +``matplotlib.mpl_toolkits`` is now implemented as an implicit namespace, following +`PEP 420 `_. + +As a consequence using ``pip`` to install a version of Matplotlib >= 3.8 on top +of a version of Matplotlib < 3.8 (e.g. via ``pip install --local`` or +``python -m venv --system-site-packages ...``) will fail because the old +``matplotlib.mpl_toolkits`` files will be found whereas the newer files will be +found for all other modules. This will result in errors due to the version +mismatch. + +To avoid this issue you need to avoid having multiple versions of Matplotlib +in different entries of ``sys.path``. Either uninstall Matplotlib +at the system level or use a more isolated virtual environment. diff --git a/doc/api/prev_api_changes/api_changes_3.8.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.8.0/deprecations.rst new file mode 100644 index 000000000000..5398cec623b9 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.0/deprecations.rst @@ -0,0 +1,300 @@ +Deprecations +------------ + +Calling ``paths.get_path_collection_extents`` with empty *offsets* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Calling `~.get_path_collection_extents` with an empty *offsets* parameter +has an ambiguous interpretation and is therefore deprecated. When the +deprecation period expires, this will produce an error. + + +``axes_grid1.axes_divider`` API changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``AxesLocator`` class is deprecated. The ``new_locator`` method of divider +instances now instead returns an opaque callable (which can still be passed to +``ax.set_axes_locator``). + +``Divider.locate`` is deprecated; use ``Divider.new_locator(...)(ax, renderer)`` +instead. + + +``bbox.anchored()`` with no explicit container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Not passing a *container* argument to `.BboxBase.anchored` is now deprecated. + + +Functions in ``mpl_toolkits.mplot3d.proj3d`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The function ``transform`` is just an alias for ``proj_transform``, +use the latter instead. + +The following functions are either unused (so no longer required in Matplotlib) +or considered private. If you rely on them, please make a copy of the code, +including all functions that starts with a ``_`` (considered private). + +* ``ortho_transformation`` +* ``persp_transformation`` +* ``proj_points`` +* ``proj_trans_points`` +* ``rot_x`` +* ``rotation_about_vector`` +* ``view_transformation`` + + +Arguments other than ``renderer`` to ``get_tightbbox`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are keyword-only arguments. This is for consistency and that +different classes have different additional arguments. + + +The object returned by ``pcolor()`` has changed to a ``PolyQuadMesh`` class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The old object was a `.PolyCollection` with flattened vertices and array data. +The new `.PolyQuadMesh` class subclasses `.PolyCollection`, but adds in better +2D coordinate and array handling in alignment with `.QuadMesh`. Previously, if +a masked array was input, the list of polygons within the collection would shrink +to the size of valid polygons and users were required to keep track of which +polygons were drawn and call ``set_array()`` with the smaller "compressed" array size. +Passing the "compressed" and flattened array values is now deprecated and the +full 2D array of values (including the mask) should be passed +to `.PolyQuadMesh.set_array`. + + +``LocationEvent.lastevent`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated with no replacement. + + +``allsegs``, ``allkinds``, ``tcolors`` and ``tlinewidths`` attributes of `.ContourSet` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated; if required, directly retrieve the vertices +and codes of the Path objects from ``ContourSet.get_paths()`` and the colors +and the linewidths via ``ContourSet.get_facecolor()``, ``ContourSet.get_edgecolor()`` +and ``ContourSet.get_linewidths()``. + + +``ContourSet.collections`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. `.ContourSet` is now implemented as a single `.Collection` of paths, +each path corresponding to a contour level, possibly including multiple unconnected +components. + +During the deprecation period, accessing ``ContourSet.collections`` will revert the +current ContourSet instance to the old object layout, with a separate `.PathCollection` +per contour level. + + +``INVALID_NON_AFFINE``, ``INVALID_AFFINE``, ``INVALID`` attributes of ``TransformNode`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These attributes are deprecated. + + +``Grouper.clean()`` +~~~~~~~~~~~~~~~~~~~ + +with no replacement. The Grouper class now cleans itself up automatically. + + +``GridHelperCurveLinear.get_data_boundary`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. Use ``grid_finder.extreme_finder(*[None] * 5)`` to get the +extremes of the grid. + + +*np_load* parameter of ``cbook.get_sample_data`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This parameter is deprecated; `.get_sample_data` now auto-loads numpy arrays. +Use ``get_sample_data(..., asfileobj=False)`` instead to get the filename of +the data file, which can then be passed to `open`, if desired. + + +``RendererAgg.tostring_rgb`` and ``FigureCanvasAgg.tostring_rgb`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated with no direct replacement. Consider using ``buffer_rgba`` +instead, which should cover most use cases. + + +The parameter of ``Annotation.contains`` and ``Legend.contains`` is renamed to *mouseevent* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... consistently with `.Artist.contains`. + + +Accessing ``event.guiEvent`` after event handlers return +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated: for some GUI toolkits, it is unsafe to do so. In the +future, ``event.guiEvent`` will be set to None once the event handlers return; +you may separately stash the object at your own risk. + + +Widgets +~~~~~~~ + +The *visible* attribute getter of Selector widgets has been deprecated; +use ``get_visible`` + + +Method parameters renamed to match base classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The only parameter of ``transform_affine`` and ``transform_non_affine`` in ``Transform`` subclasses is renamed +to *values*. + +The *points* parameter of ``transforms.IdentityTransform.transform`` is renamed to *values*. + +The *trans* parameter of ``table.Cell.set_transform`` is renamed to *t* consistently with +`.Artist.set_transform`. + +The *clippath* parameters of ``axis.Axis.set_clip_path`` and ``axis.Tick.set_clip_path`` are +renamed to *path* consistently with `.Artist.set_clip_path`. + +The *s* parameter of ``images.NonUniformImage.set_filternorm`` is renamed to *filternorm* +consistently with ``_ImageBase.set_filternorm``. + +The *s* parameter of ``images.NonUniformImage.set_filterrad`` is renamed to *filterrad* +consistently with ``_ImageBase.set_filterrad``. + + +*numdecs* parameter and attribute of ``LogLocator`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated without replacement, because they have no effect. + + +``NavigationToolbar2QT.message`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... with no replacement. + + +``ft2font.FT2Image.draw_rect`` and ``ft2font.FT2Font.get_xys`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... are deprecated as they are unused. If you rely on these, please let us know. + + +``backend_ps.psDefs`` +~~~~~~~~~~~~~~~~~~~~~ + +The ``psDefs`` module-level variable in ``backend_ps`` is deprecated with no +replacement. + + +Callable axisartist Axes +~~~~~~~~~~~~~~~~~~~~~~~~ +Calling an axisartist Axes to mean `~matplotlib.pyplot.axis` is deprecated; explicitly +call the method instead. + + +``AnchoredEllipse`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Instead, directly construct an `.AnchoredOffsetbox`, an `.AuxTransformBox`, and an +`~.patches.Ellipse`, as demonstrated in :doc:`/gallery/misc/anchored_artists`. + + +Automatic papersize selection in PostScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting :rc:`ps.papersize` to ``'auto'`` or passing ``papersize='auto'`` to +`.Figure.savefig` is deprecated. Either pass an explicit paper type name, or +omit this parameter to use the default from the rcParam. + + +``Tick.set_label1`` and ``Tick.set_label2`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... are deprecated. Calling these methods from third-party code usually has no +effect, as the labels are overwritten at draw time by the tick formatter. + + +Passing extra positional arguments to ``Figure.add_axes`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional arguments passed to `.Figure.add_axes` other than a rect or an +existing ``Axes`` are currently ignored, and doing so is now deprecated. + + +``CbarAxesBase.toggle_label`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. Instead, use standard methods for manipulating colorbar +labels (`.Colorbar.set_label`) and tick labels (`.Axes.tick_params`). + + +``TexManager.texcache`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is considered private and deprecated. The location of the cache directory is +clarified in the doc-string. + + +Artists explicitly passed in will no longer be filtered by legend() based on their label +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Currently, artists explicitly passed to ``legend(handles=[...])`` are filtered +out if their label starts with an underscore. This behavior is deprecated; +explicitly filter out such artists +(``[art for art in artists if not art.get_label().startswith('_')]``) if +necessary. + + +``FigureCanvasBase.switch_backends`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated with no replacement. + + +``cbook.Stack`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... with no replacement. + + +``inset_location.InsetPosition`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use `~.Axes.inset_axes` instead. + + +``axisartist.axes_grid`` and ``axisartist.axes_rgb`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These modules, which provide wrappers combining the functionality of +`.axes_grid1` and `.axisartist`, are deprecated; directly use e.g. +``AxesGrid(..., axes_class=axislines.Axes)`` instead. + + +``ContourSet.antialiased`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated; use `~.Collection.get_antialiased` or +`~.Collection.set_antialiased` instead. Note that `~.Collection.get_antialiased` +returns an array. + + +Passing non-int or sequence of non-int to ``Table.auto_set_column_width`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Column numbers are ints, and formerly passing any other type was effectively +ignored. This will become an error in the future. + + +``PdfPages(keep_empty=True)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A zero-page pdf is not valid, thus passing ``keep_empty=True`` to +`.backend_pdf.PdfPages` and `.backend_pgf.PdfPages`, and the ``keep_empty`` +attribute of these classes, are deprecated. Currently, these classes default +to keeping empty outputs, but that behavior is deprecated too. Explicitly +passing ``keep_empty=False`` remains supported for now to help transition to +the new behavior. + +Furthermore, `.backend_pdf.PdfPages` no longer immediately creates the target +file upon instantiation, but only when the first figure is saved. To fully +control file creation, directly pass an opened file object as argument +(``with open(path, "wb") as file, PdfPages(file) as pdf: ...``). + + +Auto-closing of figures when switching backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. Explicitly call ``plt.close("all")`` if necessary. In the +future, allowable backend switches (i.e. those that do not swap a GUI event +loop with another one) will not close existing figures. + + +Support for passing the "frac" key in ``annotate(..., arrowprops={"frac": ...})`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... has been removed. This key has had no effect since Matplotlib 1.5. diff --git a/doc/api/prev_api_changes/api_changes_3.8.0/development.rst b/doc/api/prev_api_changes/api_changes_3.8.0/development.rst new file mode 100644 index 000000000000..2be0505f38ea --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.0/development.rst @@ -0,0 +1,79 @@ +Development changes +------------------- + + +Increase to minimum supported versions of dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.8, the :ref:`minimum supported versions ` are +being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.7 | min in mpl3.8 | ++============+=================+===============+ +| Python | 3.8 | 3.9 | ++------------+-----------------+---------------+ +| kiwisolver | 1.0.1 | 1.3.1 | ++------------+-----------------+---------------+ +| NumPy | 1.20.0 | 1.21.0 | ++------------+-----------------+---------------+ +| Pillow | 6.2.1 | 8.0 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `NEP29 +`__ + + +Increase to minimum supported optional dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.8, the :ref:`minimum supported versions of optional dependencies +` are being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.7 | min in mpl3.8 | ++============+=================+===============+ +| Tk | 8.4 | 8.5 | ++------------+-----------------+---------------+ +| Qt | 5.10 | 5.12 | ++------------+-----------------+---------------+ + +- There are no wheels or conda packages that support both Qt 5.11 (or older) and + Python 3.9 (or newer). + +This is consistent with our :ref:`min_deps_policy` + +Provisional support for PEP484 Type Hint Annotations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New public API should be type hinted in ``.pyi`` stub files (except ``pyplot`` and tests +which are typed in-line). +Tests should be type hinted minimally, essentially only when ``mypy`` generates errors. + +CI and configuration for running ``mypy`` have been added. + +Generation of ``pyplot.py`` requires ``black`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The autogenerated portions of ``pyplot.py`` use ``black`` autoformatting to ensure +syntax-correct, readable output code. + +As such ``black`` is now a development and test requirement (for the test which +regenerates ``pyplot``). + +Wheels for some systems are no longer distributed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pre-compiled wheels for 32-bit Linux and Windows are no longer provided on PyPI +since Matplotlib 3.8. + +Multi-architecture ``universal2`` wheels for macOS are no longer provided on PyPI since +Matplotlib 3.8. In general, ``pip`` will always prefer the architecture-specific +(``amd64``- or ``arm64``-only) wheels, so these provided little benefit. + +New wheel architectures +~~~~~~~~~~~~~~~~~~~~~~~ + +Wheels have been added for: + +- musl based systems diff --git a/doc/api/prev_api_changes/api_changes_3.8.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.8.0/removals.rst new file mode 100644 index 000000000000..90e5fd882486 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.0/removals.rst @@ -0,0 +1,287 @@ +Removals +-------- + +cbook removals +~~~~~~~~~~~~~~ + +- ``matplotlib.cbook.MatplotlibDeprecationWarning`` and + ``matplotlib.cbook.mplDeprecation`` are removed; use + `matplotlib.MatplotlibDeprecationWarning` instead. +- ``cbook.maxdict``; use the standard library ``functools.lru_cache`` instead. + +Groupers from ``get_shared_x_axes`` / ``get_shared_y_axes`` are immutable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Modifications to the Groupers returned by ``get_shared_x_axes`` and +``get_shared_y_axes`` are no longer allowed. Note that previously, calling e.g. +``join()`` would already fail to set up the correct structures for sharing +axes; use `.Axes.sharex` or `.Axes.sharey` instead. + +Deprecated modules removed +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following deprecated modules are removed: + +* ``afm`` +* ``docstring`` +* ``fontconfig_pattern`` +* ``tight_bbox`` +* ``tight_layout`` +* ``type1font`` + +Parameters to ``plt.figure()`` and the ``Figure`` constructor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All parameters to `.pyplot.figure` and the `.Figure` constructor, other than +*num*, *figsize*, and *dpi*, are now keyword-only. + +``stem(..., use_line_collection=False)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is no longer supported. This was a compatibility fallback to a +former more inefficient representation of the stem lines. + +Positional / keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing all but the very few first arguments positionally in the constructors +of Artists is no longer possible. Most arguments are now keyword-only. + +The *emit* and *auto* parameters of ``set_xlim``, ``set_ylim``, +``set_zlim``, ``set_rlim`` are now keyword-only. + +The *transOffset* parameter of `.Collection.set_offset_transform` and the +various ``create_collection`` methods of legend handlers has been renamed to +*offset_transform* (consistently with the property name). + +``Axes.get_window_extent`` / ``Figure.get_window_extent`` accept only +*renderer*. This aligns the API with the general `.Artist.get_window_extent` +API. All other parameters were ignored anyway. + +Methods to set parameters in ``LogLocator`` and ``LogFormatter*`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In `~.LogFormatter` and derived subclasses, the methods ``base`` and +``label_minor`` for setting the respective parameter are removed and +replaced by ``set_base`` and ``set_label_minor``, respectively. + +In `~.LogLocator`, the methods ``base`` and ``subs`` for setting the respective +parameter are removed. Instead, use ``set_params(base=..., subs=...)``. + +``Axes.get_renderer_cache`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The canvas now takes care of the renderer and whether to cache it or not, +so the ``Axes.get_renderer_cache`` method is removed. The +alternative is to call ``axes.figure.canvas.get_renderer()``. + +Unused methods in ``Axis``, ``Tick``, ``XAxis``, and ``YAxis`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Tick.label`` is now removed. Use ``Tick.label1`` instead. + +The following methods are no longer used and removed without a replacement: + +- ``Axis.get_ticklabel_extents`` +- ``Tick.get_pad_pixels`` +- ``XAxis.get_text_heights`` +- ``YAxis.get_text_widths`` + +``mlab.stride_windows`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed. Use ``numpy.lib.stride_tricks.sliding_window_view`` instead. + +``Axes3D`` +~~~~~~~~~~ + +The ``dist`` attribute has been privatized. Use the *zoom* keyword argument in +`.Axes3D.set_box_aspect` instead. + +The ``w_xaxis``, ``w_yaxis``, and ``w_zaxis`` attributes are now removed. +Instead use ``xaxis``, ``yaxis``, and ``zaxis``. + +3D Axis +~~~~~~~ + +``mplot3d.axis3d.Axis.set_pane_pos`` is removed. This is an internal method +where the provided values are overwritten during drawing. Hence, it does not +serve any purpose to be directly accessible. + +The two helper functions ``mplot3d.axis3d.move_from_center`` and +``mplot3d.axis3d.tick_update_position`` are considered internal and deprecated. +If these are required, please vendor the code from the corresponding private +methods ``_move_from_center`` and ``_tick_update_position``. + +``checkdep_usetex`` removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This method was only intended to disable tests in case no latex install was +found. As such, it is considered to be private and for internal use only. + +Please vendor the code from a previous version if you need this. + +``date_ticker_factory`` removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``date_ticker_factory`` method in the `matplotlib.dates` module is +removed. Instead use `~.AutoDateLocator` and `~.AutoDateFormatter` for a +more flexible and scalable locator and formatter. + +If you need the exact ``date_ticker_factory`` behavior, please copy the code +from a previous version. + +``transforms.Affine2D.identity()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed in favor of directly calling the `.Affine2D` constructor with +no arguments. + +Removals in ``testing.decorators`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The unused class ``CleanupTestCase`` and decorator ``cleanup`` are removed. +The function ``check_freetype_version`` is considered internal and removed. +Vendor the code from a previous version. + +``text.get_rotation()`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed with no replacement. Copy the previous implementation if +needed. +``Figure.callbacks`` is removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Figure ``callbacks`` property has been removed. The only signal was +"dpi_changed", which can be replaced by connecting to the "resize_event" on the +canvas ``figure.canvas.mpl_connect("resize_event", func)`` instead. + + +Passing too many positional arguments to ``tripcolor`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... raises ``TypeError`` (extra arguments were previously ignored). + + +The *filled* argument to ``Colorbar`` is removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This behavior was already governed by the underlying ``ScalarMappable``. + + +Widgets +~~~~~~~ + +The *visible* attribute setter of Selector widgets has been removed; use ``set_visible`` +The associated getter is also deprecated, but not yet expired. + +``Axes3D.set_frame_on`` and ``Axes3D.get_frame_on`` removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Axes3D.set_frame_on`` is documented as "Set whether the 3D axes panels are +drawn.". However, it has no effect on 3D axes and is being removed in +favor of ``Axes3D.set_axis_on`` and ``Axes3D.set_axis_off``. + +Miscellaneous internals +~~~~~~~~~~~~~~~~~~~~~~~ + +- ``axes_grid1.axes_size.AddList``; use ``sum(sizes, start=Fixed(0))`` (for + example) to sum multiple size objects. +- ``axes_size.Padded``; use ``size + pad`` instead +- ``axes_size.SizeFromFunc``, ``axes_size.GetExtentHelper`` +- ``AxisArtistHelper.delta1`` and ``AxisArtistHelper.delta2`` +- ``axislines.GridHelperBase.new_gridlines`` and + ``axislines.Axes.new_gridlines`` +- ``_DummyAxis.dataLim`` and ``_DummyAxis.viewLim``; use + ``get_data_interval()``, ``set_data_interval()``, ``get_view_interval()``, + and ``set_view_interval()`` instead. +- ``ImageMagickBase.delay`` and ``ImageMagickBase.output_args`` +- ``MathtextBackend``, ``MathtextBackendAgg``, ``MathtextBackendPath``, + ``MathTextWarning`` +- ``TexManager.get_font_config``; it previously returned an internal hashed key + for used for caching purposes. +- ``TextToPath.get_texmanager``; directly construct a `.texmanager.TexManager` + instead. +- ``ticker.is_close_to_int``; use ``math.isclose(x, round(x))`` instead. +- ``ticker.is_decade``; use ``y = numpy.log(x)/numpy.log(base); + numpy.isclose(y, numpy.round(y))`` instead. + + +Backend-specific removals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``backend_pdf.Name.hexify`` +- ``backend_pdf.Operator`` and ``backend_pdf.Op.op`` are removed in favor of + a single standard `enum.Enum` interface on `.backend_pdf.Op`. +- ``backend_pdf.fill``; vendor the code of the similarly named private + functions if you rely on these functions. +- ``backend_pgf.LatexManager.texcommand`` and + ``backend_pgf.LatexManager.latex_header`` +- ``backend_pgf.NO_ESCAPE`` +- ``backend_pgf.common_texification`` +- ``backend_pgf.get_fontspec`` +- ``backend_pgf.get_preamble`` +- ``backend_pgf.re_mathsep`` +- ``backend_pgf.writeln`` +- ``backend_ps.convert_psfrags`` +- ``backend_ps.quote_ps_string``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.escape_attrib``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_cdata``; vendor the code of the similarly named private + functions if you rely on it. +- ``backend_svg.escape_comment``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.short_float_fmt``; vendor the code of the similarly named + private functions if you rely on it. +- ``backend_svg.generate_transform`` and ``backend_svg.generate_css`` + +Removal of deprecated APIs +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following deprecated APIs have been removed. Unless a replacement is stated, please +vendor the previous implementation if needed. + +- The following methods of `.FigureCanvasBase`: ``pick`` (use ``Figure.pick`` instead), + ``resize``, ``draw_event``, ``resize_event``, ``close_event``, ``key_press_event``, + ``key_release_event``, ``pick_event``, ``scroll_event``, ``button_press_event``, + ``button_release_event``, ``motion_notify_event``, ``leave_notify_event``, + ``enter_notify_event`` (for all the ``foo_event`` methods, construct the relevant + `.Event` object and call ``canvas.callbacks.process(event.name, event)`` instead). +- ``ToolBase.destroy`` (connect to ``tool_removed_event`` instead). +- The *cleared* parameter to `.FigureCanvasAgg.get_renderer` (call ``renderer.clear()`` + instead). +- The following methods of `.RendererCairo`: ``set_ctx_from_surface`` and + ``set_width_height`` (use ``set_context`` instead, which automatically infers the + canvas size). +- The ``window`` or ``win`` parameters and/or attributes of ``NavigationToolbar2Tk``, + ``NavigationToolbar2GTK3``, and ``NavigationToolbar2GTK4``, and the ``lastrect`` + attribute of ``NavigationToolbar2Tk`` +- The ``error_msg_gtk`` function and the ``icon_filename`` and ``window_icon`` globals + in ``backend_gtk3``; the ``error_msg_wx`` function in ``backend_wx``. +- ``FigureManagerGTK3Agg`` and ``FigureManagerGTK4Agg`` (use ``FigureManagerGTK3`` + instead); ``RendererGTK3Cairo`` and ``RendererGTK4Cairo``. +- ``NavigationToolbar2Mac.prepare_configure_subplots`` (use + `~.NavigationToolbar2.configure_subplots` instead). +- ``FigureManagerMac.close``. +- The ``qApp`` global in `.backend_qt` (use ``QtWidgets.QApplication.instance()`` + instead). +- The ``offset_text_height`` method of ``RendererWx``; the ``sizer``, ``figmgr``, + ``num``, ``toolbar``, ``toolmanager``, ``get_canvas``, and ``get_figure_manager`` + attributes or methods of ``FigureFrameWx`` (use ``frame.GetSizer()``, + ``frame.canvas.manager``, ``frame.canvas.manager.num``, ``frame.GetToolBar()``, + ``frame.canvas.manager.toolmanager``, the *canvas_class* constructor parameter, and + ``frame.canvas.manager``, respectively, instead). +- ``FigureFrameWxAgg`` and ``FigureFrameWxCairo`` (use + ``FigureFrameWx(..., canvas_class=FigureCanvasWxAgg)`` and + ``FigureFrameWx(..., canvas_class=FigureCanvasWxCairo)``, respectively, instead). +- The ``filled`` attribute and the ``draw_all`` method of `.Colorbar` (instead of + ``draw_all``, use ``figure.draw_without_rendering``). +- Calling `.MarkerStyle` without setting the *marker* parameter or setting it to None + (use ``MarkerStyle("")`` instead). +- Support for third-party canvas classes without a ``required_interactive_framework`` + attribute (this can only occur if the canvas class does not inherit from + `.FigureCanvasBase`). +- The ``canvas`` and ``background`` attributes of `.MultiCursor`; the + ``state_modifier_keys`` attribute of selector widgets. +- Passing *useblit*, *horizOn*, or *vertOn* positionally to `.MultiCursor`. +- Support for the ``seaborn-`` styles; use ``seaborn-v0_8-`` instead, or + directly use the seaborn API. diff --git a/doc/api/prev_api_changes/api_changes_3.8.1.rst b/doc/api/prev_api_changes/api_changes_3.8.1.rst new file mode 100644 index 000000000000..9c40167ebdcc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.8.1.rst @@ -0,0 +1,33 @@ +API Changes for 3.8.1 +===================== + +Behaviour +--------- + +Default behaviour of ``hexbin`` with *C* provided requires at least 1 point +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The behaviour changed in 3.8.0 to be inclusive of *mincnt*. However, that resulted in +errors or warnings with some reduction functions, so now the default is to require at +least 1 point to call the reduction function. This effectively restores the default +behaviour to match that of Matplotlib 3.7 and before. + + +Deprecations +------------ + +Deprecations removed in ``contour`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``contour.allsegs``, ``contour.allkinds``, and ``contour.find_nearest_contour`` are no +longer marked for deprecation. + + +Development +----------- + +Minimum version of setuptools bumped to 64 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To comply with requirements of ``setuptools_scm``, the minimum version of ``setuptools`` +has been increased from 42 to 64. diff --git a/doc/api/prev_api_changes/api_changes_3.9.0.rst b/doc/api/prev_api_changes/api_changes_3.9.0.rst new file mode 100644 index 000000000000..8bd2628c90dc --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0.rst @@ -0,0 +1,14 @@ +API Changes for 3.9.0 +===================== + +.. contents:: + :local: + :depth: 1 + +.. include:: /api/prev_api_changes/api_changes_3.9.0/behaviour.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/deprecations.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/removals.rst + +.. include:: /api/prev_api_changes/api_changes_3.9.0/development.rst diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst new file mode 100644 index 000000000000..498dfb766922 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/behaviour.rst @@ -0,0 +1,120 @@ +Behaviour Changes +----------------- + +plot() shorthand format interprets "Cn" (n>9) as a color-cycle color +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, ``plot(..., "-C11")`` would be interpreted as requesting a plot using +linestyle "-", color "C1" (color #1 of the color cycle), and marker "1" ("tri-down"). +It is now interpreted as requesting linestyle "-" and color "C11" (color #11 of the +color cycle). + +It is recommended to pass ambiguous markers (such as "1") explicitly using the *marker* +keyword argument. If the shorthand form is desired, such markers can also be +unambiguously set by putting them *before* the color string. + +Legend labels for ``plot`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. Now, if the sequence has only one element, that element will be the legend label. +To keep the old behavior, cast the sequence to string before passing. + +Boxplots now ignore masked data points +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`~matplotlib.axes.Axes.boxplot` and `~matplotlib.cbook.boxplot_stats` now ignore any +masked points in the input data. + +``axhspan`` and ``axvspan`` now return ``Rectangle``\s, not ``Polygon``\s +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This change allows using `~.Axes.axhspan` to draw an annulus on polar axes. + +This change also affects other elements built via `~.Axes.axhspan` and `~.Axes.axvspan`, +such as ``Slider.poly``. + +Improved handling of pan/zoom events of overlapping Axes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The forwarding of pan/zoom events is now determined by the visibility of the +background-patch (e.g. ``ax.patch.get_visible()``) and by the ``zorder`` of the axes. + +- Axes with a visible patch capture the event and do not pass it on to axes below. Only + the Axes with the highest ``zorder`` that contains the event is triggered (if there + are multiple Axes with the same ``zorder``, the last added Axes counts) +- Axes with an invisible patch are also invisible to events and they are passed on to + the axes below. + +To override the default behavior and explicitly set whether an Axes should forward +navigation events, use `.Axes.set_forward_navigation_events`. + +``loc='best'`` for ``legend`` now considers ``Text`` and ``PolyCollections`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The location selection ``legend`` now considers the existence of ``Text`` and +``PolyCollections`` in the ``badness`` calculation. + +Note: The ``best`` option can already be quite slow for plots with large amounts of +data. For ``PolyCollections``, it only considers the ``Path`` of ``PolyCollections`` and +not the enclosed area when checking for overlap to reduce additional latency. However, +it can still be quite slow when there are large amounts of ``PolyCollections`` in the +plot to check for. + +Exception when not passing a Bbox to BboxTransform*-classes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The exception when not passing a Bbox to BboxTransform*-classes that expect one, e.g., +`~matplotlib.transforms.BboxTransform` has changed from ``ValueError`` to ``TypeError``. + +*loc* parameter of ``Cell`` no longer accepts ``None`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default value of the *loc* parameter has been changed from ``None`` to ``right``, +which already was the default location. The behavior of `.Cell` didn't change when +called without an explicit *loc* parameter. + +``ContourLabeler.add_label`` now respects *use_clabeltext* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... and sets `.Text.set_transform_rotates_text` accordingly. + +``Line2D`` +^^^^^^^^^^ + +When creating a Line2D or using `.Line2D.set_xdata` and `.Line2D.set_ydata`, +passing x/y data as non sequence is now an error. + +``ScalarMappable``\s auto-scale their norm when an array is set +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Collections previously deferred auto-scaling of the norm until draw time. This has been +changed to scale the norm whenever the first array is set to align with the docstring +and reduce unexpected behavior when accessing the norm before drawing. + +``SubplotParams`` moved from ``matplotlib.figure`` to ``matplotlib.gridspec`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is still importable from ``matplotlib.figure``, so does not require any changes to +existing code. + +``PowerNorm`` no longer clips values below vmin +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When ``clip=False`` is set (the default) on `~matplotlib.colors.PowerNorm`, values below +``vmin`` are now linearly normalised. Previously they were clipped to zero. This fixes +issues with the display of colorbars associated with a power norm. + +Image path semantics of toolmanager-based tools +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, MEP22 ("toolmanager-based") Tools would try to load their icon +(``tool.image``) relative to the current working directory, or, as a fallback, from +Matplotlib's own image directory. Because both approaches are problematic for +third-party tools (the end-user may change the current working directory at any time, +and third-parties cannot add new icons in Matplotlib's image directory), this behavior +is deprecated; instead, ``tool.image`` is now interpreted relative to the directory +containing the source file where the ``Tool.image`` class attribute is defined. +(Defining ``tool.image`` as an absolute path also works and is compatible with both the +old and the new semantics.) diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst new file mode 100644 index 000000000000..2cf1df8c9579 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/deprecations.rst @@ -0,0 +1,93 @@ +Deprecations +------------ + +``plot_date`` +^^^^^^^^^^^^^ + +Use of ``plot_date`` has been discouraged since Matplotlib 3.5 and the function is +now formally deprecated. + +- ``datetime``-like data should directly be plotted using `~.Axes.plot`. +- If you need to plot plain numeric data as :ref:`date-format` or need to set a + timezone, call ``ax.xaxis.axis_date`` / ``ax.yaxis.axis_date`` before `~.Axes.plot`. + See `.Axis.axis_date`. + +Legend labels for ``plot`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. This behavior is now deprecated and in future will error if the sequence length +is not one (consistent with multi-dataset behavior, where the number of elements must +match the number of datasets). To keep the old behavior, cast the sequence to string +before passing. + +``boxplot`` tick labels +^^^^^^^^^^^^^^^^^^^^^^^ + +The parameter *labels* has been renamed to *tick_labels* for clarity and consistency +with `~.Axes.bar`. + +Mixing positional and keyword arguments for ``legend`` handles and labels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This previously only raised a warning, but is now formally deprecated. If passing +*handles* and *labels*, they must be passed either both positionally or both as keyword. + +Applying theta transforms in ``PolarTransform`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Applying theta transforms in `~matplotlib.projections.polar.PolarTransform` and +`~matplotlib.projections.polar.InvertedPolarTransform` is deprecated, and will be +removed in a future version of Matplotlib. This is currently the default behaviour when +these transforms are used externally, but only takes affect when: + +- An axis is associated with the transform. +- The axis has a non-zero theta offset or has theta values increasing in a clockwise + direction. + +To silence this warning and adopt future behaviour, set +``apply_theta_transforms=False``. If you need to retain the behaviour where theta values +are transformed, chain the ``PolarTransform`` with a `~matplotlib.transforms.Affine2D` +transform that performs the theta shift and/or sign shift. + +*interval* parameter of ``TimerBase.start`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Setting the timer *interval* while starting it is deprecated. The interval can be +specified instead in the timer constructor, or by setting the ``timer.interval`` +attribute. + +*nth_coord* parameter to axisartist helpers for fixed axis +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Helper APIs in `.axisartist` for generating a "fixed" axis on rectilinear axes +(`.FixedAxisArtistHelperRectilinear`) no longer take a *nth_coord* parameter, as that +parameter is entirely inferred from the (required) *loc* parameter and having +inconsistent *nth_coord* and *loc* is an error. + +For curvilinear axes, the *nth_coord* parameter remains supported (it affects the +*ticks*, not the axis position itself), but that parameter will become keyword-only, for +consistency with the rectilinear case. + +``rcsetup.interactive_bk``, ``rcsetup.non_interactive_bk`` and ``rcsetup.all_backends`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... are deprecated and replaced by ``matplotlib.backends.backend_registry.list_builtin`` +with the following arguments + +- ``matplotlib.backends.BackendFilter.INTERACTIVE`` +- ``matplotlib.backends.BackendFilter.NON_INTERACTIVE`` +- ``None`` + +respectively. + +Miscellaneous deprecations +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``backend_ps.get_bbox_header`` is considered an internal helper +- ``BboxTransformToMaxOnly``; if you rely on this, please make a copy of the code +- ``ContourLabeler.add_label_clabeltext`` +- ``TransformNode.is_bbox``; instead check the object using ``isinstance(..., + BboxBase)`` +- ``GridHelperCurveLinear.get_tick_iterator`` diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/development.rst b/doc/api/prev_api_changes/api_changes_3.9.0/development.rst new file mode 100644 index 000000000000..c16e8e98ecc4 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/development.rst @@ -0,0 +1,84 @@ +Development changes +------------------- + +Build system ported to Meson +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The build system of Matplotlib has been ported from setuptools to `meson-python +`_ and `Meson `_. +Consequently, there have been a few changes for development and packaging purposes. + +1. Installation by ``pip`` of packages with ``pyproject.toml`` use `build isolation + `_ + by default, which interferes with editable installation. Thus for developers using + editable installs, it is now necessary to pass the ``--no-build-isolation`` flag to + ``pip install``. This means that all build-time requirements must be available in the + environment for an editable install. +2. Build configuration has moved from a custom :file:`mplsetup.cfg` (also configurable + via ``MPLSETUP`` environment variable) to Meson options. These may be specified using + `meson-python's build config settings + `_ + for ``setup-args``. See :file:`meson_options.txt` for all options. For example, a + :file:`mplsetup.cfg` containing the following:: + + [rc_options] + backend=Agg + + [libs] + system_qhull = True + + may be replaced by passing the following arguments to ``pip``:: + + --config-settings=setup-args="-DrcParams-backend=Agg" + --config-settings=setup-args="-Dsystem-qhull=true" + + Note that you must use ``pip`` >= 23.1 in order to pass more than one setting. +3. Relatedly, Meson's `builtin options `_ + are now used instead of custom options, e.g., the LTO option is now ``b_lto``. +4. On Windows, Meson activates a Visual Studio environment automatically. However, it + will not do so if another compiler is available. See `Meson's documentation + `_ if you wish to + change the priority of chosen compilers. +5. Installation of test data was previously controlled by :file:`mplsetup.cfg`, but has + now been moved to Meson's install tags. To install test data, add the ``tests`` tag + to the requested install (be sure to include the existing tags as below):: + + --config-settings=install-args="--tags=data,python-runtime,runtime,tests" +6. Checking typing stubs with ``stubtest`` does not work easily with editable install. + For the time being, we suggest using a normal (non-editable) install if you wish to + run ``stubtest``. + +Increase to minimum supported versions of dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For Matplotlib 3.9, the :ref:`minimum supported versions ` are being +bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.8 | min in mpl3.9 | ++============+=================+===============+ +| NumPy | 1.21.0 | 1.23.0 | ++------------+-----------------+---------------+ +| setuptools | 42 | 64 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` and `SPEC 0 +`__. + +To comply with requirements of ``setuptools_scm``, the minimum version of ``setuptools`` +has been increased from 42 to 64. + +Extensions require C++17 +^^^^^^^^^^^^^^^^^^^^^^^^ + +Matplotlib now requires a compiler that supports C++17 in order to build its extensions. +According to `SciPy's analysis +`_, this +should be available on all supported platforms. + +Windows on ARM64 support +^^^^^^^^^^^^^^^^^^^^^^^^ + +Windows on ARM64 now bundles FreeType 2.6.1 instead of 2.11.1 when building from source. +This may cause small changes to text rendering, but should become consistent with all +other platforms. diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst new file mode 100644 index 000000000000..791c04149981 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst @@ -0,0 +1,159 @@ +Removals +-------- + +Top-level cmap registration and access functions in ``mpl.cm`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As part of the `multi-step refactoring of colormap registration +`_, the following functions have +been removed: + +- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you have a + `str`. + + Use `matplotlib.cm.ColormapRegistry.get_cmap` if you have a `str`, `None` or a + `matplotlib.colors.Colormap` object that you want to convert to a `.Colormap` object. +- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead. +- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister + <.ColormapRegistry.unregister>` instead. +- ``matplotlib.pyplot.register_cmap``; use `matplotlib.colormaps.register + <.ColormapRegistry.register>` instead. + +The `matplotlib.pyplot.get_cmap` function will stay available for backward +compatibility. + +Contour labels +^^^^^^^^^^^^^^ + +``contour.ClabelText`` and ``ContourLabeler.set_label_props`` are removed. Use +``Text(..., transform_rotates_text=True)`` as a replacement for +``contour.ClabelText(...)`` and ``text.set(text=text, color=color, +fontproperties=labeler.labelFontProps, clip_box=labeler.axes.bbox)`` as a replacement +for the ``ContourLabeler.set_label_props(label, text, color)``. + +The ``labelFontProps``, ``labelFontSizeList``, and ``labelTextsList`` attributes of +`.ContourLabeler` have been removed. Use the ``labelTexts`` attribute and the font +properties of the corresponding text objects instead. + +``num2julian``, ``julian2num`` and ``JULIAN_OFFSET`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... of the `.dates` module are removed without replacements. These were undocumented and +not exported. + +Julian dates in Matplotlib were calculated from a Julian date epoch: ``jdate = (date - +np.datetime64(EPOCH)) / np.timedelta64(1, 'D')``. Conversely, a Julian date was +converted to datetime as ``date = np.timedelta64(int(jdate * 24 * 3600), 's') + +np.datetime64(EPOCH)``. Matplotlib was using ``EPOCH='-4713-11-24T12:00'`` so that +2000-01-01 at 12:00 is 2_451_545.0 (see https://en.wikipedia.org/wiki/Julian_day). + +``offsetbox`` methods +^^^^^^^^^^^^^^^^^^^^^ + +``offsetbox.bbox_artist`` is removed. This was just a wrapper to call +`.patches.bbox_artist` if a flag is set in the file, so use that directly if you need +the behavior. + +``OffsetBox.get_extent_offsets`` and ``OffsetBox.get_extent`` are removed; these methods +are also removed on all subclasses of `.OffsetBox`. To get the offsetbox extents, +instead of ``get_extent``, use `.OffsetBox.get_bbox`, which directly returns a `.Bbox` +instance. To also get the child offsets, instead of ``get_extent_offsets``, separately +call `~.OffsetBox.get_offset` on each children after triggering a draw. + +``parse_fontconfig_pattern`` raises on unknown constant names +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, in a fontconfig pattern like ``DejaVu Sans:foo``, the unknown ``foo`` +constant name would be silently ignored. This now raises an error. + +``tri`` submodules +^^^^^^^^^^^^^^^^^^ + +The ``matplotlib.tri.*`` submodules are removed. All functionality is available in +``matplotlib.tri`` directly and should be imported from there. + +Widget API +^^^^^^^^^^ + +- ``CheckButtons.rectangles`` and ``CheckButtons.lines`` are removed; `.CheckButtons` + now draws itself using `~.Axes.scatter`. +- ``RadioButtons.circles`` is removed; `.RadioButtons` now draws itself using + `~.Axes.scatter`. +- ``MultiCursor.needclear`` is removed with no replacement. +- The unused parameter *x* to ``TextBox.begin_typing`` was a required argument, and is + now removed. + +Most arguments to widgets have been made keyword-only +""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Passing all but the very few first arguments positionally in the constructors of Widgets +is now keyword-only. In general, all optional arguments are keyword-only. + +``Axes3D`` API +^^^^^^^^^^^^^^ + +- ``Axes3D.unit_cube``, ``Axes3D.tunit_cube``, and ``Axes3D.tunit_edges`` are removed + without replacement. +- ``axes3d.vvec``, ``axes3d.eye``, ``axes3d.sx``, and ``axes3d.sy`` are removed without + replacement. + +Inconsistent *nth_coord* and *loc* passed to ``_FixedAxisArtistHelperBase`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The value of the *nth_coord* parameter of ``_FixedAxisArtistHelperBase`` and its +subclasses is now inferred from the value of *loc*; passing inconsistent values (e.g., +requesting a "top y axis" or a "left x axis") has no more effect. + +Passing undefined *label_mode* to ``Grid`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is no longer allowed. This includes `mpl_toolkits.axes_grid1.axes_grid.Grid`, +`mpl_toolkits.axes_grid1.axes_grid.AxesGrid`, and +`mpl_toolkits.axes_grid1.axes_grid.ImageGrid` as well as the corresponding classes +imported from ``mpl_toolkits.axisartist.axes_grid``. + +Pass ``label_mode='keep'`` instead to get the previous behavior of not modifying labels. + +``draw_gouraud_triangle`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +... is removed. Use `~.RendererBase.draw_gouraud_triangles` instead. + +A ``draw_gouraud_triangle`` call in a custom `~matplotlib.artist.Artist` can readily be +replaced as:: + + self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), + colors.reshape((1, 3, 4)), trans) + +A `~.RendererBase.draw_gouraud_triangles` method can be implemented from an +existing ``draw_gouraud_triangle`` method as:: + + transform = transform.frozen() + for tri, col in zip(triangles_array, colors_array): + self.draw_gouraud_triangle(gc, tri, col, transform) + +Miscellaneous removals +^^^^^^^^^^^^^^^^^^^^^^ + +The following items have previously been replaced, and are now removed: + +- *ticklabels* parameter of ``matplotlib.axis.Axis.set_ticklabels`` has been renamed to + *labels*. +- ``Barbs.barbs_doc`` and ``Quiver.quiver_doc`` are removed. These are the doc-strings + and should not be accessible as a named class member, but as normal doc-strings would. +- ``collections.PolyCollection.span_where`` and ``collections.BrokenBarHCollection``; + use ``fill_between`` instead. +- ``Legend.legendHandles`` was undocumented and has been renamed to ``legend_handles``. + +The following items have been removed without replacements: + +- The attributes ``repeat`` of `.TimedAnimation` and subclasses and ``save_count`` of + `.FuncAnimation` are considered private and removed. +- ``matplotlib.backend.backend_agg.BufferRegion.to_string`` +- ``matplotlib.backend.backend_agg.BufferRegion.to_string_argb`` +- ``matplotlib.backends.backend_ps.PsBackendHelper`` +- ``matplotlib.backends.backend_webagg.ServerThread`` +- *raw* parameter of `.GridSpecBase.get_grid_positions` +- ``matplotlib.patches.ConnectionStyle._Base.SimpleEvent`` +- ``passthru_pt`` attribute of ``mpl_toolkits.axisartist.AxisArtistHelper`` diff --git a/doc/api/prev_api_changes/api_changes_3.9.1.rst b/doc/api/prev_api_changes/api_changes_3.9.1.rst new file mode 100644 index 000000000000..4a9a1fc6669c --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.1.rst @@ -0,0 +1,13 @@ +API Changes for 3.9.1 +===================== + +Development +----------- + +Documentation-specific custom Sphinx roles are now semi-public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For third-party packages that derive types from Matplotlib, our use of custom roles may +prevent Sphinx from building their docs. These custom Sphinx roles are now public solely +for the purposes of use within projects that derive from Matplotlib types. See +:mod:`matplotlib.sphinxext.roles` for details. diff --git a/doc/api/prev_api_changes/api_changes_3.9.2.rst b/doc/api/prev_api_changes/api_changes_3.9.2.rst new file mode 100644 index 000000000000..4c2a69634502 --- /dev/null +++ b/doc/api/prev_api_changes/api_changes_3.9.2.rst @@ -0,0 +1,16 @@ +API Changes for 3.9.2 +===================== + +Development +----------- + +Windows wheel runtime bundling made static +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In 3.7.0, the MSVC runtime DLL was bundled in wheels to enable importing Matplotlib on +systems that do not have it installed. However, this could cause inconsistencies with +other wheels that did the same, and trigger random crashes depending on import order. See +`this issue `_ for further +details. + +Since 3.9.2, wheels now bundle the MSVC runtime DLL statically to avoid such issues. diff --git a/doc/api/projections/geo.rst b/doc/api/projections/geo.rst new file mode 100644 index 000000000000..beaa7ec343f3 --- /dev/null +++ b/doc/api/projections/geo.rst @@ -0,0 +1,7 @@ +****************************** +``matplotlib.projections.geo`` +****************************** + +.. automodule:: matplotlib.projections.geo + :members: + :show-inheritance: diff --git a/doc/api/projections/polar.rst b/doc/api/projections/polar.rst new file mode 100644 index 000000000000..3491fd92d16e --- /dev/null +++ b/doc/api/projections/polar.rst @@ -0,0 +1,7 @@ +******************************** +``matplotlib.projections.polar`` +******************************** + +.. automodule:: matplotlib.projections.polar + :members: + :show-inheritance: diff --git a/doc/api/projections_api.rst b/doc/api/projections_api.rst index bf4bbd8c31a8..f0c742c241e7 100644 --- a/doc/api/projections_api.rst +++ b/doc/api/projections_api.rst @@ -1,19 +1,18 @@ -*********** -projections -*********** - - -:mod:`matplotlib.projections` -============================= +************************** +``matplotlib.projections`` +************************** .. automodule:: matplotlib.projections :members: :show-inheritance: +Built-in projections +==================== +Matplotlib has built-in support for polar and some geographic projections. +See the following pages for more information: -:mod:`matplotlib.projections.polar` -=================================== +.. toctree:: + :maxdepth: 1 -.. automodule:: matplotlib.projections.polar - :members: - :show-inheritance: + projections/polar + projections/geo diff --git a/doc/api/pylab.rst b/doc/api/pylab.rst new file mode 100644 index 000000000000..184d0b578c71 --- /dev/null +++ b/doc/api/pylab.rst @@ -0,0 +1,6 @@ +********* +``pylab`` +********* + +.. automodule:: pylab + :no-members: diff --git a/doc/api/pyplot_api.rst b/doc/api/pyplot_api.rst deleted file mode 100644 index 94e3baac26f0..000000000000 --- a/doc/api/pyplot_api.rst +++ /dev/null @@ -1,13 +0,0 @@ -****** -pyplot -****** - - -:mod:`matplotlib.pyplot` -======================== - -.. automodule:: matplotlib.pyplot - :members: - :undoc-members: - :show-inheritance: - :exclude-members: plotting, colormaps diff --git a/doc/api/pyplot_summary.rst b/doc/api/pyplot_summary.rst index 7236fa864ccc..97d9c576cc86 100644 --- a/doc/api/pyplot_summary.rst +++ b/doc/api/pyplot_summary.rst @@ -1,8 +1,340 @@ -Plotting commands summary -========================= +********************* +``matplotlib.pyplot`` +********************* .. currentmodule:: matplotlib.pyplot -.. autofunction:: plotting +.. automodule:: matplotlib.pyplot + :no-members: + :no-undoc-members: -.. autofunction:: colormaps + +Managing Figure and Axes +------------------------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + axes + cla + clf + close + delaxes + fignum_exists + figure + gca + gcf + get_figlabels + get_fignums + sca + subplot + subplot2grid + subplot_mosaic + subplots + twinx + twiny + + +Adding data to the plot +----------------------- + +Basic +^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + plot + errorbar + scatter + step + loglog + semilogx + semilogy + fill_between + fill_betweenx + bar + barh + bar_label + grouped_bar + stem + eventplot + pie + pie_label + stackplot + broken_barh + vlines + hlines + fill + polar + + +Spans +^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + axhline + axhspan + axvline + axvspan + axline + + +Spectral +^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + acorr + angle_spectrum + cohere + csd + magnitude_spectrum + phase_spectrum + psd + specgram + xcorr + + +Statistics +^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + ecdf + boxplot + violinplot + + +Binned +^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + hexbin + hist + hist2d + stairs + + +Contours +^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + clabel + contour + contourf + + +2D arrays +^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + imshow + matshow + pcolor + pcolormesh + spy + figimage + + +Unstructured triangles +^^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + triplot + tripcolor + tricontour + tricontourf + + +Text and annotations +^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + annotate + text + figtext + table + arrow + figlegend + legend + + +Vector fields +^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + barbs + quiver + quiverkey + streamplot + + +Axis configuration +------------------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + autoscale + axis + box + grid + locator_params + minorticks_off + minorticks_on + rgrids + thetagrids + tick_params + ticklabel_format + xlabel + xlim + xscale + xticks + ylabel + ylim + yscale + yticks + suptitle + title + + +Layout +------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + margins + subplots_adjust + subplot_tool + tight_layout + + +Colormapping +------------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + clim + colorbar + gci + sci + get_cmap + set_cmap + imread + imsave + +Colormaps are available via the colormap registry `matplotlib.colormaps`. For +convenience this registry is available in ``pyplot`` as + +.. autodata:: colormaps + :no-value: + +Additionally, there are shortcut functions to set builtin colormaps; e.g. +``plt.viridis()`` is equivalent to ``plt.set_cmap('viridis')``. + + +.. autodata:: color_sequences + :no-value: + + +Configuration +------------- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + rc + rc_context + rcdefaults + + +Output +------ + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + draw + draw_if_interactive + ioff + ion + install_repl_displayhook + isinteractive + pause + savefig + show + switch_backend + uninstall_repl_displayhook + + +Other +----- + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + connect + disconnect + findobj + get + getp + get_current_fig_manager + ginput + new_figure_manager + set_loglevel + setp + waitforbuttonpress + xkcd diff --git a/doc/api/quiver_api.rst b/doc/api/quiver_api.rst new file mode 100644 index 000000000000..8dbcce61beb5 --- /dev/null +++ b/doc/api/quiver_api.rst @@ -0,0 +1,20 @@ +********************* +``matplotlib.quiver`` +********************* + +.. currentmodule:: matplotlib.quiver + +.. automodule:: matplotlib.quiver + :no-members: + :no-inherited-members: + +Classes +------- + +.. autosummary:: + :toctree: _as_gen/ + :template: autosummary.rst + + Quiver + QuiverKey + Barbs diff --git a/doc/api/rcsetup_api.rst b/doc/api/rcsetup_api.rst new file mode 100644 index 000000000000..7c5d109e2a78 --- /dev/null +++ b/doc/api/rcsetup_api.rst @@ -0,0 +1,8 @@ +********************** +``matplotlib.rcsetup`` +********************** + +.. automodule:: matplotlib.rcsetup + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/sankey_api.rst b/doc/api/sankey_api.rst index 51bdb15fa5f4..86223e7417c2 100644 --- a/doc/api/sankey_api.rst +++ b/doc/api/sankey_api.rst @@ -1,10 +1,6 @@ -****** -sankey -****** - - -:mod:`matplotlib.sankey` -======================== +********************* +``matplotlib.sankey`` +********************* .. automodule:: matplotlib.sankey :members: diff --git a/doc/api/scale_api.rst b/doc/api/scale_api.rst index 07778d04856b..623fbdd0392f 100644 --- a/doc/api/scale_api.rst +++ b/doc/api/scale_api.rst @@ -1,12 +1,9 @@ -***** -scale -***** - - -:mod:`matplotlib.scale` -======================= +******************** +``matplotlib.scale`` +******************** .. automodule:: matplotlib.scale :members: :undoc-members: :show-inheritance: + :member-order: bysource diff --git a/doc/api/sphinxext_figmpl_directive_api.rst b/doc/api/sphinxext_figmpl_directive_api.rst new file mode 100644 index 000000000000..9323fd31134a --- /dev/null +++ b/doc/api/sphinxext_figmpl_directive_api.rst @@ -0,0 +1,6 @@ +========================================= +``matplotlib.sphinxext.figmpl_directive`` +========================================= + +.. automodule:: matplotlib.sphinxext.figmpl_directive + :no-undoc-members: diff --git a/doc/api/sphinxext_mathmpl_api.rst b/doc/api/sphinxext_mathmpl_api.rst new file mode 100644 index 000000000000..839334ca39fe --- /dev/null +++ b/doc/api/sphinxext_mathmpl_api.rst @@ -0,0 +1,7 @@ +================================ +``matplotlib.sphinxext.mathmpl`` +================================ + +.. automodule:: matplotlib.sphinxext.mathmpl + :exclude-members: latex_math + :no-undoc-members: diff --git a/doc/api/sphinxext_plot_directive_api.rst b/doc/api/sphinxext_plot_directive_api.rst new file mode 100644 index 000000000000..a3b75c09bdd8 --- /dev/null +++ b/doc/api/sphinxext_plot_directive_api.rst @@ -0,0 +1,6 @@ +======================================= +``matplotlib.sphinxext.plot_directive`` +======================================= + +.. automodule:: matplotlib.sphinxext.plot_directive + :no-undoc-members: diff --git a/doc/api/sphinxext_roles.rst b/doc/api/sphinxext_roles.rst new file mode 100644 index 000000000000..99959ff05d14 --- /dev/null +++ b/doc/api/sphinxext_roles.rst @@ -0,0 +1,7 @@ +============================== +``matplotlib.sphinxext.roles`` +============================== + +.. automodule:: matplotlib.sphinxext.roles + :no-undoc-members: + :private-members: _rcparam_role, _mpltype_role diff --git a/doc/api/spines_api.rst b/doc/api/spines_api.rst index aeab960c0776..f119ef4e80f4 100644 --- a/doc/api/spines_api.rst +++ b/doc/api/spines_api.rst @@ -1,10 +1,6 @@ -****** -spines -****** - - -:mod:`matplotlib.spines` -======================== +********************* +``matplotlib.spines`` +********************* .. automodule:: matplotlib.spines :members: diff --git a/doc/api/style_api.rst b/doc/api/style_api.rst index 10576f4559d6..0900bf46cc75 100644 --- a/doc/api/style_api.rst +++ b/doc/api/style_api.rst @@ -1,10 +1,15 @@ -***** -style -***** +******************** +``matplotlib.style`` +******************** +Styles are predefined sets of `.rcParams` that define the visual appearance of +a plot. -:mod:`matplotlib.style` -======================= +:ref:`customizing` describes the mechanism and usage +of styles. + +The :doc:`/gallery/style_sheets/style_sheets_reference` gives an overview of +the builtin styles. .. automodule:: matplotlib.style :members: @@ -12,10 +17,17 @@ style :show-inheritance: :imported-members: -.. data:: matplotlib.style.library +.. imported variables have to be specified explicitly due to + https://github.com/sphinx-doc/sphinx/issues/6607 + +.. data:: library + + A dict mapping from style name to `.rcParams` defining that style. + + This is meant to be read-only. Use `.reload_library` to update. - Dictionary of available styles +.. data:: available -.. data:: matplotlib.style.available + List of the names of the available styles. - List of available styles + This is meant to be read-only. Use `.reload_library` to update. diff --git a/doc/api/table_api.rst b/doc/api/table_api.rst new file mode 100644 index 000000000000..ee44af0949f7 --- /dev/null +++ b/doc/api/table_api.rst @@ -0,0 +1,8 @@ +******************** +``matplotlib.table`` +******************** + +.. automodule:: matplotlib.table + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/testing_api.rst b/doc/api/testing_api.rst new file mode 100644 index 000000000000..ae81d2f89ca7 --- /dev/null +++ b/doc/api/testing_api.rst @@ -0,0 +1,47 @@ +********************** +``matplotlib.testing`` +********************** + + +:mod:`matplotlib.testing` +========================= + +.. automodule:: matplotlib.testing + :members: + :undoc-members: + :show-inheritance: + + +:mod:`matplotlib.testing.compare` +================================= + +.. automodule:: matplotlib.testing.compare + :members: + :undoc-members: + :show-inheritance: + + +:mod:`matplotlib.testing.decorators` +==================================== + +.. automodule:: matplotlib.testing.decorators + :members: + :undoc-members: + :show-inheritance: + + +:mod:`matplotlib.testing.exceptions` +==================================== + +.. automodule:: matplotlib.testing.exceptions + :members: + :undoc-members: + :show-inheritance: + + +Testing with optional dependencies +================================== +For more information on fixtures, see :external+pytest:ref:`pytest fixtures `. + +.. autofunction:: matplotlib.testing.conftest.pd +.. autofunction:: matplotlib.testing.conftest.xr diff --git a/doc/api/texmanager_api.rst b/doc/api/texmanager_api.rst new file mode 100644 index 000000000000..0dc52d2bc27c --- /dev/null +++ b/doc/api/texmanager_api.rst @@ -0,0 +1,8 @@ +************************* +``matplotlib.texmanager`` +************************* + +.. automodule:: matplotlib.texmanager + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/api/text_api.rst b/doc/api/text_api.rst index 19ec2f8d833c..af37e5c526a3 100644 --- a/doc/api/text_api.rst +++ b/doc/api/text_api.rst @@ -1,12 +1,33 @@ -**** -text -**** +******************* +``matplotlib.text`` +******************* - -:mod:`matplotlib.text` -======================= +.. redirect-from:: /api/textpath_api .. automodule:: matplotlib.text + :no-members: + +.. autoclass:: matplotlib.text.Text + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.Annotation + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.OffsetFrom + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.TextPath + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: matplotlib.text.TextToPath :members: :undoc-members: :show-inheritance: diff --git a/doc/api/ticker_api.rst b/doc/api/ticker_api.rst index dae66cf07394..652050dafedc 100644 --- a/doc/api/ticker_api.rst +++ b/doc/api/ticker_api.rst @@ -1,12 +1,12 @@ -****** -ticker -****** - - -:mod:`matplotlib.ticker` -======================== +********************* +``matplotlib.ticker`` +********************* .. automodule:: matplotlib.ticker :members: :undoc-members: :show-inheritance: + + +.. inheritance-diagram:: matplotlib.ticker + :parts: 1 diff --git a/doc/api/tight_layout_api.rst b/doc/api/tight_layout_api.rst deleted file mode 100644 index 11cdaa9b71da..000000000000 --- a/doc/api/tight_layout_api.rst +++ /dev/null @@ -1,12 +0,0 @@ -************ -tight_layout -************ - - -:mod:`matplotlib.tight_layout` -============================== - -.. automodule:: matplotlib.tight_layout - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/api/toolkits/axes_grid1.rst b/doc/api/toolkits/axes_grid1.rst new file mode 100644 index 000000000000..c48a6a31af90 --- /dev/null +++ b/doc/api/toolkits/axes_grid1.rst @@ -0,0 +1,42 @@ +.. module:: mpl_toolkits.axes_grid1 + +.. redirect-from:: /api/toolkits/axes_grid + +``mpl_toolkits.axes_grid1`` +=========================== + +:mod:`mpl_toolkits.axes_grid1` provides a framework of helper classes to adjust +the positioning of multiple fixed-aspect Axes (e.g., displaying images). It +can be contrasted with the ``aspect`` property of Matplotlib Axes, which +adjusts the position of a single Axes. + +See :ref:`axes_grid1_users-guide-index` for a guide on the usage of axes_grid1. + +.. figure:: ../../gallery/axes_grid1/images/sphx_glr_demo_axes_grid_001.png + :target: ../../gallery/axes_grid1/demo_axes_grid.html + :align: center + :scale: 50 + +.. note:: + + This module contains classes and function that were formerly part of the + ``mpl_toolkits.axes_grid`` module that was removed in 3.6. Additional + classes from that older module may also be found in + `mpl_toolkits.axisartist`. + +.. currentmodule:: mpl_toolkits + +**The submodules of the axes_grid1 API are:** + +.. autosummary:: + :toctree: ../_as_gen + :template: automodule.rst + + axes_grid1.anchored_artists + axes_grid1.axes_divider + axes_grid1.axes_grid + axes_grid1.axes_rgb + axes_grid1.axes_size + axes_grid1.inset_locator + axes_grid1.mpl_axes + axes_grid1.parasite_axes diff --git a/doc/api/toolkits/axisartist.rst b/doc/api/toolkits/axisartist.rst new file mode 100644 index 000000000000..2dec9cafa01d --- /dev/null +++ b/doc/api/toolkits/axisartist.rst @@ -0,0 +1,43 @@ +.. module:: mpl_toolkits.axisartist + +``mpl_toolkits.axisartist`` +=========================== + +The *axisartist* namespace provides a derived Axes implementation +(:class:`~mpl_toolkits.axisartist.axislines.Axes`), designed to support curvilinear +grids. The biggest difference is that the artists that are responsible for +drawing axis lines, ticks, ticklabels, and axis labels are separated out from +Matplotlib's Axis class. + +You can find a tutorial describing usage of axisartist at the +:ref:`axisartist_users-guide-index` user guide. + +.. figure:: ../../gallery/axisartist/images/sphx_glr_demo_curvelinear_grid_001.png + :target: ../../gallery/axisartist/demo_curvelinear_grid.html + :align: center + :scale: 50 + +.. note:: + + This module contains classes and function that were formerly part of the + ``mpl_toolkits.axes_grid`` module that was removed in 3.6. Additional + classes from that older module may also be found in + `mpl_toolkits.axes_grid1`. + +.. currentmodule:: mpl_toolkits + +**The submodules of the axisartist API are:** + +.. autosummary:: + :toctree: ../_as_gen + :template: automodule.rst + + axisartist.angle_helper + axisartist.axes_divider + axisartist.axis_artist + axisartist.axisline_style + axisartist.axislines + axisartist.floating_axes + axisartist.grid_finder + axisartist.grid_helper_curvelinear + axisartist.parasite_axes diff --git a/doc/api/toolkits/mplot3d.rst b/doc/api/toolkits/mplot3d.rst new file mode 100644 index 000000000000..4810bb742bd2 --- /dev/null +++ b/doc/api/toolkits/mplot3d.rst @@ -0,0 +1,123 @@ +.. _toolkit_mplot3d-index: +.. currentmodule:: mpl_toolkits.mplot3d + +************************ +``mpl_toolkits.mplot3d`` +************************ + +The mplot3d toolkit adds simple 3D plotting capabilities (scatter, surface, +line, mesh, etc.) to Matplotlib by supplying an Axes object that can create +a 2D projection of a 3D scene. The resulting graph will have the same look +and feel as regular 2D plots. Not the fastest or most feature complete 3D +library out there, but it ships with Matplotlib and thus may be a lighter +weight solution for some use cases. + +See the :ref:`mplot3d tutorial ` for +more information. + +.. image:: /_static/demo_mplot3d.png + :align: center + +The interactive backends also provide the ability to rotate and zoom the 3D +scene. One can rotate the 3D scene by simply clicking-and-dragging the scene. +Panning is done by clicking the middle mouse button, and zooming is done by +right-clicking the scene and dragging the mouse up and down. Unlike 2D plots, +the toolbar pan and zoom buttons are not used. + +.. toctree:: + :maxdepth: 2 + + mplot3d/faq.rst + mplot3d/view_angles.rst + mplot3d/axes3d.rst + +.. note:: + `.pyplot` cannot be used to add content to 3D plots, because its function + signatures are strictly 2D and cannot handle the additional information + needed for 3D. Instead, use the explicit API by calling the respective + methods on the `.Axes3D` object. + +.. automodule:: mpl_toolkits.mplot3d + :no-members: + :no-undoc-members: + +.. module:: mpl_toolkits.mplot3d.axes3d +.. currentmodule:: mpl_toolkits.mplot3d + +:mod:`~mpl_toolkits.mplot3d.axes3d` +=================================== + +.. note:: + 3D plotting in Matplotlib is still not as mature as the 2D case. + Please report any functions that do not behave as expected as a bug. + In addition, help and patches would be greatly appreciated! + + +`axes3d.Axes3D` (fig[, rect, elev, azim, roll, ...]) 3D Axes object. + + +.. module:: mpl_toolkits.mplot3d.axis3d +.. currentmodule:: mpl_toolkits.mplot3d + +:mod:`~mpl_toolkits.mplot3d.axis3d` +=================================== + +.. note:: + See :attr:`!mpl_toolkits.mplot3d.axis3d._axinfo` for a dictionary containing + constants that may be modified for controlling the look and feel + of mplot3d axes (e.g., label spacing, font colors and panel colors). + Historically, axis3d has suffered from having hard-coded constants + that precluded user adjustments, and this dictionary was implemented + in version 1.1 as a stop-gap measure. + + +.. autosummary:: + :toctree: ../_as_gen + :template: autosummary.rst + + axis3d.Axis + + +.. module:: mpl_toolkits.mplot3d.art3d +.. currentmodule:: mpl_toolkits.mplot3d + +:mod:`~mpl_toolkits.mplot3d.art3d` +================================== + +.. autosummary:: + :toctree: ../_as_gen + :template: autosummary.rst + + art3d.Line3D + art3d.Line3DCollection + art3d.Patch3D + art3d.Patch3DCollection + art3d.Path3DCollection + art3d.PathPatch3D + art3d.Poly3DCollection + art3d.Text3D + art3d.get_dir_vector + art3d.juggle_axes + art3d.line_2d_to_3d + art3d.line_collection_2d_to_3d + art3d.patch_2d_to_3d + art3d.patch_collection_2d_to_3d + art3d.pathpatch_2d_to_3d + art3d.poly_collection_2d_to_3d + art3d.rotate_axes + art3d.text_2d_to_3d + +.. module:: mpl_toolkits.mplot3d.proj3d +.. currentmodule:: mpl_toolkits.mplot3d + +:mod:`~mpl_toolkits.mplot3d.proj3d` +=================================== + +.. autosummary:: + :toctree: ../_as_gen + :template: autosummary.rst + + proj3d.inv_transform + proj3d.proj_transform + proj3d.proj_transform_clip + proj3d.world_transformation diff --git a/doc/api/toolkits/mplot3d/axes3d.rst b/doc/api/toolkits/mplot3d/axes3d.rst new file mode 100644 index 000000000000..612b3dd82a4b --- /dev/null +++ b/doc/api/toolkits/mplot3d/axes3d.rst @@ -0,0 +1,317 @@ +mpl\_toolkits.mplot3d.axes3d.Axes3D +=================================== + + +.. currentmodule:: mpl_toolkits.mplot3d.axes3d + + +.. autoclass:: Axes3D + :no-members: + :no-undoc-members: + :show-inheritance: + + +.. currentmodule:: mpl_toolkits.mplot3d.axes3d.Axes3D + + +Plotting +-------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + plot + scatter + bar + bar3d + + plot_surface + plot_wireframe + plot_trisurf + fill_between + + clabel + contour + tricontour + contourf + tricontourf + + quiver + voxels + errorbar + stem + + +Text and annotations +-------------------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + text + text2D + + +Clearing +-------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + clear + + +Appearance +---------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + set_axis_off + set_axis_on + grid + + +Axis +---- + +Axis limits and direction +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + get_zaxis + get_xlim + set_xlim + get_ylim + set_ylim + get_zlim + set_zlim + get_w_lims + get_xinverted + set_xinverted + invert_xaxis + xaxis_inverted + get_yinverted + set_yinverted + invert_yaxis + yaxis_inverted + get_zinverted + set_zinverted + invert_zaxis + zaxis_inverted + get_xbound + set_xbound + get_ybound + set_ybound + get_zbound + set_zbound + + +Axis labels and title +^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + set_zlabel + get_zlabel + set_title + + +Axis scales +^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + set_xscale + set_yscale + set_zscale + get_zscale + + +Autoscaling and margins +^^^^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + get_zmargin + set_zmargin + margins + autoscale + autoscale_view + set_autoscalez_on + get_autoscalez_on + auto_scale_xyz + + +Aspect ratio +^^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + set_aspect + set_box_aspect + apply_aspect + + +Ticks +^^^^^ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + tick_params + set_zticks + get_zticks + set_zticklabels + get_zticklines + get_zgridlines + get_zminorticklabels + get_zmajorticklabels + zaxis_date + + +Units +----- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + convert_zunits + + +Adding artists +-------------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + add_collection3d + + +Sharing +------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + sharez + shareview + + +Interactive +----------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + can_zoom + can_pan + disable_mouse_rotation + mouse_init + drag_pan + format_zdata + format_coord + + +Projection and perspective +-------------------------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + view_init + set_proj_type + get_proj + set_top_view + + +Drawing +------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + draw + get_tightbbox + + +Aliases and deprecated methods +------------------------------ + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + set_zlim3d + stem3D + text3D + + +Other +----- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + get_axis_position + add_contour_set + add_contourf_set + update_datalim + + +.. currentmodule:: mpl_toolkits.mplot3d + +Sample 3D data +-------------- + +.. autosummary:: + :toctree: ../../_as_gen + :template: autosummary.rst + :nosignatures: + + axes3d.get_test_data + + +.. minigallery:: mpl_toolkits.mplot3d.axes3d.Axes3D + :add-heading: diff --git a/doc/api/toolkits/mplot3d/faq.rst b/doc/api/toolkits/mplot3d/faq.rst new file mode 100644 index 000000000000..20fe81e574fe --- /dev/null +++ b/doc/api/toolkits/mplot3d/faq.rst @@ -0,0 +1,51 @@ +.. _toolkit_mplot3d-faq: + +*********** +mplot3d FAQ +*********** + +How is mplot3d different from Mayavi? +===================================== +Mayavi_ is a very powerful and featureful 3D graphing library. For advanced +3D scenes and excellent rendering capabilities, it is highly recommended to +use Mayavi. + +mplot3d was intended to allow users to create simple 3D graphs with the same +"look-and-feel" as matplotlib's 2D plots. Furthermore, users can use the same +toolkit that they are already familiar with to generate both their 2D and 3D +plots. + + +My 3D plot doesn't look right at certain viewing angles +======================================================= +This is probably the most commonly reported issue with mplot3d. The problem +is that -- from some viewing angles -- a 3D object would appear in front +of another object, even though it is physically behind it. This can result in +plots that do not look "physically correct." + +Unfortunately, while some work is being done to reduce the occurrence of this +artifact, it is currently an intractable problem, and cannot be fully solved +until matplotlib supports 3D graphics rendering at its core. + +The problem occurs due to the reduction of 3D data down to 2D + z-order +scalar. A single value represents the 3rd dimension for all parts of 3D +objects in a collection. Therefore, when the bounding boxes of two collections +intersect, it becomes possible for this artifact to occur. Furthermore, the +intersection of two 3D objects (such as polygons or patches) cannot be +rendered properly in matplotlib's 2D rendering engine. + +This problem will likely not be solved until OpenGL support is added to all of +the backends (patches are greatly welcomed). Until then, if you need complex +3D scenes, we recommend using Mayavi_. + + +I don't like how the 3D plot is laid out, how do I change that? +=============================================================== +Historically, mplot3d has suffered from a hard-coding of parameters used +to control visuals such as label spacing, tick length, and grid line width. +Work is being done to eliminate this issue. For matplotlib v1.1.0, there is +a semi-official manner to modify these parameters. See the note in the +:mod:`.mplot3d.axis3d` section of the mplot3d API documentation for +more information. + +.. _Mayavi: https://docs.enthought.com/mayavi/mayavi/ diff --git a/doc/api/toolkits/mplot3d/view_angles.rst b/doc/api/toolkits/mplot3d/view_angles.rst new file mode 100644 index 000000000000..e4200cd2d0e4 --- /dev/null +++ b/doc/api/toolkits/mplot3d/view_angles.rst @@ -0,0 +1,204 @@ +.. _toolkit_mplot3d-view-angles: + +******************* +mplot3d View Angles +******************* + +How to define the view angle +============================ + +The position of the viewport "camera" in a 3D plot is defined by three angles: +*elevation*, *azimuth*, and *roll*. From the resulting position, it always +points towards the center of the plot box volume. The angle direction is a +common convention, and is shared with +`PyVista `_ and MATLAB_. +Note that a positive roll angle rotates the +viewing plane clockwise, so the 3d axes will appear to rotate +counter-clockwise. + +.. image:: /_static/mplot3d_view_angles.png + :align: center + :scale: 50 + +Rotating the plot using the mouse will control azimuth, elevation, +as well as roll, and all three angles can be set programmatically:: + + import matplotlib.pyplot as plt + ax = plt.figure().add_subplot(projection='3d') + ax.view_init(elev=30, azim=45, roll=15) + + +Primary view planes +=================== + +To look directly at the primary view planes, the required elevation, azimuth, +and roll angles are shown in the diagram of an "unfolded" plot below. These are +further documented in the `.mplot3d.axes3d.Axes3D.view_init` API. + +.. plot:: gallery/mplot3d/view_planes_3d.py + :align: center + + +.. _toolkit_mouse-rotation: + +Rotation with mouse +=================== + +3D plots can be reoriented by dragging the mouse. +There are various ways to accomplish this; the style of mouse rotation +can be specified by setting :rc:`axes3d.mouserotationstyle`, see +:doc:`/users/explain/customizing`. + +Prior to v3.10, the 2D mouse position corresponded directly +to azimuth and elevation; this is also how it is done in MATLAB_. +To keep it this way, set ``mouserotationstyle: azel``. +This approach works fine for spherical coordinate plots, where the *z* axis is special; +however, it leads to a kind of 'gimbal lock' when looking down the *z* axis: +the plot reacts differently to mouse movement, dependent on the particular +orientation at hand. Also, 'roll' cannot be controlled. + +As an alternative, there are various mouse rotation styles where the mouse +manipulates a virtual 'trackball'. In its simplest form (``mouserotationstyle: trackball``), +the trackball rotates around an in-plane axis perpendicular to the mouse motion +(it is as if there is a plate laying on the trackball; the plate itself is fixed +in orientation, but you can drag the plate with the mouse, thus rotating the ball). +This is more natural to work with than the ``azel`` style; however, +the plot cannot be easily rotated around the viewing direction - one has to +move the mouse in circles with a handedness opposite to the desired rotation, +counterintuitively. + +A different variety of trackball rotates along the shortest arc on the virtual +sphere (``mouserotationstyle: sphere``). Rotating around the viewing direction +is straightforward with it: grab the ball near its edge instead of near the center. + +Ken Shoemake's ARCBALL [Shoemake1992]_ is also available (``mouserotationstyle: Shoemake``); +it resembles the ``sphere`` style, but is free of hysteresis, +i.e., returning mouse to the original position +returns the figure to its original orientation; the rotation is independent +of the details of the path the mouse took, which could be desirable. +However, Shoemake's arcball rotates at twice the angular rate of the +mouse movement (it is quite noticeable, especially when adjusting roll), +and it lacks an obvious mechanical equivalent; arguably, the path-independent +rotation is not natural (however convenient), it could take some getting used to. +So it is a trade-off. + +Henriksen et al. [Henriksen2002]_ provide an overview. In summary: + +.. list-table:: + :width: 100% + :widths: 30 20 20 20 20 35 + + * - Style + - traditional [1]_ + - incl. roll [2]_ + - uniform [3]_ + - path independent [4]_ + - mechanical counterpart [5]_ + * - azel + - ✔️ + - ❌ + - ❌ + - ✔️ + - ✔️ + * - trackball + - ❌ + - ✓ [6]_ + - ✔️ + - ❌ + - ✔️ + * - sphere + - ❌ + - ✔️ + - ✔️ + - ❌ + - ✔️ + * - arcball + - ❌ + - ✔️ + - ✔️ + - ✔️ + - ❌ + + +.. [1] The way it was prior to v3.10; this is also MATLAB's style +.. [2] Mouse controls roll too (not only azimuth and elevation) +.. [3] Figure reacts the same way to mouse movements, regardless of orientation (no difference between 'poles' and 'equator') +.. [4] Returning mouse to original position returns figure to original orientation (rotation is independent of the details of the path the mouse took) +.. [5] The style has a corresponding natural implementation as a mechanical device +.. [6] While it is possible to control roll with the ``trackball`` style, this is not immediately obvious (it requires moving the mouse in large circles) and a bit counterintuitive (the resulting roll is in the opposite direction) + +You can try out one of the various mouse rotation styles using: + +.. code-block:: python + + import matplotlib as mpl + mpl.rcParams['axes3d.mouserotationstyle'] = 'trackball' # 'azel', 'trackball', 'sphere', or 'arcball' + + import numpy as np + import matplotlib.pyplot as plt + from matplotlib import cm + + ax = plt.figure().add_subplot(projection='3d') + + X = np.arange(-5, 5, 0.25) + Y = np.arange(-5, 5, 0.25) + X, Y = np.meshgrid(X, Y) + R = np.sqrt(X**2 + Y**2) + Z = np.sin(R) + + surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, + linewidth=0, antialiased=False) + + plt.show() + +Alternatively, create a file ``matplotlibrc``, with contents:: + + axes3d.mouserotationstyle: trackball + +(or any of the other styles, instead of ``trackball``), and then run any of +the :ref:`mplot3d-examples-index` examples. + +The size of the virtual trackball, sphere, or arcball can be adjusted +by setting :rc:`axes3d.trackballsize`. This specifies how much +mouse motion is needed to obtain a given rotation angle (when near the center), +and it controls where the edge of the sphere or arcball is (how far from +the center, hence how close to the plot edge). +The size is specified in units of the Axes bounding box, +i.e., to make the arcball span the whole bounding box, set it to 1. +A size of about 2/3 appears to work reasonably well; this is the default. + +Both arcballs (``mouserotationstyle: sphere`` and +``mouserotationstyle: arcball``) have a noticeable edge; the edge can be made +less abrupt by specifying a border width, :rc:`axes3d.trackballborder`. +This works somewhat like Gavin Bell's arcball, which was +originally written for OpenGL [Bell1988]_, and is used in Blender and Meshlab. +Bell's arcball extends the arcball's spherical control surface with a hyperbola; +the two are smoothly joined. However, the hyperbola extends all the way beyond +the edge of the plot. In the mplot3d sphere and arcball style, the border extends +to a radius ``trackballsize/2 + trackballborder``. +Beyond the border, the style works like the original: it controls roll only. +A border width of about 0.2 appears to work well; this is the default. +To obtain the original Shoemake's arcball with a sharp border, +set the border width to 0. +For an extended border similar to Bell's arcball, where the transition from +the arcball to the border occurs at 45°, set the border width to +:math:`\sqrt 2 \approx 1.414`. +The border is a circular arc, wrapped around the arcball sphere cylindrically +(like a doughnut), joined smoothly to the sphere, much like Bell's hyperbola. + +.. _MATLAB: https://www.mathworks.com/help/matlab/ref/view.html + +.. [Shoemake1992] Ken Shoemake, "ARCBALL: A user interface for specifying + three-dimensional rotation using a mouse", in Proceedings of Graphics + Interface '92, 1992, pp. 151-156, https://doi.org/10.20380/GI1992.18 + +.. [Bell1988] Gavin Bell, in the examples included with the GLUT (OpenGL + Utility Toolkit) library, + https://github.com/markkilgard/glut/blob/master/progs/examples/trackball.h + +.. [Henriksen2002] Knud Henriksen, Jon Sporring, Kasper Hornbæk, + "Virtual Trackballs Revisited", in IEEE Transactions on Visualization + and Computer Graphics, Volume 10, Issue 2, March-April 2004, pp. 206-216, + https://doi.org/10.1109/TVCG.2004.1260772 `[full-text]`__; + +__ https://www.researchgate.net/publication/8329656_Virtual_Trackballs_Revisited#fullTextFileContent diff --git a/doc/api/transformations.rst b/doc/api/transformations.rst new file mode 100644 index 000000000000..7d5dd09d28c2 --- /dev/null +++ b/doc/api/transformations.rst @@ -0,0 +1,17 @@ +************************* +``matplotlib.transforms`` +************************* + +.. inheritance-diagram:: matplotlib.transforms + :parts: 1 + +.. automodule:: matplotlib.transforms + :members: TransformNode, BboxBase, Bbox, TransformedBbox, Transform, + TransformWrapper, AffineBase, Affine2DBase, Affine2D, IdentityTransform, + BlendedGenericTransform, BlendedAffine2D, blended_transform_factory, + CompositeGenericTransform, CompositeAffine2D, + composite_transform_factory, BboxTransform, BboxTransformTo, + BboxTransformFrom, ScaledTranslation, TransformedPath, nonsingular, + interval_contains, interval_contains_open + :show-inheritance: + :special-members: __add__, __sub__ diff --git a/doc/api/transforms.dot b/doc/api/transforms.dot new file mode 100644 index 000000000000..c3ea975158bf --- /dev/null +++ b/doc/api/transforms.dot @@ -0,0 +1,141 @@ +digraph { + splines="polyline"; + + node [ + fontname="DejaVu Sans, Vera Sans, Liberation Sans, Arial, Helvetica, sans", + shape=box, + ]; + edge [ + arrowsize=0.5, + fontname="DejaVu Sans, Vera Sans, Liberation Sans, Arial, Helvetica, sans", + ]; + + // Axes properties. + Axes__bbox [ + label=Axes.bbox>, + target="_top", + tooltip="TransformedBbox", + URL="transformations.html#matplotlib.transforms.TransformedBbox", + ]; + Axes__transAxes [ + label=Axes.transAxes> + target="_top", + tooltip="BboxTransformTo", + URL="transformations.html#matplotlib.transforms.BboxTransformTo", + ]; + Axes__transData [ + label=Axes.transData> + target="_top", + tooltip="CompositeGenericTransform", + URL="transformations.html#matplotlib.transforms.CompositeGenericTransform", + ]; + Axes__transLimits [ + label=Axes.transLimits> + target="_top", + tooltip="BboxTransformFrom", + URL="transformations.html#matplotlib.transforms.BboxTransformFrom", + ]; + Axes__transScale [ + label=Axes.transScale> + target="_top", + tooltip="TransformWrapper", + URL="transformations.html#matplotlib.transforms.TransformWrapper", + ]; + Axes__position [ + label=Axes.get_position()> + target="_top", + tooltip="Bbox", + URL="transformations.html#matplotlib.transforms.Bbox", + ]; + Axes__viewLim [ + label = Axes._viewLim> + target="_top", + tooltip="Bbox", + URL="transformations.html#matplotlib.transforms.Bbox", + ]; + + // Axis properties. + XAxis_transform [ + label=Axes.xaxis.get_transform()> + target="_top", + tooltip="IdentityTransform", + URL="transformations.html#matplotlib.transforms.IdentityTransform", + ]; + YAxis_transform [ + label=Axes.yaxis.get_transform()> + target="_top", + tooltip="IdentityTransform", + URL="transformations.html#matplotlib.transforms.IdentityTransform", + ]; + + // Figure properties. + Figure__transFigure [ + label=Figure.transFigure> + target="_top", + tooltip="BboxTransformTo", + URL="transformations.html#matplotlib.transforms.BboxTransformTo", + ]; + Figure__bbox [ + label=Figure.bbox> + target="_top", + tooltip="TransformedBbox", + URL="transformations.html#matplotlib.transforms.TransformedBbox", + ]; + Figure__bbox_inches [ + label=Figure.bbox_inches> + target="_top", + tooltip="Bbox", + URL="transformations.html#matplotlib.transforms.Bbox", + ]; + Figure__dpi_scale_trans [ + label=Figure.dpi_scale_trans> + target="_top", + tooltip="Affine2D", + URL="transformations.html#matplotlib.transforms.Affine2D", + ]; + + // Internal unnamed transform children. + Axes__transDataB [ + label="CompositeGenericTransform", + target="_top", + tooltip="CompositeGenericTransform", + URL="transformations.html#matplotlib.transforms.CompositeGenericTransform", + ]; + Axes__transLimitsBbox [ + label="TransformedBbox", + target="_top", + tooltip="TransformedBbox", + URL="transformations.html#matplotlib.transforms.TransformedBbox", + ]; + Axes__transScaleBlend [ + label="BlendedAffine2D", + target="_top", + tooltip="BlendedAffine2D", + URL="transformations.html#matplotlib.transforms.BlendedAffine2D", + ]; + + // The actual Axes__transform tree follows: + Axes__transData -> Axes__transScale [label="a", labelangle=90]; + Axes__transData -> Axes__transDataB [label="b"]; + Axes__transDataB -> Axes__transLimits [label="a"]; + Axes__transDataB -> Axes__transAxes [label="b"]; + + Axes__transScale -> Axes__transScaleBlend [label="child"]; + Axes__transScaleBlend -> XAxis_transform [label="x_transform"]; + Axes__transScaleBlend -> YAxis_transform [label="y_transform"]; + + Axes__transLimits -> Axes__transLimitsBbox [label="boxin"]; + Axes__transLimitsBbox -> Axes__viewLim [label="bbox"]; + Axes__transLimitsBbox -> Axes__transScale [label="transform"]; + + Axes__transAxes -> Axes__bbox [label="boxout"]; + Axes__bbox -> Axes__position [label="bbox"]; + Axes__bbox -> Figure__transFigure [label="transform"]; + + Figure__transFigure -> Figure__bbox [label="boxout"]; + Figure__bbox -> Figure__bbox_inches [label="bbox"]; + Figure__bbox -> Figure__dpi_scale_trans [label="transform"]; +} diff --git a/doc/api/tri_api.rst b/doc/api/tri_api.rst index 8ede2a9beb28..0b4e046eec08 100644 --- a/doc/api/tri_api.rst +++ b/doc/api/tri_api.rst @@ -1,14 +1,17 @@ -**************** -triangular grids -**************** +****************** +``matplotlib.tri`` +****************** -:mod:`matplotlib.tri` -===================== -.. automodule:: matplotlib.tri +Unstructured triangular grid functions. + +.. py:module:: matplotlib.tri .. autoclass:: matplotlib.tri.Triangulation :members: +.. autoclass:: matplotlib.tri.TriContourSet + :show-inheritance: + .. autoclass:: matplotlib.tri.TriFinder .. autoclass:: matplotlib.tri.TrapezoidMapTriFinder @@ -16,7 +19,7 @@ triangular grids :show-inheritance: .. autoclass:: matplotlib.tri.TriInterpolator - + .. autoclass:: matplotlib.tri.LinearTriInterpolator :members: __call__, gradient :show-inheritance: @@ -29,7 +32,7 @@ triangular grids .. autoclass:: matplotlib.tri.UniformTriRefiner :show-inheritance: - :members: + :members: .. autoclass:: matplotlib.tri.TriAnalyzer - :members: + :members: diff --git a/doc/api/type1font.rst b/doc/api/type1font.rst deleted file mode 100644 index bd4501bbd91b..000000000000 --- a/doc/api/type1font.rst +++ /dev/null @@ -1,11 +0,0 @@ -**************** -type1font -**************** - -:mod:`matplotlib.type1font` -=========================== - -.. automodule:: matplotlib.type1font - :members: - :undoc-members: - :show-inheritance: diff --git a/doc/api/typing_api.rst b/doc/api/typing_api.rst new file mode 100644 index 000000000000..4c0cad953487 --- /dev/null +++ b/doc/api/typing_api.rst @@ -0,0 +1,34 @@ +********************* +``matplotlib.typing`` +********************* + +.. automodule:: matplotlib.typing + :no-members: + :no-undoc-members: + +Color +===== + +.. autodata:: matplotlib.typing.ColorType +.. autodata:: matplotlib.typing.RGBColorType +.. autodata:: matplotlib.typing.RGBAColorType +.. autodata:: matplotlib.typing.ColourType +.. autodata:: matplotlib.typing.RGBColourType +.. autodata:: matplotlib.typing.RGBAColourType + +Styles +====== + +.. autodata:: matplotlib.typing.LineStyleType +.. autodata:: matplotlib.typing.DrawStyleType +.. autodata:: matplotlib.typing.MarkEveryType +.. autodata:: matplotlib.typing.FillStyleType +.. autodata:: matplotlib.typing.CapStyleType +.. autodata:: matplotlib.typing.JoinStyleType + +Other types +=========== + +.. autodata:: matplotlib.typing.CoordsType +.. autodata:: matplotlib.typing.RcStyleType +.. autodata:: matplotlib.typing.HashableList diff --git a/doc/api/units_api.rst b/doc/api/units_api.rst index c29596eb3d3c..aae2d7aa3254 100644 --- a/doc/api/units_api.rst +++ b/doc/api/units_api.rst @@ -1,10 +1,6 @@ -***** -units -***** - - -:mod:`matplotlib.units` -======================== +******************** +``matplotlib.units`` +******************** .. automodule:: matplotlib.units :members: diff --git a/doc/api/widgets_api.rst b/doc/api/widgets_api.rst index edc196717988..739b0f8931e0 100644 --- a/doc/api/widgets_api.rst +++ b/doc/api/widgets_api.rst @@ -1,12 +1,54 @@ -******* -widgets -******* +********************** +``matplotlib.widgets`` +********************** - -:mod:`matplotlib.widgets` -========================= +.. currentmodule:: matplotlib.widgets .. automodule:: matplotlib.widgets - :members: - :undoc-members: - :show-inheritance: + :no-members: + :no-undoc-members: + + +Widget classes +============== + +.. inheritance-diagram:: matplotlib.widgets.Widget + :parts: 1 + :private-bases: + :include-subclasses: + +.. autosummary:: + :toctree: _as_gen + :template: autosummary.rst + :nosignatures: + + Widget + AxesWidget + Cursor + MultiCursor + Button + CheckButtons + RadioButtons + SliderBase + Slider + RangeSlider + TextBox + _SelectorWidget + RectangleSelector + EllipseSelector + Lasso + LassoSelector + PolygonSelector + SpanSelector + SubplotTool + +Helper classes +============== + +.. autosummary:: + :toctree: _as_gen + :nosignatures: + + LockDraw + ToolHandles + ToolLineHandles diff --git a/doc/conf.py b/doc/conf.py index 0982d6da5f32..806561f0bfc0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,82 +1,357 @@ -# -*- coding: utf-8 -*- -# # Matplotlib documentation build configuration file, created by # sphinx-quickstart on Fri May 2 12:33:25 2008. # -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set to its containing +# dir. # # The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). +# that aren't picklable (module imports are okay, they're removed +# automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. +from datetime import datetime, timezone +import logging import os +from pathlib import Path +import re +import shutil +import subprocess import sys +import time +from urllib.parse import urlsplit, urlunsplit +import warnings + +from packaging.version import parse as parse_version import sphinx +import yaml + +import matplotlib + +# debug that building expected version +print(f"Building Documentation for Matplotlib: {matplotlib.__version__}") + +# Release mode enables optimizations and other related options. +is_release_build = tags.has('release') # noqa + +# are we running circle CI? +CIRCLECI = 'CIRCLECI' in os.environ +# are we deploying this build to matplotlib.org/devdocs? +# This is a copy of the logic in .circleci/deploy-docs.sh +DEVDOCS = ( + CIRCLECI and + (os.environ.get("CIRCLE_PROJECT_USERNAME") == "matplotlib") and + (os.environ.get("CIRCLE_BRANCH") == "main") and + (not os.environ.get("CIRCLE_PULL_REQUEST", "").startswith( + "https://github.com/matplotlib/matplotlib/pull"))) + + +def _parse_skip_subdirs_file(): + """ + Read .mpl_skip_subdirs.yaml for subdirectories to not + build if we do `make html-skip-subdirs`. Subdirectories + are relative to the toplevel directory. Note that you + cannot skip 'users' as it contains the table of contents, + but you can skip subdirectories of 'users'. Doing this + can make partial builds very fast. + """ + default_skip_subdirs = [ + 'release/prev_whats_new/*', 'users/explain/*', 'api/*', 'gallery/*', + 'tutorials/*', 'plot_types/*', 'devel/*'] + try: + with open(".mpl_skip_subdirs.yaml", 'r') as fin: + print('Reading subdirectories to skip from', + '.mpl_skip_subdirs.yaml') + out = yaml.full_load(fin) + return out['skip_subdirs'] + except FileNotFoundError: + # make a default: + with open(".mpl_skip_subdirs.yaml", 'w') as fout: + yamldict = {'skip_subdirs': default_skip_subdirs, + 'comment': 'For use with make html-skip-subdirs'} + yaml.dump(yamldict, fout) + print('Skipping subdirectories, but .mpl_skip_subdirs.yaml', + 'not found so creating a default one. Edit this file', + 'to customize which directories are included in build.') + + return default_skip_subdirs + + +skip_subdirs = [] +# triggered via make html-skip-subdirs +if 'skip_sub_dirs=1' in sys.argv: + skip_subdirs = _parse_skip_subdirs_file() + +# Parse year using SOURCE_DATE_EPOCH, falling back to current time. +# https://reproducible-builds.org/specs/source-date-epoch/ +sourceyear = datetime.fromtimestamp( + int(os.environ.get('SOURCE_DATE_EPOCH', time.time())), timezone.utc).year # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. sys.path.append(os.path.abspath('.')) +sys.path.append('.') # 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 = ['matplotlib.sphinxext.mathmpl', 'sphinxext.math_symbol_table', - 'sphinx.ext.autodoc', 'matplotlib.sphinxext.only_directives', - 'sphinx.ext.doctest', 'sphinx.ext.autosummary', - 'matplotlib.sphinxext.plot_directive', - 'sphinx.ext.inheritance_diagram', - 'sphinxext.gen_gallery', 'sphinxext.gen_rst', - 'sphinxext.github', - 'numpydoc'] +# Unless we catch the warning explicitly somewhere, a warning should cause the +# docs build to fail. This is especially useful for getting rid of deprecated +# usage in the gallery. +warnings.filterwarnings('error', append=True) + +# Warnings for missing glyphs occur during `savefig`, and would cause any such plot to +# not be created. Because the exception occurs in savefig, there is no way for the plot +# itself to ignore these warnings locally, so we must do so globally. +warnings.filterwarnings('default', category=UserWarning, + message=r'Glyph \d+ \(.+\) missing from font\(s\)') +warnings.filterwarnings('default', category=UserWarning, + message=r'Matplotlib currently does not support .+ natively\.') + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.graphviz', + 'sphinx.ext.inheritance_diagram', + 'sphinx.ext.intersphinx', + 'sphinx.ext.ifconfig', + 'IPython.sphinxext.ipython_console_highlighting', + 'IPython.sphinxext.ipython_directive', + 'numpydoc', # Needs to be loaded *after* autodoc. + 'sphinx_gallery.gen_gallery', + 'matplotlib.sphinxext.mathmpl', + 'matplotlib.sphinxext.plot_directive', + 'matplotlib.sphinxext.roles', + 'matplotlib.sphinxext.figmpl_directive', + 'sphinxcontrib.inkscapeconverter', + 'sphinxext.github', + 'sphinxext.math_symbol_table', + 'sphinxext.missing_references', + 'sphinxext.mock_gui_toolkits', + 'sphinxext.skip_deprecated', + 'sphinxext.redirect_from', + 'sphinx_copybutton', + 'sphinx_design', + 'sphinx_tags', +] -exclude_patterns = ['api/api_changes/*', 'users/whats_new/*'] +exclude_patterns = [ + 'api/prev_api_changes/api_changes_*/*', + '**/*inc.rst', + 'users/explain/index.rst' # Page has no content, but required by sphinx gallery +] -# Use IPython's console highlighting by default -try: - from IPython.sphinxext import ipython_console_highlighting -except ImportError: - raise ImportError( - "IPython must be installed to build the matplotlib docs") +exclude_patterns += skip_subdirs + + +def _check_dependencies(): + names = { + **{ext: ext.split(".")[0] for ext in extensions}, + # Explicitly list deps that are not extensions, or whose PyPI package + # name does not match the (toplevel) module name. + "colorspacious": 'colorspacious', + "mpl_sphinx_theme": 'mpl_sphinx_theme', + "sphinxcontrib.inkscapeconverter": 'sphinxcontrib-svg2pdfconverter', + } + missing = [] + for name in names: + try: + __import__(name) + except ImportError: + missing.append(names[name]) + if missing: + raise ImportError( + "The following dependencies are missing to build the " + f"documentation: {', '.join(missing)}") + + # debug sphinx-pydata-theme and mpl-theme-version + if 'mpl_sphinx_theme' not in missing: + import pydata_sphinx_theme + import mpl_sphinx_theme + print(f"pydata sphinx theme: {pydata_sphinx_theme.__version__}") + print(f"mpl sphinx theme: {mpl_sphinx_theme.__version__}") + + if shutil.which('dot') is None: + raise OSError( + "No binary named dot - graphviz must be installed to build the " + "documentation") + if shutil.which('latex') is None: + raise OSError( + "No binary named latex - a LaTeX distribution must be installed to build " + "the documentation") + +_check_dependencies() + + +# Import only after checking for dependencies. +import sphinx_gallery + +if parse_version(sphinx_gallery.__version__) >= parse_version('0.16.0'): + gallery_order_sectionorder = 'sphinxext.gallery_order.sectionorder' + gallery_order_subsectionorder = 'sphinxext.gallery_order.subsectionorder' + clear_basic_units = 'sphinxext.util.clear_basic_units' + matplotlib_reduced_latex_scraper = 'sphinxext.util.matplotlib_reduced_latex_scraper' +else: + # gallery_order.py from the sphinxext folder provides the classes that + # allow custom ordering of sections and subsections of the gallery + from sphinxext.gallery_order import ( + sectionorder as gallery_order_sectionorder, + subsectionorder as gallery_order_subsectionorder) + from sphinxext.util import clear_basic_units, matplotlib_reduced_latex_scraper + +if parse_version(sphinx_gallery.__version__) >= parse_version('0.17.0'): + sg_matplotlib_animations = (True, 'mp4') else: - extensions.append('IPython.sphinxext.ipython_console_highlighting') - extensions.append('IPython.sphinxext.ipython_directive') + sg_matplotlib_animations = True -try: - import numpydoc -except ImportError: - raise ImportError("No module named numpydoc - you need to install " - "numpydoc to build the documentation.") -try: - import colorspacious -except ImportError: - raise ImportError("No module named colorspacious - you need to install " - "colorspacious to build the documentation") +# Prevent plt.show() from emitting a non-GUI backend warning. +warnings.filterwarnings('ignore', category=UserWarning, + message=r'(\n|.)*is non-interactive, and thus cannot be shown') -try: - from unittest.mock import MagicMock -except ImportError: - try: - from mock import MagicMock - except ImportError: - raise ImportError("No module named mock - you need to install " - "mock to build the documentation") -try: - import matplotlib -except ImportError: - msg = "Error: matplotlib must be installed before building the documentation" - sys.exit(msg) +# hack to catch sphinx-gallery 17.0 warnings +def tutorials_download_error(record): + if re.match("download file not readable: .*tutorials_(python|jupyter).zip", + record.msg): + return False + +logger = logging.getLogger('sphinx') +logger.addFilter(tutorials_download_error) autosummary_generate = True +autodoc_typehints = "none" +autodoc_mock_imports = ["pytest"] + +# we should ignore warnings coming from importing deprecated modules for +# autodoc purposes, as this will disappear automatically when they are removed +warnings.filterwarnings('ignore', category=DeprecationWarning, + module='importlib', # used by sphinx.autodoc.importer + message=r'(\n|.)*module was deprecated.*') autodoc_docstring_signature = True +autodoc_default_options = {'members': None, 'undoc-members': None} + + +def autodoc_process_bases(app, name, obj, options, bases): + """ + Hide pybind11 base object from inheritance tree. + + Note, *bases* must be modified in place. + """ + for cls in bases[:]: + if not isinstance(cls, type): + continue + if cls.__module__ == 'pybind11_builtins' and cls.__name__ == 'pybind11_object': + bases.remove(cls) + + +# make sure to ignore warnings that stem from simply inspecting deprecated +# class-level attributes +warnings.filterwarnings('ignore', category=DeprecationWarning, + module='sphinx.util.inspect') + +nitpicky = True +# change this to True to update the allowed failures +missing_references_write_json = False +missing_references_warn_unused_ignores = False + + +intersphinx_mapping = { + 'Pillow': ('https://pillow.readthedocs.io/en/stable/', None), + 'cycler': ('https://matplotlib.org/cycler/', None), + 'dateutil': ('https://dateutil.readthedocs.io/en/stable/', None), + 'ipykernel': ('https://ipykernel.readthedocs.io/en/latest/', None), + 'numpy': ('https://numpy.org/doc/stable/', None), + 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), + 'pytest': ('https://pytest.org/en/stable/', None), + 'python': ('https://docs.python.org/3/', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), + 'tornado': ('https://www.tornadoweb.org/en/stable/', None), + 'xarray': ('https://docs.xarray.dev/en/stable/', None), + 'meson-python': ('https://mesonbuild.com/meson-python/', None), + 'pip': ('https://pip.pypa.io/en/stable/', None), +} + + +gallery_dirs = [f'{ed}' for ed in + ['gallery', 'tutorials', 'plot_types', 'users/explain'] + if f'{ed}/*' not in skip_subdirs] + +example_dirs = [] +for gd in gallery_dirs: + gd = gd.replace('gallery', 'examples').replace('users/explain', 'users_explain') + example_dirs += [f'../galleries/{gd}'] + +sphinx_gallery_conf = { + 'backreferences_dir': Path('api', '_as_gen'), + # Compression is a significant effort that we skip for local and CI builds. + 'compress_images': ('thumbnails', 'images') if is_release_build else (), + 'doc_module': ('matplotlib', 'mpl_toolkits'), + 'examples_dirs': example_dirs, + 'filename_pattern': '^((?!sgskip).)*$', + 'gallery_dirs': gallery_dirs, + 'image_scrapers': (matplotlib_reduced_latex_scraper, ), + 'image_srcset': ["2x"], + 'junit': '../test-results/sphinx-gallery/junit.xml' if CIRCLECI else '', + 'matplotlib_animations': sg_matplotlib_animations, + 'min_reported_time': 1, + 'plot_gallery': 'True', # sphinx-gallery/913 + 'reference_url': {'matplotlib': None, 'mpl_toolkits': None}, + 'prefer_full_module': {r'mpl_toolkits\.'}, + 'remove_config_comments': True, + 'reset_modules': ('matplotlib', clear_basic_units, 'sphinxext.util.patch_header'), + 'subsection_order': gallery_order_sectionorder, + 'thumbnail_size': (320, 224), + 'within_subsection_order': gallery_order_subsectionorder, + 'capture_repr': (), + 'copyfile_regex': r'.*\.rst', +} + +if parse_version(sphinx_gallery.__version__) >= parse_version('0.17.0'): + sphinx_gallery_conf['parallel'] = True + # Any warnings from joblib turned into errors may cause a deadlock. + warnings.filterwarnings('default', category=UserWarning, module='joblib') + +if 'plot_gallery=0' in sys.argv: + # Gallery images are not created. Suppress warnings triggered where other + # parts of the documentation link to these images. + + def gallery_image_warning_filter(record): + msg = record.msg + for pattern in (sphinx_gallery_conf['gallery_dirs'] + + ['_static/constrained_layout']): + if msg.startswith(f'image file not readable: {pattern}'): + return False + + if msg == 'Could not obtain image size. :scale: option is ignored.': + return False + + return True + + logger = logging.getLogger('sphinx') + logger.addFilter(gallery_image_warning_filter) + +# Sphinx tags configuration +tags_create_tags = True +tags_page_title = "All tags" +tags_create_badges = True +tags_badge_colors = { + "animation": "primary", + "component:*": "secondary", + "event-handling": "success", + "interactivity:*": "dark", + "plot-type:*": "danger", + "*": "light" # default value +} + +mathmpl_fontsize = 11.0 +mathmpl_srcset = ['2x'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -87,14 +362,30 @@ # This is the default encoding, but it doesn't hurt to be explicit source_encoding = "utf-8" -# The master toctree document. -master_doc = 'contents' +# The toplevel toctree document. +root_doc = 'index' # General substitutions. +try: + SHA = subprocess.check_output( + ['git', 'describe', '--dirty']).decode('utf-8').strip() +# Catch the case where git is not installed locally, and use the setuptools_scm +# version number instead +except (subprocess.CalledProcessError, FileNotFoundError): + SHA = matplotlib.__version__ + + +html_context = { + "doc_version": SHA, +} + project = 'Matplotlib' -copyright = ('2002 - 2012 John Hunter, Darren Dale, Eric Firing, ' - 'Michael Droettboom and the matplotlib development ' - 'team; 2012 - 2016 The matplotlib development team') +copyright = ( + '2002–2012 John Hunter, Darren Dale, Eric Firing, Michael Droettboom ' + 'and the Matplotlib development team; ' + f'2012–{sourceyear} The Matplotlib development team' +) + # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. @@ -107,7 +398,7 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' @@ -115,15 +406,15 @@ unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -133,54 +424,117 @@ # Plot directive configuration # ---------------------------- -plot_formats = [('png', 100), ('pdf', 100)] - -# Subdirectories in 'examples/' directory of package and titles for gallery -mpl_example_sections = [ - ('lines_bars_and_markers', 'Lines, bars, and markers'), - ('shapes_and_collections', 'Shapes and collections'), - ('statistics', 'Statistical plots'), - ('images_contours_and_fields', 'Images, contours, and fields'), - ('pie_and_polar_charts', 'Pie and polar charts'), - ('color', 'Color'), - ('text_labels_and_annotations', 'Text, labels, and annotations'), - ('ticks_and_spines', 'Ticks and spines'), - ('scales', 'Axis scales'), - ('subplots_axes_and_figures', 'Subplots, axes, and figures'), - ('style_sheets', 'Style sheets'), - ('specialty_plots', 'Specialty plots'), - ('showcase', 'Showcase'), - ('api', 'API'), - ('pylab_examples', 'pylab examples'), - ('mplot3d', 'mplot3d toolkit'), - ('axes_grid1', 'axes_grid1 toolkit'), - ('axisartist', 'axisartist toolkit'), - ('units', 'units'), - ('widgets', 'widgets'), - ('misc', 'Miscellaneous examples'), - ] - - -# Github extension - -github_project_url = "http://github.com/matplotlib/matplotlib/" +# For speedup, decide which plot_formats to build based on build targets: +# html only -> png +# latex only -> pdf +# all other cases, including html + latex -> png, pdf +# For simplicity, we assume that the build targets appear in the command line. +# We're falling back on using all formats in case that assumption fails. +formats = {'html': ('png', 100), 'latex': ('pdf', 100)} +plot_formats = [formats[target] for target in ['html', 'latex'] + if target in sys.argv] or list(formats.values()) +# make 2x images for srcset argument to +plot_srcset = ['2x'] + +# GitHub extension + +github_project_url = "https://github.com/matplotlib/matplotlib/" + # Options for HTML output # ----------------------- +def add_html_cache_busting(app, pagename, templatename, context, doctree): + """ + Add cache busting query on CSS and JavaScript assets. + + This adds the Matplotlib version as a query to the link reference in the + HTML, if the path is not absolute (i.e., it comes from the `_static` + directory) and doesn't already have a query. + + .. note:: Sphinx 7.1 provides asset checksums; so this hook only runs on + Sphinx 7.0 and earlier. + """ + from sphinx.builders.html import Stylesheet, JavaScript + + css_tag = context['css_tag'] + js_tag = context['js_tag'] + + def css_tag_with_cache_busting(css): + if isinstance(css, Stylesheet) and css.filename is not None: + url = urlsplit(css.filename) + if not url.netloc and not url.query: + url = url._replace(query=SHA) + css = Stylesheet(urlunsplit(url), priority=css.priority, + **css.attributes) + return css_tag(css) + + def js_tag_with_cache_busting(js): + if isinstance(js, JavaScript) and js.filename is not None: + url = urlsplit(js.filename) + if not url.netloc and not url.query: + url = url._replace(query=SHA) + js = JavaScript(urlunsplit(url), priority=js.priority, + **js.attributes) + return js_tag(js) + + context['css_tag'] = css_tag_with_cache_busting + context['js_tag'] = js_tag_with_cache_busting + + # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. -#html_style = 'matplotlib.css' -html_style = 'mpl.css' +html_css_files = [ + "mpl.css", +] + +html_theme = "mpl_sphinx_theme" # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. -#html_logo = 'logo.png' +html_theme_options = { + "navbar_links": "internal", + # collapse_navigation in pydata-sphinx-theme is slow, so skipped for local + # and CI builds https://github.com/pydata/pydata-sphinx-theme/pull/386 + "collapse_navigation": not is_release_build, + "show_prev_next": False, + "switcher": { + # Add a unique query to the switcher.json url. This will be ignored by + # the server, but will be used as part of the key for caching by browsers + # so when we do a new meso release the switcher will update "promptly" on + # the stable and devdocs. + "json_url": ( + "https://output.circle-artifacts.com/output/job/" + f"{os.environ['CIRCLE_WORKFLOW_JOB_ID']}/artifacts/" + f"{os.environ['CIRCLE_NODE_INDEX']}" + "/doc/build/html/_static/switcher.json" if CIRCLECI and not DEVDOCS else + f"https://matplotlib.org/devdocs/_static/switcher.json?{SHA}" + ), + "version_match": ( + matplotlib.__version__ + if matplotlib.__version_info__.releaselevel == 'final' + else 'dev') + }, + "navbar_end": ["theme-switcher", "version-switcher", "mpl_icon_links"], + "navbar_persistent": ["search-button"], + "footer_start": ["copyright", "sphinx-version", "doc_version"], + # We override the announcement template from pydata-sphinx-theme, where + # this special value indicates the use of the unreleased banner. If we need + # an actual announcement, then just place the text here as usual. + "announcement": "unreleased" if not is_release_build else "", + "show_version_warning_banner": True, +} +include_analytics = is_release_build +if include_analytics: + html_theme_options["analytics"] = { + "plausible_analytics_domain": "matplotlib.org", + "plausible_analytics_url": "https://views.scientific-python.org/js/script.js" + } # 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, @@ -191,48 +545,60 @@ # default is ``".html"``. html_file_suffix = '.html' +# this makes this the canonical link for all the pages on the site... +html_baseurl = 'https://matplotlib.org/stable/' + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - # Content template for the index page. html_index = 'index.html' # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Custom sidebar templates, maps page names to templates. html_sidebars = { - 'index': ['badgesidebar.html','donate_sidebar.html', - 'indexsidebar.html', 'searchbox.html'], - '**': ['badgesidebar.html', 'localtoc.html', - 'relations.html', 'sourcelink.html', 'searchbox.html'] + "index": [ + # 'sidebar_announcement.html', + "cheatsheet_sidebar.html", + "donate_sidebar.html", + ], + # no sidebar for release notes, because that page is only a collection of links + # to sub-pages. The sidebar would repeat all the titles of the sub-pages and + # thus basically repeat all the content of the page. + "release/release_notes": ["empty_sidebar.html"], + # '**': ['localtoc.html', 'pagesource.html'] } -# Additional templates that should be rendered to pages, maps page names to -# template names. -html_additional_pages = {'index': 'index.html', - 'gallery':'gallery.html', - 'citing': 'citing.html'} +# Don't include link to doc source files +html_show_sourcelink = False -# If false, no module index is generated. -#html_use_modindex = True -html_domain_indices = ["py-modindex"] +# Copies only relevant code, not the '>>>' prompt +copybutton_prompt_text = r'>>> |\.\.\. ' +copybutton_prompt_is_regexp = True + +# If true, add an index to the HTML documents. +html_use_index = False + +# If true, generate domain-specific indices in addition to the general index. +# For e.g. the Python domain, this is the global module index. +html_domain_index = False # If true, the reST sources are included in the HTML build as _sources/. -#html_copy_source = True +# html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. -html_use_opensearch = 'False' +html_use_opensearch = 'https://matplotlib.org/stable' # Output file base name for HTML help builder. htmlhelp_basename = 'Matplotlibdoc' +# Use typographic quote characters. +smartquotes = False + # Path to favicon html_favicon = '_static/favicon.ico' @@ -242,16 +608,15 @@ # The paper size ('letter' or 'a4'). latex_paper_size = 'letter' -# The font size ('10pt', '11pt' or '12pt'). -latex_font_size = '11pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). +# Grouping the document tree into LaTeX files. +# List of tuples: +# (source start file, target name, title, author, +# document class [howto/manual]) latex_documents = [ - ('contents', 'Matplotlib.tex', 'Matplotlib', - 'John Hunter, Darren Dale, Eric Firing, Michael Droettboom and the ' - 'matplotlib development team', 'manual'), + (root_doc, 'Matplotlib.tex', 'Matplotlib', + 'John Hunter\\and Darren Dale\\and Eric Firing\\and Michael Droettboom' + '\\and and the matplotlib development team', 'manual'), ] @@ -259,27 +624,109 @@ # the title page. latex_logo = None +# Use Unicode aware LaTeX engine +latex_engine = 'xelatex' # or 'lualatex' + +latex_elements = {} + +# Keep babel usage also with xelatex (Sphinx default is polyglossia) +# If this key is removed or changed, latex build directory must be cleaned +latex_elements['babel'] = r'\usepackage{babel}' + +# Font configuration +# Fix fontspec converting " into right curly quotes in PDF +# cf https://github.com/sphinx-doc/sphinx/pull/6888/ +latex_elements['fontenc'] = r''' +\usepackage{fontspec} +\defaultfontfeatures[\rmfamily,\sffamily,\ttfamily]{} +''' + +# Sphinx 2.0 adopts GNU FreeFont by default, but it does not have all +# the Unicode codepoints needed for the section about Mathtext +# "Writing mathematical expressions" +latex_elements['fontpkg'] = r""" +\IfFontExistsTF{XITS}{ + \setmainfont{XITS} +}{ + \setmainfont{XITS}[ + Extension = .otf, + UprightFont = *-Regular, + ItalicFont = *-Italic, + BoldFont = *-Bold, + BoldItalicFont = *-BoldItalic, +]} +\IfFontExistsTF{FreeSans}{ + \setsansfont{FreeSans} +}{ + \setsansfont{FreeSans}[ + Extension = .otf, + UprightFont = *, + ItalicFont = *Oblique, + BoldFont = *Bold, + BoldItalicFont = *BoldOblique, +]} +\IfFontExistsTF{FreeMono}{ + \setmonofont{FreeMono} +}{ + \setmonofont{FreeMono}[ + Extension = .otf, + UprightFont = *, + ItalicFont = *Oblique, + BoldFont = *Bold, + BoldItalicFont = *BoldOblique, +]} +% needed for \mathbb (blackboard alphabet) to actually work +\usepackage{unicode-math} +\IfFontExistsTF{XITS Math}{ + \setmathfont{XITS Math} +}{ + \setmathfont{XITSMath-Regular}[ + Extension = .otf, +]} +""" + +# Fix fancyhdr complaining about \headheight being too small +latex_elements['passoptionstopackages'] = r""" + \PassOptionsToPackage{headheight=14pt}{geometry} +""" + # Additional stuff for the LaTeX preamble. -latex_preamble = r""" - % In the parameters section, place a newline after the Parameters - % header. (This is stolen directly from Numpy's conf.py, since it - % affects Numpy-style docstrings). +latex_elements['preamble'] = r""" + % Show Parts and Chapters in Table of Contents + \setcounter{tocdepth}{0} + % One line per author on title page + \DeclareRobustCommand{\and}% + {\end{tabular}\kern-\tabcolsep\\\begin{tabular}[t]{c}}% + \usepackage{etoolbox} + \AtBeginEnvironment{sphinxthebibliography}{\appendix\part{Appendices}} \usepackage{expdlist} \let\latexdescription=\description \def\description{\latexdescription{}{} \breaklabel} - - \usepackage{amsmath} - \usepackage{amsfonts} - \usepackage{amssymb} - \usepackage{txfonts} - - % The enumitem package provides unlimited nesting of lists and - % enums. Sphinx may use this in the future, in which case this can - % be removed. See - % https://bitbucket.org/birkenfeld/sphinx/issue/777/latex-output-too-deeply-nested - \usepackage{enumitem} - \setlistdepth{2048} + % But expdlist old LaTeX package requires fixes: + % 1) remove extra space + \makeatletter + \patchcmd\@item{{\@breaklabel} }{{\@breaklabel}}{}{} + \makeatother + % 2) fix bug in expdlist's way of breaking the line after long item label + \makeatletter + \def\breaklabel{% + \def\@breaklabel{% + \leavevmode\par + % now a hack because Sphinx inserts \leavevmode after term node + \def\leavevmode{\def\leavevmode{\unhbox\voidb@x}}% + }% + } + \makeatother """ +# Sphinx 1.5 provides this to avoid "too deeply nested" LaTeX error +# and usage of "enumitem" LaTeX package is unneeded. +# Value can be increased but do not set it to something such as 2048 +# which needlessly would trigger creation of thousands of TeX macros +latex_elements['maxlistdepth'] = '10' +latex_elements['pointsize'] = '11pt' + +# Better looking general index in PDF +latex_elements['printindex'] = r'\footnotesize\raggedright\printindex' # Documents to append as an appendix to all manuals. latex_appendices = [] @@ -287,162 +734,168 @@ # If false, no module index is generated. latex_use_modindex = True -latex_use_parts = True +latex_toplevel_sectioning = 'part' # Show both class-level docstring and __init__ docstring in class # documentation autoclass_content = 'both' -rst_epilog = """ -.. |minimum_numpy_version| replace:: %s -""" % matplotlib.__version__numpy__ - texinfo_documents = [ - ("contents", 'matplotlib', 'Matplotlib Documentation', + (root_doc, 'matplotlib', 'Matplotlib Documentation', 'John Hunter@*Darren Dale@*Eric Firing@*Michael Droettboom@*' 'The matplotlib development team', 'Matplotlib', "Python plotting package", 'Programming', 1), ] +# numpydoc config -class MyWX(MagicMock): - class Panel(object): - pass - - class ToolBar(object): - pass - - class Frame(object): - pass - - VERSION_STRING = '2.8.12' - - -class MyPyQt4(MagicMock): - class QtGui(object): - # PyQt4.QtGui public classes. - # Generated with - # textwrap.fill([name for name in dir(PyQt4.QtGui) - # if isinstance(getattr(PyQt4.QtGui, name), type)]) - _QtGui_public_classes = """\ - Display QAbstractButton QAbstractGraphicsShapeItem - QAbstractItemDelegate QAbstractItemView QAbstractPrintDialog - QAbstractProxyModel QAbstractScrollArea QAbstractSlider - QAbstractSpinBox QAbstractTextDocumentLayout QAction QActionEvent - QActionGroup QApplication QBitmap QBoxLayout QBrush QButtonGroup - QCalendarWidget QCheckBox QClipboard QCloseEvent QColor QColorDialog - QColumnView QComboBox QCommandLinkButton QCommonStyle QCompleter - QConicalGradient QContextMenuEvent QCursor QDataWidgetMapper QDateEdit - QDateTimeEdit QDesktopServices QDesktopWidget QDial QDialog - QDialogButtonBox QDirModel QDockWidget QDoubleSpinBox QDoubleValidator - QDrag QDragEnterEvent QDragLeaveEvent QDragMoveEvent QDropEvent - QErrorMessage QFileDialog QFileIconProvider QFileOpenEvent - QFileSystemModel QFocusEvent QFocusFrame QFont QFontComboBox - QFontDatabase QFontDialog QFontInfo QFontMetrics QFontMetricsF - QFormLayout QFrame QGesture QGestureEvent QGestureRecognizer QGlyphRun - QGradient QGraphicsAnchor QGraphicsAnchorLayout QGraphicsBlurEffect - QGraphicsColorizeEffect QGraphicsDropShadowEffect QGraphicsEffect - QGraphicsEllipseItem QGraphicsGridLayout QGraphicsItem - QGraphicsItemAnimation QGraphicsItemGroup QGraphicsLayout - QGraphicsLayoutItem QGraphicsLineItem QGraphicsLinearLayout - QGraphicsObject QGraphicsOpacityEffect QGraphicsPathItem - QGraphicsPixmapItem QGraphicsPolygonItem QGraphicsProxyWidget - QGraphicsRectItem QGraphicsRotation QGraphicsScale QGraphicsScene - QGraphicsSceneContextMenuEvent QGraphicsSceneDragDropEvent - QGraphicsSceneEvent QGraphicsSceneHelpEvent QGraphicsSceneHoverEvent - QGraphicsSceneMouseEvent QGraphicsSceneMoveEvent - QGraphicsSceneResizeEvent QGraphicsSceneWheelEvent - QGraphicsSimpleTextItem QGraphicsTextItem QGraphicsTransform - QGraphicsView QGraphicsWidget QGridLayout QGroupBox QHBoxLayout - QHeaderView QHelpEvent QHideEvent QHoverEvent QIcon QIconDragEvent - QIconEngine QIconEngineV2 QIdentityProxyModel QImage QImageIOHandler - QImageReader QImageWriter QInputContext QInputContextFactory - QInputDialog QInputEvent QInputMethodEvent QIntValidator QItemDelegate - QItemEditorCreatorBase QItemEditorFactory QItemSelection - QItemSelectionModel QItemSelectionRange QKeyEvent QKeyEventTransition - QKeySequence QLCDNumber QLabel QLayout QLayoutItem QLineEdit - QLinearGradient QListView QListWidget QListWidgetItem QMainWindow - QMatrix QMatrix2x2 QMatrix2x3 QMatrix2x4 QMatrix3x2 QMatrix3x3 - QMatrix3x4 QMatrix4x2 QMatrix4x3 QMatrix4x4 QMdiArea QMdiSubWindow - QMenu QMenuBar QMessageBox QMimeSource QMouseEvent - QMouseEventTransition QMoveEvent QMovie QPageSetupDialog QPaintDevice - QPaintEngine QPaintEngineState QPaintEvent QPainter QPainterPath - QPainterPathStroker QPalette QPanGesture QPen QPicture QPictureIO - QPinchGesture QPixmap QPixmapCache QPlainTextDocumentLayout - QPlainTextEdit QPolygon QPolygonF QPrintDialog QPrintEngine - QPrintPreviewDialog QPrintPreviewWidget QPrinter QPrinterInfo - QProgressBar QProgressDialog QProxyModel QPushButton QPyTextObject - QQuaternion QRadialGradient QRadioButton QRawFont QRegExpValidator - QRegion QResizeEvent QRubberBand QScrollArea QScrollBar - QSessionManager QShortcut QShortcutEvent QShowEvent QSizeGrip - QSizePolicy QSlider QSortFilterProxyModel QSound QSpacerItem QSpinBox - QSplashScreen QSplitter QSplitterHandle QStackedLayout QStackedWidget - QStandardItem QStandardItemModel QStaticText QStatusBar - QStatusTipEvent QStringListModel QStyle QStyleFactory QStyleHintReturn - QStyleHintReturnMask QStyleHintReturnVariant QStyleOption - QStyleOptionButton QStyleOptionComboBox QStyleOptionComplex - QStyleOptionDockWidget QStyleOptionDockWidgetV2 QStyleOptionFocusRect - QStyleOptionFrame QStyleOptionFrameV2 QStyleOptionFrameV3 - QStyleOptionGraphicsItem QStyleOptionGroupBox QStyleOptionHeader - QStyleOptionMenuItem QStyleOptionProgressBar QStyleOptionProgressBarV2 - QStyleOptionRubberBand QStyleOptionSizeGrip QStyleOptionSlider - QStyleOptionSpinBox QStyleOptionTab QStyleOptionTabBarBase - QStyleOptionTabBarBaseV2 QStyleOptionTabV2 QStyleOptionTabV3 - QStyleOptionTabWidgetFrame QStyleOptionTabWidgetFrameV2 - QStyleOptionTitleBar QStyleOptionToolBar QStyleOptionToolBox - QStyleOptionToolBoxV2 QStyleOptionToolButton QStyleOptionViewItem - QStyleOptionViewItemV2 QStyleOptionViewItemV3 QStyleOptionViewItemV4 - QStylePainter QStyledItemDelegate QSwipeGesture QSyntaxHighlighter - QSystemTrayIcon QTabBar QTabWidget QTableView QTableWidget - QTableWidgetItem QTableWidgetSelectionRange QTabletEvent - QTapAndHoldGesture QTapGesture QTextBlock QTextBlockFormat - QTextBlockGroup QTextBlockUserData QTextBrowser QTextCharFormat - QTextCursor QTextDocument QTextDocumentFragment QTextDocumentWriter - QTextEdit QTextFormat QTextFragment QTextFrame QTextFrameFormat - QTextImageFormat QTextInlineObject QTextItem QTextLayout QTextLength - QTextLine QTextList QTextListFormat QTextObject QTextObjectInterface - QTextOption QTextTable QTextTableCell QTextTableCellFormat - QTextTableFormat QTimeEdit QToolBar QToolBox QToolButton QToolTip - QTouchEvent QTransform QTreeView QTreeWidget QTreeWidgetItem - QTreeWidgetItemIterator QUndoCommand QUndoGroup QUndoStack QUndoView - QVBoxLayout QValidator QVector2D QVector3D QVector4D QWhatsThis - QWhatsThisClickedEvent QWheelEvent QWidget QWidgetAction QWidgetItem - QWindowStateChangeEvent QWizard QWizardPage QWorkspace - QX11EmbedContainer QX11EmbedWidget QX11Info - """ - for _name in _QtGui_public_classes.split(): - locals()[_name] = type(_name, (), {}) - del _name - - -class MySip(MagicMock): - def getapi(*args): - return 1 - +numpydoc_show_class_members = False -mockwxversion = MagicMock() -mockwx = MyWX() -mocksip = MySip() -mockpyqt4 = MyPyQt4() -sys.modules['wxversion'] = mockwxversion -sys.modules['wx'] = mockwx -sys.modules['sip'] = mocksip -sys.modules['PyQt4'] = mockpyqt4 +# We want to prevent any size limit, as we'll add scroll bars with CSS. +inheritance_graph_attrs = dict(size='1000.0', splines='polyline') +# Also remove minimum node dimensions, and increase line size a bit. +inheritance_node_attrs = dict(height=0.02, margin=0.055, penwidth=1, + width=0.01) +inheritance_edge_attrs = dict(penwidth=1) -# numpydoc config +graphviz_dot = shutil.which('dot') +graphviz_output_format = 'svg' -numpydoc_show_class_members = False +# ----------------------------------------------------------------------------- +# Source code links +# ----------------------------------------------------------------------------- +link_github = True +# You can add build old with link_github = False -# Skip deprecated members +if link_github: + import inspect -def skip_deprecated(app, what, name, obj, skip, options): - if skip: - return skip - skipped = {"matplotlib.colors": ["ColorConverter", "hex2color", "rgb2hex"]} - skip_list = skipped.get(getattr(obj, "__module__", None)) - if skip_list is not None: - return getattr(obj, "__name__", None) in skip_list + extensions.append('sphinx.ext.linkcode') + def linkcode_resolve(domain, info): + """ + Determine the URL corresponding to Python object + """ + if domain != 'py': + return None + + modname = info['module'] + fullname = info['fullname'] + + submod = sys.modules.get(modname) + if submod is None: + return None + + obj = submod + for part in fullname.split('.'): + try: + obj = getattr(obj, part) + except AttributeError: + return None + + if inspect.isfunction(obj): + obj = inspect.unwrap(obj) + try: + fn = inspect.getsourcefile(obj) + except TypeError: + fn = None + if not fn or fn.endswith('__init__.py'): + try: + fn = inspect.getsourcefile(sys.modules[obj.__module__]) + except (TypeError, AttributeError, KeyError): + fn = None + if not fn: + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except (OSError, TypeError): + lineno = None + + linespec = (f"#L{lineno:d}-L{lineno + len(source) - 1:d}" + if lineno else "") + + startdir = Path(matplotlib.__file__).parent.parent + try: + fn = os.path.relpath(fn, start=startdir).replace(os.path.sep, '/') + except ValueError: + return None + + if not fn.startswith(('matplotlib/', 'mpl_toolkits/')): + return None + + version = parse_version(matplotlib.__version__) + tag = 'main' if version.is_devrelease else f'v{version.public}' + return ("https://github.com/matplotlib/matplotlib/blob" + f"/{tag}/lib/{fn}{linespec}") +else: + extensions.append('sphinx.ext.viewcode') + + +def generate_ScalarMappable_docs(): + + import matplotlib.colorizer + from numpydoc.docscrape_sphinx import get_doc_object + from pathlib import Path + import textwrap + from sphinx.util.inspect import stringify_signature + target_file = Path(__file__).parent / 'api' / 'scalarmappable.gen_rst' + with open(target_file, 'w') as fout: + fout.write(""" +.. class:: ScalarMappable(colorizer, **kwargs) + :canonical: matplotlib.colorizer._ScalarMappable + +""") + for meth in [ + matplotlib.colorizer._ScalarMappable.autoscale, + matplotlib.colorizer._ScalarMappable.autoscale_None, + matplotlib.colorizer._ScalarMappable.changed, + """ + .. attribute:: colorbar + + The last colorbar associated with this ScalarMappable. May be None. +""", + matplotlib.colorizer._ScalarMappable.get_alpha, + matplotlib.colorizer._ScalarMappable.get_array, + matplotlib.colorizer._ScalarMappable.get_clim, + matplotlib.colorizer._ScalarMappable.get_cmap, + """ + .. property:: norm +""", + matplotlib.colorizer._ScalarMappable.set_array, + matplotlib.colorizer._ScalarMappable.set_clim, + matplotlib.colorizer._ScalarMappable.set_cmap, + matplotlib.colorizer._ScalarMappable.set_norm, + matplotlib.colorizer._ScalarMappable.to_rgba, + ]: + if isinstance(meth, str): + fout.write(meth) + else: + name = meth.__name__ + sig = stringify_signature(inspect.signature(meth)) + docstring = textwrap.indent( + str(get_doc_object(meth)), + ' ' + ).rstrip() + fout.write(f""" + .. method:: {name}{sig} +{docstring} + +""") + + +# ----------------------------------------------------------------------------- +# Sphinx setup +# ----------------------------------------------------------------------------- def setup(app): - app.connect('autodoc-skip-member', skip_deprecated) + if any(st in version for st in ('post', 'dev', 'alpha', 'beta')): + bld_type = 'dev' + else: + bld_type = 'rel' + app.add_config_value('skip_sub_dirs', 0, '') + app.add_config_value('releaselevel', bld_type, 'env') + app.connect('autodoc-process-bases', autodoc_process_bases) + if sphinx.version_info[:2] < (7, 1): + app.connect('html-page-context', add_html_cache_busting, priority=1000) + generate_ScalarMappable_docs() diff --git a/doc/contents.rst b/doc/contents.rst deleted file mode 100644 index 90265fed09bd..000000000000 --- a/doc/contents.rst +++ /dev/null @@ -1,29 +0,0 @@ - - -Overview -======== - -.. htmlonly:: - - :Release: |version| - :Date: |today| - - -.. toctree:: - :maxdepth: 2 - - users/index.rst - api/index.rst - faq/index.rst - mpl_toolkits/index.rst - resources/index.rst - thirdpartypackages/index.rst - devel/index.rst - examples/index.rst - glossary/index.rst - -.. htmlonly:: - - * :ref:`genindex` - * :ref:`modindex` - * :ref:`search` diff --git a/doc/devel/MEP/MEP08.rst b/doc/devel/MEP/MEP08.rst index 072a279eacf9..18419ac2bf11 100644 --- a/doc/devel/MEP/MEP08.rst +++ b/doc/devel/MEP/MEP08.rst @@ -9,7 +9,12 @@ Status ====== -**Discussion** +**Superseded** + +Current guidelines for style, including usage of pep8 are maintained +in `our pull request guidelines `_. + +We are currently enforcing a sub-set of pep8 on new code contributions. Branches and Pull requests ========================== diff --git a/doc/devel/MEP/MEP09.rst b/doc/devel/MEP/MEP09.rst index b7e0ca7a39b7..51ac47ca2c79 100644 --- a/doc/devel/MEP/MEP09.rst +++ b/doc/devel/MEP/MEP09.rst @@ -11,6 +11,7 @@ by the user. Status ====== + **Discussion** Branches and Pull requests diff --git a/doc/devel/MEP/MEP10.rst b/doc/devel/MEP/MEP10.rst index dc00e0fb9c0b..2b39959eaca7 100644 --- a/doc/devel/MEP/MEP10.rst +++ b/doc/devel/MEP/MEP10.rst @@ -9,14 +9,11 @@ Status **Progress** -Targeted for 1.3 +This is still an on-going effort Branches and Pull requests ========================== -#1665 -#1757 -#1795 Abstract ======== @@ -33,7 +30,7 @@ There should instead be short summary tables that link to detailed documentation. In addition, some of the docstrings themselves are quite long and contain redundant information. -Building the documentation takes a long time and uses a `make.py` +Building the documentation takes a long time and uses a :file:`make.py` script rather than a Makefile. Detailed description @@ -47,8 +44,7 @@ these new features. Numpy docstring format ---------------------- -`Numpy docstring format -`_: +`Numpy docstring format `_: This format divides the docstring into clear sections, each having different parsing rules that make the docstring easy to read both as raw text and as HTML. We could consider alternatives, or invent our @@ -88,7 +84,7 @@ reason, many matplotlib methods include something like:: """ This can't be parsed by Sphinx, and is rather verbose in raw text. As -of Sphinx 1.1, if the `autodoc_docstring_signature` config value is +of Sphinx 1.1, if the ``autodoc_docstring_signature`` config value is set to True, Sphinx will extract a replacement signature from the first line of the docstring, allowing this:: @@ -122,7 +118,7 @@ autosummary extension The Sphinx autosummary extension should be used to generate summary tables, that link to separate pages of documentation. Some classes -that have many methods (e.g. `Axes.axes`) should be documented with +that have many methods (e.g. `~.axes.Axes`) should be documented with one method per page, whereas smaller classes should have all of their methods together. @@ -136,8 +132,8 @@ that docstring in the parsed content on the example page. These docstrings could easily include references to any other part of the documentation. -Documentation using help() vs a browser ---------------------------------------- +Documentation using help() vs. a browser +---------------------------------------- Using Sphinx markup in the source allows for good-looking docs in your browser, but the markup also makes the raw text returned using help() @@ -162,21 +158,21 @@ Implementation should be divided on a per-module basis so no single developer is over-burdened by it. -3. Reorganize the API docs using autosummary and `sphinx-autogen`. +3. Reorganize the API docs using autosummary and ``sphinx-autogen``. This should hopefully have minimal impact on the narrative documentation. -4. Modify the example page generator (`gen_rst.py`) so that it +4. Modify the example page generator (:file:`gen_rst.py`) so that it extracts the module docstring from the example and includes it in a non-literal part of the example page. -5. Use `sphinx-quickstart` to generate a new-style Sphinx Makefile. - The following features in the current `make.py` will have to be +5. Use ``sphinx-quickstart`` to generate a new-style Sphinx Makefile. + The following features in the current :file:`make.py` will have to be addressed in some other way: - - Copying of some static content + - Copying of some static content - - Specifying a "small" build (only low-resolution PNG files for examples) + - Specifying a "small" build (only low-resolution PNG files for examples) Steps 1, 2, and 3 are interdependent. 4 and 5 may be done independently, though 5 has some dependency on 3. diff --git a/doc/devel/MEP/MEP11.rst b/doc/devel/MEP/MEP11.rst index 67274038877e..03bc3013b3e3 100644 --- a/doc/devel/MEP/MEP11.rst +++ b/doc/devel/MEP/MEP11.rst @@ -32,10 +32,10 @@ alongside matplotlib. This MEP aims to resolve some problems with that approach, bring some consistency, while continuing to make installation convenient. -At the time that was initially done, `setuptools`, `easy_install` and -`PyPI` were not mature enough to be relied on. However, at present, +At the time that was initially done, setuptools_, easy_install_ and +PyPI_ were not mature enough to be relied on. However, at present, we should be able to safely leverage the "modern" versions of those -tools, `distribute` and `pip`. +tools, distribute_ and pip_. While matplotlib has dependencies on both Python libraries and C/C++ libraries, this MEP addresses only the Python libraries so as to not @@ -47,42 +47,42 @@ Detailed description matplotlib depends on the following third-party Python libraries: - - Numpy - - dateutil (pure Python) - - pytz (pure Python) - - six -- required by dateutil (pure Python) - - pyparsing (pure Python) - - PIL (optional) - - GUI frameworks: pygtk, gobject, tkinter, PySide, PyQt4, wx (all - optional, but one is required for an interactive GUI) +- Numpy +- dateutil (pure Python) +- pytz (pure Python) +- six -- required by dateutil (pure Python) +- pyparsing (pure Python) +- PIL (optional) +- GUI frameworks: pygtk, gobject, tkinter, PySide, PyQt4, wx (all + optional, but one is required for an interactive GUI) Current behavior ---------------- -When installing from source, a `git` checkout or `pip`: +When installing from source, a :program:`git` checkout or pip_: - - `setup.py` attempts to `import numpy`. If this fails, the - installation fails. +- :file:`setup.py` attempts to ``import numpy``. If this fails, the + installation fails. - - For each of `dateutil`, `pytz` and `six`, `setup.py` attempts to - import them (from the top-level namespace). If that fails, - matplotlib installs its local copy of the library into the - top-level namespace. +- For each of dateutil_, pytz_ and six_, :file:`setup.py` attempts to + import them (from the top-level namespace). If that fails, + matplotlib installs its local copy of the library into the + top-level namespace. - - `pyparsing` is always installed inside of the matplotlib - namespace. +- pyparsing_ is always installed inside of the matplotlib + namespace. -This behavior is most surprising when used with `pip`, because no -`pip` dependency resolution is performed, even though it is likely to +This behavior is most surprising when used with pip_, because no +pip_ dependency resolution is performed, even though it is likely to work for all of these packages. -The fact that `pyparsing` is installed in the matplotlib namespace has +The fact that pyparsing_ is installed in the matplotlib namespace has reportedly (#1290) confused some users into thinking it is a matplotlib-related module and import it from there rather than the top-level. -When installing using the Windows installer, `dateutil`, `pytz` and -`six` are installed at the top-level *always*, potentially overwriting +When installing using the Windows installer, dateutil_, pytz_ and +six_ are installed at the top-level *always*, potentially overwriting already installed copies of those libraries. TODO: Describe behavior with the OS-X installer. @@ -90,7 +90,7 @@ TODO: Describe behavior with the OS-X installer. When installing using a package manager (Debian, RedHat, MacPorts etc.), this behavior actually does the right thing, and there are no special patches in the matplotlib packages to deal with the fact that -we handle `dateutil`, `pytz` and `six` in this way. However, care +we handle dateutil_, pytz_ and six_ in this way. However, care should be taken that whatever approach we move to continues to work in that context. @@ -104,9 +104,9 @@ Desired behavior ---------------- Third-party dependencies are downloaded and installed from their -canonical locations by leveraging `pip`, `distribute` and `PyPI`. +canonical locations by leveraging pip_, distribute_ and PyPI_. -`dateutil`, `pytz`, and `pyparsing` should be made into optional +dateutil_, pytz_, and pyparsing_ should be made into optional dependencies -- though obviously some features would fail if they aren't installed. This will allow the user to decide whether they want to bother installing a particular feature. @@ -116,32 +116,32 @@ Implementation For installing from source, and assuming the user has all of the C-level compilers and dependencies, this can be accomplished fairly -easily using `distribute` and following the instructions `here -`_. The only anticipated -change to the matplotlib library code will be to import `pyparsing` +easily using distribute_ and following the instructions `here +`_. The only anticipated +change to the matplotlib library code will be to import pyparsing_ from the top-level namespace rather than from within matplotlib. Note -that `distribute` will also allow us to remove the direct dependency -on `six`, since it is, strictly speaking, only a direct dependency of -`dateutil`. +that distribute_ will also allow us to remove the direct dependency +on six_, since it is, strictly speaking, only a direct dependency of +dateutil_. For binary installations, there are a number of alternatives (here ordered from best/hardest to worst/easiest): - 1. The distutils wininst installer allows a post-install script to - run. It might be possible to get this script to run `pip` to - install the other dependencies. (See `this thread - `_ - for someone who has trod that ground before). +1. The distutils wininst installer allows a post-install script to + run. It might be possible to get this script to run pip_ to + install the other dependencies. (See `this thread + `_ + for someone who has trod that ground before). - 2. Continue to ship `dateutil`, `pytz`, `six` and `pyparsing` in - our installer, but use the post-install-script to install them - *only* if they can not already be found. +2. Continue to ship dateutil_, pytz_, six_ and pyparsing_ in + our installer, but use the post-install-script to install them + *only* if they cannot already be found. - 3. Move all of these packages inside a (new) `matplotlib.extern` - namespace so it is clear for outside users that these are - external packages. Add some conditional imports in the core - matplotlib codebase so `dateutil` (at the top-level) is tried - first, and failing that `matplotlib.extern.dateutil` is used. +3. Move all of these packages inside a (new) ``matplotlib.extern`` + namespace so it is clear for outside users that these are + external packages. Add some conditional imports in the core + matplotlib codebase so dateutil_ (at the top-level) is tried + first, and failing that ``matplotlib.extern.dateutil`` is used. 2 and 3 are undesirable as they still require maintaining copies of these packages in our tree -- and this is exacerbated by the fact that @@ -164,7 +164,17 @@ accessing the network). Alternatives ============ -Distributing binary `eggs` doesn't feel like a usable solution. That -requires getting `easy_install` installed first, and Windows users -generally prefer the well known `.exe` or `.msi` installer that works +Distributing binary eggs doesn't feel like a usable solution. That +requires getting easy_install_ installed first, and Windows users +generally prefer the well known ``.exe`` or ``.msi`` installer that works out of the box. + +.. _PyPI: https://pypi.org +.. _dateutil: https://pypi.org/project/python-dateutil/ +.. _distribute: https://pypi.org/project/distribute/ +.. _pip: https://pypi.org/project/pip/ +.. _pyparsing: https://pypi.org/project/pyparsing/ +.. _pytz: https://pypi.org/project/pytz/ +.. _setuptools: https://pypi.org/project/setuptools/ +.. _six: https://pypi.org/project/six/ +.. _easy_install: https://setuptools.pypa.io/en/latest/deprecated/easy_install.html diff --git a/doc/devel/MEP/MEP12.rst b/doc/devel/MEP/MEP12.rst index 573083ada5c3..109d0f3df1af 100644 --- a/doc/devel/MEP/MEP12.rst +++ b/doc/devel/MEP/MEP12.rst @@ -11,14 +11,15 @@ Status **Progress** Initial changes added in 1.3. Conversion of the gallery is on-going. -29 September 2015 - The last `pylab_examples` where `pylab` is imported has been converted over to use `matplotlib pyplot` and `numpy`. +29 September 2015 - The last ``pylab_examples`` where ``pylab`` is imported has +been converted over to use :mod:`matplotlib.pyplot` and `numpy`. Branches and Pull requests ========================== #1623, #1924, #2181 -PR `#2474 _ +PR `#2474 `_ demonstrates a single example being cleaned up and moved to the appropriate section. @@ -87,8 +88,8 @@ the gallery would remain in those directories until they are cleaned up. After clean-up, they would be moved to one of the new gallery sections described above. "Clean-up" should involve: -* `sphinx-gallery docstrings `_: a - title and a description of the example formatted as follows, at the top of +* `sphinx-gallery docstrings `_: + a title and a description of the example formatted as follows, at the top of the example:: """ @@ -105,17 +106,17 @@ sections described above. "Clean-up" should involve: * PEP8_ clean-ups (running `flake8 - `_, or a similar checker, is + `_, or a similar checker, is highly recommended) * Commented-out code should be removed. -* Replace uses of ``pylab`` interface with ``pyplot`` (+ ``numpy``, +* Replace uses of `pylab` interface with `.pyplot` (+ `numpy`, etc.). See `c25ef1e `_ -* Remove shebang line, e.g.: +* Remove shebang line, e.g.:: #!/usr/bin/env python -* Use consistent imports. In particular: +* Use consistent imports. In particular:: import numpy as np @@ -134,15 +135,15 @@ sections described above. "Clean-up" should involve: and `1458aa8 `_ -Use of ``pylab`` should be demonstrated/discussed on a dedicated help +Use of `pylab` should be demonstrated/discussed on a dedicated help page instead of the gallery examples. **Note:** When moving an existing example, you should search for references to that example. For example, the API documentation for -`axes.py` and `pyplot.py` may use these examples to generate +:file:`axes.py` and :file:`pyplot.py` may use these examples to generate plots. Use your favorite search tool (e.g., grep, ack, `grin -`_, `pss -`_) to search the matplotlib +`_, `pss +`_) to search the matplotlib package. See `2dc9a46 `_ and `aa6b410 @@ -192,10 +193,6 @@ the entry point to these examples, and sections could really help users navigate the gallery. Thus, tags are complementary to this reorganization. -.. _PEP8: http://www.python.org/dev/peps/pep-0008/ +.. _PEP8: https://www.python.org/dev/peps/pep-0008/ -.. [1] http://github.com/matplotlib/matplotlib/pull/714 -.. [2] http://github.com/matplotlib/matplotlib/issues/524 -.. [3] http://matplotlib.1069221.n5.nabble.com/Matplotlib-gallery-td762.html#a33379091 -.. [4] http://www.loria.fr/~rougier/teaching/matplotlib/ -.. [5] http://www.loria.fr/~rougier/coding/gallery/ +.. [1] https://github.com/matplotlib/matplotlib/pull/714 diff --git a/doc/devel/MEP/MEP13.rst b/doc/devel/MEP/MEP13.rst index 3200a1b742cb..b8b80f281b6e 100644 --- a/doc/devel/MEP/MEP13.rst +++ b/doc/devel/MEP/MEP13.rst @@ -20,7 +20,7 @@ Abstract Wrap all of the matplotlib getter and setter methods with python `properties -`_, allowing +`_, allowing them to be read and written like class attributes. Detailed description @@ -42,11 +42,10 @@ Implementation a text file. 2. Classes should be reorganized so setter and getter methods are sequential in the code, with getter methods first. -3. Getter and setter methods the provide additional optional optional - arguments should have those arguments accessible in another manner, - either as additional getter or setter methods or attributes of - other classes. If those classes are not accessible, getters for - them should be added. +3. Getter and setter methods that provide additional optional arguments should + have those arguments accessible in another manner, either as additional + getter or setter methods or attributes of other classes. If those classes + are not accessible, getters for them should be added. 4. Property decorators will be added to the setter and getter methods without the prefix. Those with the prefix will be marked as deprecated. @@ -64,7 +63,7 @@ The following steps can be done simultaneously: 1, 2, and 3; 4 and 5; Only the following steps must be done in the same release: 4, 5, and 6. All other changes can be done in separate releases. 8 should -be done several major releases after everything else. +be done several macro releases after everything else. Backward compatibility ====================== diff --git a/doc/devel/MEP/MEP14.rst b/doc/devel/MEP/MEP14.rst index 9542b6885fef..d79d3c2d3115 100644 --- a/doc/devel/MEP/MEP14.rst +++ b/doc/devel/MEP/MEP14.rst @@ -62,7 +62,7 @@ has a number of shortcomings. - It only handles right-to-left languages, and doesn't handle many special features of Unicode, such as combining diacriticals. - The multiline support is imperfect and only supports manual - line-breaking -- it can not break up a paragraph into lines of a + line-breaking -- it cannot break up a paragraph into lines of a certain length. - It also does not handle inline formatting changes in order to support something like Markdown, reStructuredText or HTML. (Though @@ -73,21 +73,23 @@ has a number of shortcomings. Supporting these things is difficult, and is the "full-time job" of a number of other projects: - - `pango `_/`harfbuzz - `_ - - `QtTextLayout - `_ - - `Microsoft DirectWrite - `_ - - `Apple Core Text - `_ +- pango_/harfbuzz_ +- QtTextLayout_ +- `Microsoft DirectWrite`_ +- `Apple Core Text`_ -Of the above options, it should be noted that `harfbuzz` is designed +.. _pango: https://github.com/GNOME/pango +.. _harfbuzz: https://github.com/harfbuzz/harfbuzz +.. _QtTextLayout: https://doc.qt.io/archives/qt-4.8/qtextlayout.html +.. _Microsoft DirectWrite: https://docs.microsoft.com/en-ca/windows/win32/directwrite/introducing-directwrite +.. _Apple Core Text: https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Overview/Overview.html + +Of the above options, it should be noted that harfbuzz_ is designed from the start as a cross platform option with minimal dependencies, so therefore is a good candidate for a single option to support. Additionally, for supporting rich text, we could consider using -`WebKit `_, and possibly whether than +`WebKit `_, and possibly whether than represents a good single cross-platform option. Again, however, rich text formatting is outside of the scope of this project. @@ -110,16 +112,16 @@ complicated than it seems at first. The "built-in" and "usetex" renderers have very different ways of handling font selection, given their different technologies. TeX requires the installation of TeX-specific font packages, for example, -and can not use TrueType fonts directly. Unfortunately, despite the +and cannot use TrueType fonts directly. Unfortunately, despite the different semantics for font selection, the same set of font properties are used for each. This is true of both the -`FontProperties` class and the font-related `rcParams` (which +`.FontProperties` class and the font-related `.rcParams` (which basically share the same code underneath). Instead, we should define a core set of font selection parameters that will work across all text engines, and have engine-specific configuration to allow the user to do engine-specific things when required. For example, it is possible to directly select a font by name in the "built-in" using -`font.family`, but the same is not possible with "usetex". It may be +:rc:`font.family`, but the same is not possible with "usetex". It may be possible to make it easier to use TrueType fonts by using XeTeX, but users will still want to use the traditional metafonts through TeX font packages. So the issue still stands that different text engines @@ -128,12 +130,11 @@ to the user which configuration will work across text engines and which are engine-specific. Note that even excluding "usetex", there are different ways to find -fonts. The default is to use the font list cache in `font_manager.py` +fonts. The default is to use the font list cache in :mod:`.font_manager` which matches fonts using our own algorithm based on the `CSS font matching algorithm `_. It doesn't always do the same thing as the native font selection -algorithms on Linux (`fontconfig -`_), Mac and +algorithms on Linux (fontconfig_), Mac and Windows, and it doesn't always find all of the fonts on the system that the OS would normally pick up. However, it is cross-platform, and always finds the fonts that ship with matplotlib. The Cairo and @@ -146,13 +147,14 @@ we ship with matplotlib. (It may be possible to add the fonts to their search path, though, or we may need to find a way to install our fonts to a location the OS expects to find them). +.. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ + There are also special modes in the PS and PDF to only use the core fonts that are always available to those formats. There, the font lookup mechanism must only match against those fonts. It is unclear whether the OS-native font lookup systems can handle this case. -There is also experimental support for using `fontconfig -`_ for font +There is also experimental support for using fontconfig_ for font selection in matplotlib, turned off by default. fontconfig is the native font selection algorithm on Linux, but is also cross platform and works well on the other platforms (though obviously is an @@ -166,7 +168,7 @@ All of the above seems to suggest that we should move away from our self-written font selection algorithm and use the native APIs where possible. That's what Cairo and MacOSX backends already want to use, and it will be a requirement of any complex text layout library. On -Linux, we already have the bones of a `fontconfig` implementation +Linux, we already have the bones of a fontconfig_ implementation (which could also be accessed through pango). On Windows and Mac we may need to write custom wrappers. The nice thing is that the API for font lookup is relatively small, and essentially consist of "given a @@ -188,10 +190,10 @@ difficult to fix over time. Instead, we should be able to use FreeType to get the font outlines and write our own code (probably in Python) to output subsetted fonts -(Type 3 on PS and PDF and SVGFonts or paths on SVG). Freetype, as a -popular and well-maintained project, handles a wide variety of fonts -in the wild. This would remove a lot of custom C code, and remove -some code duplication between backends. +(Type 3 on PS and PDF and paths on SVG). Freetype, as a popular and +well-maintained project, handles a wide variety of fonts in the wild. +This would remove a lot of custom C code, and remove some code +duplication between backends. Note that subsetting fonts this way, while the easiest route, does lose the hinting in the font, so we will need to continue, as we do @@ -200,9 +202,11 @@ possible. Alternative font subsetting options include using the subsetting built-in to Cairo (not clear if it can be used without the rest of -Cairo), or using `fontforge` (which is a heavy and not terribly +Cairo), or using fontforge_ (which is a heavy and not terribly cross-platform dependency). +.. _fontforge: https://fontforge.org + **Freetype wrappers** Our FreeType wrapper could really use a reworking. It defines its own @@ -252,7 +256,7 @@ arbitrary text engine in another should be possible. The text mode is currently set by a global rcParam ("text.usetex") so it's either all on or all off. We should continue to have a global rcParam to choose the text engine ("text.layout_engine"), but it -should under the hood be an overridable property on the `Text` object, +should under the hood be an overridable property on the `.Text` object, so the same figure can combine the results of multiple text layout engines if necessary. @@ -261,16 +265,16 @@ Implementation ============== A concept of a "text engine" will be introduced. Each text engine -will implement a number of abstract classes. The `TextFont` interface +will implement a number of abstract classes. The ``TextFont`` interface will represent text for a given set of font properties. It isn't necessarily limited to a single font file -- if the layout engine supports rich text, it may handle a number of font files in a family. -Given a `TextFont` instance, the user can get a `TextLayout` instance, +Given a ``TextFont`` instance, the user can get a ``TextLayout`` instance, which represents the layout for a given string of text in a given -font. From a `TextLayout`, an iterator over `TextSpans` is returned +font. From a ``TextLayout``, an iterator over ``TextSpan``\ s is returned so the engine can output raw editable text using as few spans as possible. If the engine would rather get individual characters, they -can be obtained from the `TextSpan` instance:: +can be obtained from the ``TextSpan`` instance:: class TextFont(TextFontBase): @@ -324,7 +328,7 @@ can be obtained from the `TextSpan` instance:: def get_path(self): """ - Returns a single Path object of the entire layed out text. + Returns a single Path object of the entire laid out text. [Not strictly necessary, but might be useful for textpath functionality] @@ -372,10 +376,10 @@ copy of the path for each character will be stored in the file. Special casing: The "usetex" functionality currently is able to get Postscript directly from TeX to insert directly in a Postscript file, but for other backends, parses a DVI file and generates something more -abstract. For a case like this, `TextLayout` would implement -`get_spans` for most backends, but add `get_ps` for the Postscript +abstract. For a case like this, ``TextLayout`` would implement +``get_spans`` for most backends, but add ``get_ps`` for the Postscript backend, which would look for the presence of this method and use it -if available, or fall back to `get_spans`. This kind of special +if available, or fall back to ``get_spans``. This kind of special casing may also be necessary, for example, when the graphics backend and text engine belong to the same ecosystem, e.g. Cairo and Pango, or MacOSX and CoreText. @@ -384,10 +388,10 @@ There are three main pieces to the implementation: 1) Rewriting the freetype wrapper, and removing ttconv. - a) Once (1) is done, as a proof of concept, we can move to the - upstream STIX .otf fonts + a) Once (1) is done, as a proof of concept, we can move to the + upstream STIX .otf fonts - b) Add support for web fonts loaded from a remote URL. (Enabled by using freetype for font subsetting). + b) Add support for web fonts loaded from a remote URL. (Enabled by using freetype for font subsetting). 2) Refactoring the existing "builtin" and "usetex" code into separate text engines and to follow the API outlined above. diff --git a/doc/devel/MEP/MEP15.rst b/doc/devel/MEP/MEP15.rst index 0fed93813528..8e2f80707429 100644 --- a/doc/devel/MEP/MEP15.rst +++ b/doc/devel/MEP/MEP15.rst @@ -1,6 +1,6 @@ -========================================================================== - MEP15 - Fix axis autoscaling when limits are specified for one axis only -========================================================================== +========================================================================= + MEP15: Fix axis autoscaling when limits are specified for one axis only +========================================================================= .. contents:: :local: @@ -18,15 +18,15 @@ None so far. Abstract ======== -When one axis of a 2-dimensional plot if overridden via `xlim` or `ylim`, -automatic scaling of the remaining axis should be based on the data that falls -within the specified limits of the first axis. +When one Axis of a 2-dimensional plot is overridden via `~.Axes.set_xlim` or +`~.Axes.set_ylim`, automatic scaling of the remaining Axis should be based on +the data that falls within the specified limits of the first Axis. Detailed description ==================== -When axis limits for a 2-D plot are specified for one axis only (via `xlim` or -`ylim`), matplotlib currently does not currently rescale the other axis. The +When axis limits for a 2-D plot are specified for one axis only (via `~.Axes.set_xlim` or +`~.Axes.set_ylim`), matplotlib currently does not currently rescale the other axis. The result is that the displayed curves or symbols may be compressed into a tiny portion of the available area, so that the final plot conveys much less information than it would with appropriate axis scaling. diff --git a/doc/devel/MEP/MEP19.rst b/doc/devel/MEP/MEP19.rst index e96ebf74d8cd..02ae0f9e7b95 100644 --- a/doc/devel/MEP/MEP19.rst +++ b/doc/devel/MEP/MEP19.rst @@ -5,7 +5,7 @@ Status ====== -**Discussion** +**Completed** Branches and Pull requests ========================== @@ -34,9 +34,9 @@ has a number of shortcomings: - It often fails for inexplicable reasons. - build or test products can only be saved from build off of branches - on the main repo, not pull requsts, so it is often difficult to + on the main repo, not pull requests, so it is often difficult to "post mortem" analyse what went wrong. This is particularly - frustrating when the failure can not be subsequently reproduced + frustrating when the failure cannot be subsequently reproduced locally. - It is not extremely fast. matplotlib's cpu and memory requirements @@ -67,7 +67,7 @@ great!]: **Documentation** -Documentation of master is now built by travis and uploaded to http://matplotlib.org/devdocs/index.html +Documentation of main is now built by travis and uploaded to https://matplotlib.org/devdocs/index.html @NelleV, I believe, generates the docs automatically and posts them on the web to chart MEP10 progress. @@ -90,7 +90,7 @@ Requirements This section outlines the requirements that we would like to have. -#. Testing all pull requests by hooking into the Github API, as +#. Testing all pull requests by hooking into the GitHub API, as Travis-CI does #. Testing on all major platforms: Linux, Mac OS-X, MS Windows (in @@ -114,7 +114,7 @@ This section outlines the requirements that we would like to have. (This would not replace the static documentation for stable releases as a default). -#. The test systems should be managable by multiple developers, so +#. The test systems should be manageable by multiple developers, so that no single person becomes a bottleneck. (Travis-CI's design does this well -- storing build configuration in the git repository, rather than elsewhere, is a very good design.) @@ -122,7 +122,7 @@ This section outlines the requirements that we would like to have. #. Make it easy to test a large but sparse matrix of different versions of matplotlib's dependencies. The matplotlib user survey provides some good data as to where to focus our efforts: - https://docs.google.com/spreadsheet/ccc?key=0AjrPjlTMRTwTdHpQS25pcTZIRWdqX0pNckNSU01sMHc#gid=0 + https://docs.google.com/spreadsheets/d/1jbK0J4cIkyBNncnS-gP7pINSliNy9lI-N4JHwxlNSXE/edit #. Nice to have: A decentralized design so that those with more obscure platforms can publish build results to a central dashboard. @@ -169,7 +169,7 @@ CI Infrastructure - Investigate finding or building a tool that would collect and synthesize test results from a number of sources and post it to - Github using the Github API. This may be of general use to the + GitHub using the GitHub API. This may be of general use to the Scipy community. - For both Windows and Mac, we should document (or better yet, script) @@ -184,9 +184,9 @@ The test framework itself - We should investigate ways to make it take less time - - Eliminating redundant tests, if possible + - Eliminating redundant tests, if possible - - General performance improvements to matplotlib will help + - General performance improvements to matplotlib will help - We should be covering more things, particularly more backends diff --git a/doc/devel/MEP/MEP22.rst b/doc/devel/MEP/MEP22.rst index a72ca0764e04..8f8fe69b41a6 100644 --- a/doc/devel/MEP/MEP22.rst +++ b/doc/devel/MEP/MEP22.rst @@ -13,16 +13,18 @@ Status Branches and Pull requests ========================== -Previous work - * https://github.com/matplotlib/matplotlib/pull/1849 - * https://github.com/matplotlib/matplotlib/pull/2557 - * https://github.com/matplotlib/matplotlib/pull/2465 +Previous work: + +* https://github.com/matplotlib/matplotlib/pull/1849 +* https://github.com/matplotlib/matplotlib/pull/2557 +* https://github.com/matplotlib/matplotlib/pull/2465 Pull Requests: - * Removing the NavigationToolbar classes - https://github.com/matplotlib/matplotlib/pull/2740 **CLOSED** - * Keeping the NavigationToolbar classes https://github.com/matplotlib/matplotlib/pull/2759 **CLOSED** - * Navigation by events: https://github.com/matplotlib/matplotlib/pull/3652 + +* Removing the NavigationToolbar classes + https://github.com/matplotlib/matplotlib/pull/2740 **CLOSED** +* Keeping the NavigationToolbar classes https://github.com/matplotlib/matplotlib/pull/2759 **CLOSED** +* Navigation by events: https://github.com/matplotlib/matplotlib/pull/3652 Abstract ======== @@ -39,7 +41,7 @@ reconfiguration. This approach will make easier to create and share tools among users. In the far future, we can even foresee a kind of Marketplace -for `Tools` where the most popular can be added into the main +for ``Tool``\s where the most popular can be added into the main distribution. Detailed description @@ -53,20 +55,20 @@ example see https://github.com/matplotlib/matplotlib/issues/2694 also the shortcuts are hardcoded and again not easily modifiable https://github.com/matplotlib/matplotlib/issues/2699 -The proposed solution is to take the actions out of the `Toolbar` and -the shortcuts out of the `Canvas`. This actions and shortcuts will be -in the form of `Tools`. +The proposed solution is to take the actions out of the ``Toolbar`` and the +shortcuts out of the ``Canvas``. The actions and shortcuts will be in the form +of ``Tool``\s. -A new class `Navigation` will be the bridge between the events from -the `Canvas` and `Toolbar` and redirect them to the appropiate `Tool`. +A new class ``Navigation`` will be the bridge between the events from the +``Canvas`` and ``Toolbar`` and redirect them to the appropriate ``Tool``. At the end the user interaction will be divided into three classes: - * NavigationBase: This class is instantiated for each FigureManager - and connect the all user interactions with the Tools - * ToolbarBase: This existing class is relegated only as a GUI access - to Tools. - * ToolBase: Is the basic definition of Tools. +* NavigationBase: This class is instantiated for each FigureManager + and connect the all user interactions with the Tools +* ToolbarBase: This existing class is relegated only as a GUI access + to Tools. +* ToolBase: Is the basic definition of Tools. Implementation @@ -75,113 +77,127 @@ Implementation ToolBase(object) ---------------- -Tools can have a graphical representation as the `SubplotTool` or not even be present in the Toolbar as `Quit` +Tools can have a graphical representation as the ``SubplotTool`` or not even be +present in the Toolbar as ``Quit``. -The `ToolBase` has the following class attributes for configuration at definition time +The `.ToolBase` has the following class attributes for configuration at definition time - * keymap = None: Key(s) to be used to trigger the tool - * description = '': Small description of the tool - * image = None: Image that is used in the toolbar +* keymap = None: Key(s) to be used to trigger the tool +* description = '': Small description of the tool +* image = None: Image that is used in the toolbar The following instance attributes are set at instantiation: - * name - * navigation - -**Methods** - * trigger(self, event): This is the main method of the Tool, it is called when the Tool is triggered by: - * Toolbar button click - * keypress associated with the Tool Keymap - * Call to navigation.trigger_tool(name) - * set_figure(self, figure): Set the figure and navigation attributes - * ``destroy(self, *args)``: Destroy the `Tool` graphical interface (if exists) - -**Available Tools** - * ToolQuit - * ToolEnableAllNavigation - * ToolEnableNavigation - * ToolToggleGrid - * ToolToggleFullScreen - * ToolToggleYScale - * ToolToggleXScale - * ToolHome - * ToolBack - * ToolForward - * SaveFigureBase - * ConfigureSubplotsBase +* name +* navigation + +Methods +~~~~~~~ + +* ``trigger(self, event)``: This is the main method of the Tool, it is called + when the Tool is triggered by: + + * Toolbar button click + * keypress associated with the Tool Keymap + * Call to navigation.trigger_tool(name) + +* ``set_figure(self, figure)``: Set the figure and navigation attributes +* ``destroy(self, *args)``: Destroy the ``Tool`` graphical interface (if + exists) + +Available Tools +~~~~~~~~~~~~~~~ + +* ToolQuit +* ToolEnableAllNavigation +* ToolEnableNavigation +* ToolToggleGrid +* ToolToggleFullScreen +* ToolToggleYScale +* ToolToggleXScale +* ToolHome +* ToolBack +* ToolForward +* SaveFigureBase +* ConfigureSubplotsBase ToolToggleBase(ToolBase) ------------------------ -The `ToolToggleBase` has the following class attributes for +The `.ToolToggleBase` has the following class attributes for configuration at definition time - * radio_group = None: Attribute to group 'radio' like tools (mutually - exclusive) - * cursor = None: Cursor to use when the tool is active +* radio_group = None: Attribute to group 'radio' like tools (mutually + exclusive) +* cursor = None: Cursor to use when the tool is active The **Toggleable** Tools, can capture keypress, mouse moves, and mouse button press -It defines the following methods - * enable(self, event): Called by `ToolToggleBase.trigger` method - * disable(self, event): Called when the tool is untoggled - * toggled : **Property** True or False +Methods +~~~~~~~ -**Available Tools** - * ToolZoom - * ToolPan +* ``enable(self, event)``: Called by `.ToolToggleBase.trigger` method +* ``disable(self, event)``: Called when the tool is untoggled +* ``toggled``: **Property** True or False -NavigationBase --------------- +Available Tools +~~~~~~~~~~~~~~~ -Defines the following attributes - * canvas: - * keypresslock: Lock to know if the `canvas` key_press_event` is - available and process it - * messagelock: Lock to know if the message is available to write - -Public methods for **User use**: - * nav_connect(self, s, func): Connect to to navigation for events - * nav_disconnect(self, cid): Disconnect from navigation event - * message_event(self, message, sender=None): Emit a - tool_message_event event - * active_toggle(self): **Property** The currently toggled tools or - None - * get_tool_keymap(self, name): Return a list of keys that are - associated with the tool - * set_tool_keymap(self, name, ``*keys``): Set the keys for the given tool - * remove_tool(self, name): Removes tool from the navigation control. - * add_tools(self, tools): Add multiple tools to `Navigation` - * add_tool(self, name, tool, group=None, position=None): Add a tool - to the Navigation - * tool_trigger_event(self, name, sender=None, canvasevent=None, - data=None): Trigger a tool and fire the event - - * tools(self) **Property**: Return a dict with available tools with - corresponding keymaps, descriptions and objects - * get_tool(self, name): Return the tool object +* ToolZoom +* ToolPan +NavigationBase +-------------- +Defines the following attributes: + +* canvas: +* keypresslock: Lock to know if the ``canvas`` ``key_press_event`` is + available and process it +* messagelock: Lock to know if the message is available to write + +Methods (intended for the end user) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``nav_connect(self, s, func)``: Connect to navigation for events +* ``nav_disconnect(self, cid)``: Disconnect from navigation event +* ``message_event(self, message, sender=None)``: Emit a + tool_message_event event +* ``active_toggle(self)``: **Property** The currently toggled tools or + None +* ``get_tool_keymap(self, name)``: Return a list of keys that are + associated with the tool +* ``set_tool_keymap(self, name, ``*keys``)``: Set the keys for the given tool +* ``remove_tool(self, name)``: Removes tool from the navigation control. +* ``add_tools(self, tools)``: Add multiple tools to ``Navigation`` +* ``add_tool(self, name, tool, group=None, position=None)``: Add a tool + to the ``Navigation`` +* ``tool_trigger_event(self, name, sender=None, canvasevent=None, + data=None)``: Trigger a tool and fire the event +* ``tools``: **Property** A dict with available tools with + corresponding keymaps, descriptions and objects +* ``get_tool(self, name)``: Return the tool object ToolbarBase ----------- -Methods for **Backend implementation** - * add_toolitem(self, name, group, position, image, description, - toggle): Add a toolitem to the toolbar. This method is a callback - from `tool_added_event` (emited by navigation) - * set_message(self, s): Display a message on toolbar or in status bar - * toggle_toolitem(self, name): Toggle the toolitem without firing - event. - * remove_toolitem(self, name): Remove a toolitem from the `Toolbar` +Methods (for backend implementation) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* ``add_toolitem(self, name, group, position, image, description, toggle)``: + Add a toolitem to the toolbar. This method is a callback from + ``tool_added_event`` (emitted by navigation) +* ``set_message(self, s)``: Display a message on toolbar or in status bar +* ``toggle_toolitem(self, name)``: Toggle the toolitem without firing event. +* ``remove_toolitem(self, name)``: Remove a toolitem from the ``Toolbar`` Backward compatibility ====================== -For backward compatibility added a 'navigation' key to -`rcsetup.validate_toolbar`, that is used for Navigation classes +For backward compatibility added 'navigation' to the list of values +supported by :rc:`toolbar`, that is used for ``Navigation`` classes instantiation instead of the NavigationToolbar classes With this parameter, it makes it transparent to anyone using the diff --git a/doc/devel/MEP/MEP23.rst b/doc/devel/MEP/MEP23.rst index ae51294f017a..ec56f362c867 100644 --- a/doc/devel/MEP/MEP23.rst +++ b/doc/devel/MEP/MEP23.rst @@ -23,7 +23,7 @@ Abstract ======== Add the possibility to have multiple figures grouped under the same -`FigureManager` +`~.backend_template.FigureManager` Detailed description ==================== @@ -34,83 +34,87 @@ This is and may continue to be the desired method of operation for most use cases. Sometimes when there are too many figures open at the same time, it is -desirable to be able to group these under the same window -[see](https://github.com/matplotlib/matplotlib/issues/2194). +desirable to be able to group these under the same window. See :ghpull:`2194`. -The proposed solution modifies `FigureManagerBase` to contain and -manage more than one `canvas`. The settings parameter -`rcParams['backend.multifigure']` control when the **MultiFigure** -behaviour is desired. + +The proposed solution modifies `.FigureManagerBase` to contain and manage more +than one ``Canvas``. The ``backend.multifigure`` rcParam controls when the +**MultiFigure** behaviour is desired. **Note** It is important to note, that the proposed solution, assumes that the -[MEP22](https://github.com/matplotlib/matplotlib/wiki/Mep22) is +`MEP22 `_. is already in place. This is simply because the actual implementation of -the `Toolbar` makes it pretty hard to switch between canvases. +the ``Toolbar`` makes it pretty hard to switch between canvases. Implementation ============== -The first implementation will be done in `GTK3` using a Notebook as +The first implementation will be done in GTK3 using a Notebook as canvas container. -`FigureManagerBase` -------------------- +``FigureManagerBase`` +--------------------- will add the following new methods -* `add_canvas`: To add a canvas to an existing `FigureManager` object -* `remove_canvas`: To remove a canvas from a `FigureManager` object, - if it is the last one, it will be destroyed -* `move_canvas`: To move a canvas from one `FigureManager` to another. -* `set_canvas_title`: To change the title associated with a specific +* ``add_canvas``: To add a canvas to an existing + `~.backend_template.FigureManager` object +* ``remove_canvas``: To remove a canvas from a + `~.backend_template.FigureManager` object, if it is the last one, it will be + destroyed +* ``move_canvas``: To move a canvas from one `~.backend_template.FigureManager` + to another. +* ``set_canvas_title``: To change the title associated with a specific canvas container -* `get_canvas_title`: To get the title associated with a specific +* ``get_canvas_title``: To get the title associated with a specific canvas container -* `get_active_canvas`: To get the canvas that is in the foreground and - is subject to the gui events. There is no `set_active_canvas` - because the active canvas, is defined when `show` is called on a - `Canvas` object. +* ``get_active_canvas``: To get the canvas that is in the foreground and + is subject to the gui events. There is no ``set_active_canvas`` + because the active canvas, is defined when ``show`` is called on a + ``Canvas`` object. -`new_figure_manager` --------------------- +``new_figure_manager`` +---------------------- -To control which `FigureManager` will contain the new figures, an -extra optional parameter `figuremanager` will be added, this parameter -value will be passed to `new_figure_manager_given_figure` +To control which `~.backend_template.FigureManager` will contain the new +figures, an extra optional parameter *figuremanager* will be added, this +parameter value will be passed to ``new_figure_manager_given_figure``. -`new_figure_manager_given_figure` ---------------------------------- +``new_figure_manager_given_figure`` +----------------------------------- -* If `figuremanager` parameter is give, this `FigureManager` object - will be used instead of creating a new one. -* If `rcParams['backend.multifigure'] == True`: The last - `FigureManager` object will be used instead of creating a new one. +* If *figuremanager* parameter is given, this + `~.backend_template.FigureManager` object will be used instead of creating a + new one. +* If ``rcParams['backend.multifigure']`` is True: The last + `~.backend_template.FigureManager` object will be used instead of creating a + new one. -`NavigationBase` ----------------- +``NavigationBase`` +------------------ -Modifies the `NavigationBase` to keep a list of canvases, directing -the actions to the active one +Modifies the ``NavigationBase`` to keep a list of canvases, directing the +actions to the active one. Backward compatibility ====================== For the **MultiFigure** properties to be visible, the user has to -activate them directly setting `rcParams['backend.multifigure'] = -True` +activate them directly setting ``rcParams['backend.multifigure'] = +True`` It should be backwards compatible for backends that adhere to the -current `FigureManagerBase` structure even if they have not +current `.FigureManagerBase` structure even if they have not implemented the **MultiFigure** magic yet. Alternatives ============ -Insted of modifing the `FigureManagerBase` it could be possible to add +Instead of modifying the `.FigureManagerBase` it could be possible to add a parallel class, that handles the cases where -`rcParams['backend.multifigure'] = True`. This will warranty that +``rcParams['backend.multifigure'] = True``. This will warranty that there won't be any problems with custom made backends, but also makes -bigger the code, and more things to mantain. +bigger the code, and more things to maintain. diff --git a/doc/devel/MEP/MEP24.rst b/doc/devel/MEP/MEP24.rst index 89132cc7cd0e..b0620ce3dc8f 100644 --- a/doc/devel/MEP/MEP24.rst +++ b/doc/devel/MEP/MEP24.rst @@ -1,5 +1,5 @@ ======================================= - MEP24: negative radius in polar plots + MEP24: Negative radius in polar plots ======================================= .. contents:: @@ -29,9 +29,9 @@ One obvious application that we should support is bB plots (see https://github.com/matplotlib/matplotlib/issues/1730#issuecomment-40815837), but this seems more generally useful (for example growth rate as a function of angle). The assumption in the current code (as I -understand it) is that the center of the graph is `r==0`, however it -would be good to be able to set the center to be at any `r` (with any -value less than the off set clipped). +understand it) is that the center of the graph is ``r==0``, however it +would be good to be able to set the center to be at any ``r`` (with any +value less than the offset clipped). Implementation ============== diff --git a/doc/devel/MEP/MEP25.rst b/doc/devel/MEP/MEP25.rst index 0c41348a7586..7f0298210a9b 100644 --- a/doc/devel/MEP/MEP25.rst +++ b/doc/devel/MEP/MEP25.rst @@ -7,7 +7,9 @@ MEP25: Serialization Status ------ -**Discussion** +**Rejected** + +This work is important, but this particular effort has stalled. Branches and Pull requests -------------------------- @@ -65,7 +67,7 @@ be able to query for it. Additional Notes: -* The `raw data` does not necessarily need to be a ``list``, +* The "raw data" does not necessarily need to be a ``list``, ``ndarray``, etc. Rather, it can more abstractly just have a method to yield data when needed. @@ -111,31 +113,31 @@ Implementation 1. Create base ``Controller`` objects that are able to manage ``Artist`` objects (e.g., ``Hist``) - Comments: + Comments: - * initialization should happen via unpacking ``**``, so we need a - copy of call signature parameter for the ``Artist`` we're - ultimately trying to control. Unfortunate hard-coded - repetition... - * should the additional ``**kwargs`` accepted by each ``Artist`` - be tracked at the ``Controller`` - * how does a ``Controller`` know which artist belongs where? E.g., - do we need to pass ``axes`` references? + * initialization should happen via unpacking ``**``, so we need a + copy of call signature parameter for the ``Artist`` we're + ultimately trying to control. Unfortunate hard-coded + repetition... + * should the additional ``**kwargs`` accepted by each ``Artist`` + be tracked at the ``Controller`` + * how does a ``Controller`` know which artist belongs where? E.g., + do we need to pass ``axes`` references? - Progress: + Progress: - * A simple NB demonstrating some functionality for - ``Line2DController`` objects: - http://nbviewer.ipython.org/gist/theengineear/f0aa8d79f64325e767c0 + * A simple NB demonstrating some functionality for + ``Line2DController`` objects: + https://nbviewer.jupyter.org/gist/theengineear/f0aa8d79f64325e767c0 2. Write in protocols for the ``Controller`` to *update* the model. - Comments: + Comments: - * how should containers be dealt with? E.g., what happens to old - patches when we re-bin a histogram? - * in the link from (1), the old line is completely destroyed and - redrawn, what if something is referencing it? + * how should containers be dealt with? E.g., what happens to old + patches when we re-bin a histogram? + * in the link from (1), the old line is completely destroyed and + redrawn, what if something is referencing it? 3. Create method by which a json object can be assembled from the ``Controllers`` diff --git a/doc/devel/MEP/MEP26.rst b/doc/devel/MEP/MEP26.rst index c282735d8a46..9d3af8f8c703 100644 --- a/doc/devel/MEP/MEP26.rst +++ b/doc/devel/MEP/MEP26.rst @@ -9,7 +9,7 @@ Status ====== -**Proposed** +**Rejected** Branches and Pull requests ========================== @@ -34,24 +34,24 @@ Detailed description ==================== Currently, the look and appearance of existing artist objects (figure, -axes, Line2D etc...) can only be updated via `set_` and `get_` methods +axes, Line2D, etc.) can only be updated via ``set_`` and ``get_`` methods on the artist object, which is quite laborious, especially if no reference to the artist(s) has been stored. The new style sheets introduced in 1.4 allow styling before a plot is created, but do not offer any means to dynamically update plots or distinguish between -artists of the same type (i.e. to specifiy the `line color` and `line -style` separately for differing `Line2D` objects). +artists of the same type (i.e. to specify the ``line color`` and ``line +style`` separately for differing `.Line2D` objects). The initial development should concentrate on allowing styling of -artist primitives (those `artists` that do not contain other -`artists`), and further development could expand the CSS syntax rules +artist primitives (those `.Artist`\s that do not contain other +`.Artist`\s), and further development could expand the CSS syntax rules and parser to allow more complex styling. See the appendix for a list of primitives. The new methodology would require development of a number of steps: - A new stylesheet syntax (likely based on CSS) to allow selection of - artists by type, class, id etc... + artists by type, class, id, etc. - A mechanism by which to parse a stylesheet into a tree - A mechanism by which to translate the parse-tree into something which can be used to update the properties of relevant @@ -65,14 +65,13 @@ The new methodology would require development of a number of steps: Implementation ============== -It will be easiest to allow a '3rd party' to modify/set the style of -an artist if the 'style' is created as a separate class and store -against the artist as a property. The `GraphicsContext` class already -provides a the basis of a `Style` class and an artists `draw` method can -be refactored to use the `Style` class rather than setting up it's own -`GraphicsContext` and transferring it's style-related properties to -it. A minimal example of how this could be implemented is shown here: -https://github.com/JamesRamm/mpl_experiment +It will be easiest to allow a '3rd party' to modify/set the style of an artist +if the 'style' is created as a separate class and store against the artist as a +property. The `.GraphicsContextBase` class already provides a the basis of a +``Style`` class and an artist's `~.Artist.draw` method can be refactored to use +the ``Style`` class rather than setting up its own `.GraphicsContextBase` and +transferring its style-related properties to it. A minimal example of how this +could be implemented is shown here: https://github.com/JamesRamm/mpl_experiment IMO, this will also make the API and code base much neater as individual get/set methods for artist style properties are now @@ -103,7 +102,7 @@ the syntax is given below and then explained :: propValue ::= Ident | Number | Colour | "None" -`ArtistIdent`, `Ident`, `Number` and `Colour` are tokens (the basic +``ArtistIdent``, ``Ident``, ``Number`` and ``Colour`` are tokens (the basic building blocks of the expression) which are defined by regular expressions. @@ -116,14 +115,14 @@ syntax :: selector {attribute: value;} -Each rule can have any number of `attribute`: `value` pairs, and a +Each rule can have any number of ``attribute: value`` pairs, and a stylesheet can have any number of rules. -The initial syntax is designed only for `artist` primitives. It does -not address the question of how to set properties on `container` types -(whose properties may themselves be `artists` with settable +The initial syntax is designed only for `.Artist` primitives. It does +not address the question of how to set properties on `.Container` types +(whose properties may themselves be `.Artist`\s with settable properties), however, a future solution to this could simply be nested -`RuleSet` s +``RuleSet``\s Selectors ~~~~~~~~~ @@ -138,22 +137,22 @@ initial development: Artist Type Selector -Select an `artist` by it's type. E.g `Line2D` or `Text`:: +Select an `.Artist` by it's type. E.g `.Line2D` or `.Text`:: Line2D {attribute: value} -The regex for matching the artist type selector (`ArtistIdent` in the BNF grammar) would be:: +The regex for matching the artist type selector (``ArtistIdent`` in the BNF grammar) would be:: ArtistIdent = r'(?P\bLine2D\b|\bText\b|\bAxesImage\b|\bFigureImage\b|\bPatch\b)' GID selector ~~~~~~~~~~~~ -Select an `artist` by its `gid`:: +Select an `.Artist` by its ``gid``:: Line2D#myGID {attribute: value} -A `gid` can be any string, so the regex could be as follows:: +A ``gid`` can be any string, so the regex could be as follows:: Ident = r'(?P[a-zA-Z_][a-zA-Z_0-9]*)' @@ -164,18 +163,18 @@ The above selectors roughly correspond to their CSS counterparts Attributes and values ~~~~~~~~~~~~~~~~~~~~~ -- `Attributes` are any valid (settable) property for the `artist` in question. -- `Values` are any valid value for the property (Usually a string, or number). +- ``Attributes`` are any valid (settable) property for the `.Artist` in question. +- ``Values`` are any valid value for the property (Usually a string, or number). Parsing ------- Parsing would consist of breaking the stylesheet into tokens (the python cookbook gives a nice tokenizing recipe on page 66), applying -the syntax rules and constructing a `Tree`. This requires defining the +the syntax rules and constructing a ``Tree``. This requires defining the grammar of the stylesheet (again, we can borrow from CSS) and writing a parser. Happily, there is a recipe for this in the python cookbook -aswell. +as well. Visitor pattern for matplotlib figure @@ -184,7 +183,7 @@ Visitor pattern for matplotlib figure In order to apply the stylesheet rules to the relevant artists, we need to 'visit' each artist in a figure and apply the relevant rule. Here is a visitor class (again, thanks to python cookbook), where each -`node` would be an artist in the figure. A `visit_` method would need +``node`` would be an artist in the figure. A ``visit_`` method would need to be implemented for each mpl artist, to handle the different properties for each :: @@ -196,7 +195,7 @@ properties for each :: raise NotImplementedError return meth(node) -An `evaluator` class would then take the stylesheet rules and +An ``evaluator`` class would then take the stylesheet rules and implement the visitor on each one of them. @@ -204,10 +203,10 @@ implement the visitor on each one of them. Backward compatibility ====================== -Implementing a separate `Style` class would break backward +Implementing a separate ``Style`` class would break backward compatibility as many get/set methods on an artist would become redundant. While it would be possible to alter these methods to hook -into the `Style` class (stored as a property against the artist), I +into the ``Style`` class (stored as a property against the artist), I would be in favor of simply removing them to both neaten/simplify the codebase and to provide a simple, uncluttered API... diff --git a/doc/devel/MEP/MEP27.rst b/doc/devel/MEP/MEP27.rst index 57b0540a4c91..caf032c5c22d 100644 --- a/doc/devel/MEP/MEP27.rst +++ b/doc/devel/MEP/MEP27.rst @@ -1,5 +1,5 @@ ====================================== - MEP27: decouple pyplot from backends + MEP27: Decouple pyplot from backends ====================================== .. contents:: @@ -8,14 +8,16 @@ Status ====== -**Discussion** +**Progress** Branches and Pull requests ========================== Main PR (including GTK3): + + https://github.com/matplotlib/matplotlib/pull/4143 Backend specific branch diffs: + + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-tkagg + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-qt + https://github.com/OceanWolf/matplotlib/compare/backend-refactor...backend-refactor-wx @@ -49,27 +51,26 @@ Two main places for generic code appear in the classes derived from 1. ``FigureManagerBase`` has **three** jobs at the moment: - 1. The documentation describes it as a *``Helper class for pyplot - mode, wraps everything up into a neat bundle''* - 2. But it doesn't just wrap the canvas and toolbar, it also does - all of the windowing tasks itself. The conflation of these two - tasks gets seen the best in the following line: ```python - self.set_window_title("Figure %d" % num) ``` This combines - backend specific code ``self.set_window_title(title)`` with - matplotlib generic code ``title = "Figure %d" % num``. - - 3. Currently the backend specific subclass of ``FigureManager`` - decides when to end the mainloop. This also seems very wrong - as the figure should have no control over the other figures. + 1. The documentation describes it as a *Helper class for pyplot + mode, wraps everything up into a neat bundle* + 2. But it doesn't just wrap the canvas and toolbar, it also does + all of the windowing tasks itself. The conflation of these two + tasks gets seen the best in the following line: + ``self.set_window_title("Figure %d" % num)`` This combines + backend specific code ``self.set_window_title(title)`` with + matplotlib generic code ``title = "Figure %d" % num``. + 3. Currently the backend specific subclass of ``FigureManager`` + decides when to end the mainloop. This also seems very wrong + as the figure should have no control over the other figures. 2. ``ShowBase`` has two jobs: - 1. It has the job of going through all figure managers registered - in ``_pylab_helpers.Gcf`` and telling them to show themselves. - 2. And secondly it has the job of performing the backend specific - ``mainloop`` to block the main programme and thus keep the - figures from dying. + 1. It has the job of going through all figure managers registered + in ``_pylab_helpers.Gcf`` and telling them to show themselves. + 2. And secondly it has the job of performing the backend specific + ``mainloop`` to block the main programme and thus keep the + figures from dying. Implementation ============== @@ -95,7 +96,7 @@ The description of this MEP gives us most of the solution: 1. This allows us to break up the conversion of backends into separate PRs as we can keep the existing ``FigureManagerBase`` class and its dependencies intact. - 2. and this also anticipates MEP22 where the new + 2. And this also anticipates MEP22 where the new ``NavigationBase`` has morphed into a backend independent ``ToolManager``. @@ -114,9 +115,6 @@ The description of this MEP gives us most of the solution: +--------------------------------------+------------------------------+---------------------+--------------------------------+ |key_press |key_press | | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|show_popup |show_poup | |Not used anywhere in mpl, and | -| | | |does nothing. | -+--------------------------------------+------------------------------+---------------------+--------------------------------+ |get_window_title | |get_window_title | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ |set_window_title | |set_window_title | | diff --git a/doc/devel/MEP/MEP28.rst b/doc/devel/MEP/MEP28.rst index 777500176faa..7ae9f8e610d4 100644 --- a/doc/devel/MEP/MEP28.rst +++ b/doc/devel/MEP/MEP28.rst @@ -1,34 +1,28 @@ ============================================= - MEP 28: Remove Complexity from Axes.boxplot + MEP28: Remove Complexity from Axes.boxplot ============================================= .. contents:: :local: + Status ====== - -.. -.. MEPs go through a number of phases in their lifetime: - **Discussion** -.. -.. - **Progress**: Consensus was reached on the mailing list and -.. implementation work has begun. -.. -.. - **Completed**: The implementation has been merged into master. -.. -.. - **Superseded**: This MEP has been abandoned in favor of another -.. approach. Branches and Pull requests ========================== -Adding pre- & post-processing options to ``cbook.boxplot_stats``: https://github.com/phobson/matplotlib/tree/boxplot-stat-transforms -Exposing ``cbook.boxplot_stats`` through ``Axes.boxplot`` kwargs: None -Remove redundant statistical kwargs in ``Axes.boxplot``: None -Remove redundant style options in ``Axes.boxplot``: None -Remaining items that arise through discussion: None +The following lists any open PRs or branches related to this MEP: + +#. Deprecate redundant statistical kwargs in ``Axes.boxplot``: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations +#. Deprecate redundant style options in ``Axes.boxplot``: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations +#. Deprecate passings 2D NumPy arrays as input: None +#. Add pre- & post-processing options to ``cbook.boxplot_stats``: https://github.com/phobson/matplotlib/tree/boxplot-stat-transforms +#. Exposing ``cbook.boxplot_stats`` through ``Axes.boxplot`` kwargs: None +#. Remove redundant statistical kwargs in ``Axes.boxplot``: None +#. Remove redundant style options in ``Axes.boxplot``: None +#. Remaining items that arise through discussion: None Abstract ======== @@ -52,7 +46,7 @@ Detailed description Currently, the ``Axes.boxplot`` method accepts parameters that allow the users to specify medians and confidence intervals for each box that -will be drawn in the plot. These were provided so that avdanced users +will be drawn in the plot. These were provided so that advanced users could provide statistics computed in a different fashion that the simple method provided by matplotlib. However, handling this input requires complex logic to make sure that the forms of the data structure match what @@ -86,14 +80,14 @@ the seaborn API to the underlying matplotlib functions. This will be achieved in the following way: - 1. ``cbook.boxplot_stats`` will be modified to allow pre- and post- - computation transformation functions to be passed in (e.g., ``np.log`` - and ``np.exp`` for lognormally distributed data) - 2. ``Axes.boxplot`` will be modified to also accept and naïvely pass them - to ``cbook.boxplots_stats`` (Alt: pass the stat function and a dict - of its optional parameters). - 3. Outdated parameters from ``Axes.boxplot`` will be deprecated and - later removed. +1. ``cbook.boxplot_stats`` will be modified to allow pre- and post- + computation transformation functions to be passed in (e.g., ``np.log`` + and ``np.exp`` for lognormally distributed data) +2. ``Axes.boxplot`` will be modified to also accept and naïvely pass them + to ``cbook.boxplots_stats`` (Alt: pass the stat function and a dict + of its optional parameters). +3. Outdated parameters from ``Axes.boxplot`` will be deprecated and + later removed. Importance ---------- @@ -121,7 +115,7 @@ data differently depending one these types of transforms. ax.set_yscale('log') data = np.random.lognormal(-1.75, 2.75, size=37) - stats = cbook.boxplot_stats(data, labels=['arimetic']) + stats = cbook.boxplot_stats(data, labels=['arithmetic']) logstats = cbook.boxplot_stats(np.log(data), labels=['log-transformed']) for lsdict in logstats: @@ -152,7 +146,7 @@ These transformations can then be added to the call signature of ``Axes.boxplot`` with little impact to that method's complexity. This is because they can be directly passed to ``cbook.boxplot_stats``. Alternatively, ``Axes.boxplot`` could be modified to accept an optional -statistical function kwarg and a dictionary of parameters to be direcly +statistical function kwarg and a dictionary of parameters to be directly passed to it. At this point in the implementation users and external libraries like @@ -170,9 +164,9 @@ and ``Axes.bxp``. The parameters to be deprecated and removed include: - 1. ``usermedians`` - processed by 10 SLOC, 3 ``if`` blocks, a ``for`` loop - 2. ``conf_intervals`` - handled by 15 SLOC, 6 ``if`` blocks, a ``for`` loop - 3. ``sym`` - processed by 12 SLOC, 4 ``if`` blocks +1. ``usermedians`` - processed by 10 SLOC, 3 ``if`` blocks, a ``for`` loop +2. ``conf_intervals`` - handled by 15 SLOC, 6 ``if`` blocks, a ``for`` loop +3. ``sym`` - processed by 12 SLOC, 4 ``if`` blocks Removing the ``sym`` option allows all code in handling the remaining styling parameters to be moved to ``Axes.bxp``. This doesn't remove @@ -203,11 +197,22 @@ Schedule An accelerated timeline could look like the following: #. v2.0.1 add transforms to ``cbook.boxplots_stats``, expose in ``Axes.boxplot`` -#. v2.1.0 deprecate ``usermedians``, ``conf_intervals``, ``sym`` parameters -#. v2.2.0 make deprecations noisier -#. v2.3.0 remove ``usermedians``, ``conf_intervals``, ``sym`` parameters -#. v2.3.0 deprecate ``notch`` in favor of ``shownotches`` to be consistent with other parameters and ``Axes.bxp`` -#. v2.4.0 remove ``notch`` parameter, move all style and artist toggling logic to ``Axes.bxp``. ``Axes.boxplot`` is little more than a broker between ``Axes.bxp`` and ``cbook.boxplots_stats`` +#. v2.1.0 Initial Deprecations , and using 2D NumPy arrays as input + + a. Using 2D NumPy arrays as input. The semantics around 2D arrays are generally confusing. + b. ``usermedians``, ``conf_intervals``, ``sym`` parameters + +#. v2.2.0 + + a. remove ``usermedians``, ``conf_intervals``, ``sym`` parameters + b. deprecate ``notch`` in favor of ``shownotches`` to be consistent with + other parameters and ``Axes.bxp`` + +#. v2.3.0 + + a. remove ``notch`` parameter + b. move all style and artist toggling logic to ``Axes.bxp`` such ``Axes.boxplot`` + is little more than a broker between ``Axes.bxp`` and ``cbook.boxplots_stats`` Anticipated Impacts to Users @@ -242,7 +247,7 @@ Variations on the theme This MEP can be divided into a few loosely coupled components: -#. Allowing pre- and post-computation tranformation function in ``cbook.boxplot_stats`` +#. Allowing pre- and post-computation transformation function in ``cbook.boxplot_stats`` #. Exposing that transformation in the ``Axes.boxplot`` API #. Removing redundant statistical options in ``Axes.boxplot`` #. Shifting all styling parameter processing from ``Axes.boxplot`` to ``Axes.bxp``. @@ -250,15 +255,16 @@ This MEP can be divided into a few loosely coupled components: With this approach, #2 depends and #1, and #4 depends on #3. There are two possible approaches to #2. The first and most direct would -be to mirror the new ``transform_in`` and ``tranform_out`` parameters of +be to mirror the new ``transform_in`` and ``transform_out`` parameters of ``cbook.boxplot_stats`` in ``Axes.boxplot`` and pass them directly. The second approach would be to add ``statfxn`` and ``statfxn_args`` parameters to ``Axes.boxplot``. Under this implementation, the default value of ``statfxn`` would be ``cbook.boxplot_stats``, but users could -pass their own function. Then ``transform_in`` and ``tranform_out`` would +pass their own function. Then ``transform_in`` and ``transform_out`` would then be passed as elements of the ``statfxn_args`` parameter. +.. rstcheck: ignore-next-code-block .. code:: python def boxplot_stats(data, ..., transform_in=None, transform_out=None): @@ -330,7 +336,7 @@ Users could also pass their own function to compute the stats: ax1.boxplot(data, statfxn=my_box_stats, bootstrap_method='BCA', whisker_method='dynamic') -From the examples above, Option Two seems to have only marginal benifit, +From the examples above, Option Two seems to have only marginal benefit, but in the context of downstream libraries like seaborn, its advantage is more apparent as the following would be possible without any patches to seaborn: diff --git a/doc/devel/MEP/MEP29.rst b/doc/devel/MEP/MEP29.rst new file mode 100644 index 000000000000..fce4e3c5072c --- /dev/null +++ b/doc/devel/MEP/MEP29.rst @@ -0,0 +1,84 @@ +========================= + MEP29: Text light markup +========================= + +.. contents:: + :local: + + +Status +====== + +Discussion + + +Branches and Pull requests +========================== + +None at the moment, proof of concept only. + +Abstract +======== + +This MEP proposes to add lightweight markup to the text artist. + +Detailed description +==================== + +Using different size/color/family in a text annotation is difficult because the +`~.Axes.text` method accepts argument for size/color/family/weight/etc. that are used +for the whole text. But, if one wants, for example, to have different colors, +one has to look at the gallery where one such example is provided: +:doc:`/gallery/text_labels_and_annotations/rainbow_text` + +This example takes a list of strings as well as a list of colors which makes it +cumbersome to use. An alternative would be to use a restricted set of pango_-like markup and to interpret this markup. + +.. _pango: https://docs.gtk.org/Pango/pango_markup.html#pango-markup + +Some markup examples:: + + Hello world!` + Hello world! + + +Implementation +============== + +A proof of concept is provided in `markup_example.py `_ but it currently only handles the horizontal direction. + +Improvements +------------ + +* This proof of concept uses regex to parse the text but it may be better + to use the html.parser from the standard library. + +* Computation of text fragment positions could benefit from the OffsetFrom + class. See for example item 5 in `Using Complex Coordinates with Annotations `_ + +Problems +-------- + +* One serious problem is how to deal with text having both LaTeX and + HTML-like tags. For example, consider the following:: + + $Bold$ + + Recommendation would be to have mutual exclusion. + + +Backward compatibility +====================== + +None at the moment since it is only a proof of concept + + +Alternatives +============ + +As proposed by @anntzer, this could be also implemented as improvements to +mathtext. For example:: + + r"$\text{Hello \textbf{world}}$" + r"$\text{Hello \textcolor{blue}{world}}$" + r"$\text{Hello \textsf{\small world}}$" diff --git a/doc/devel/MEP/README.rst b/doc/devel/MEP/README.rst new file mode 100644 index 000000000000..fe58ee685d91 --- /dev/null +++ b/doc/devel/MEP/README.rst @@ -0,0 +1,18 @@ +:orphan: + + +################################ +Matplotlib Enhancement Proposals +################################ + +Matplotlib Enhancement Proposals (MEP), inspired by cpython's `PEP's +`__ but less formal, are design +documents for large or controversial changes to Matplotilb. These +documents should provide a discussion of both why and how the changes +should be made. + +To create a new MEP open a pull request (PR) adding a file based on +:ref:`the template ` to this the MEP directory. For the +initial PR only a rough description is required and it should be +merged quickly. Further detailed discussion can happen in follow on +PRs. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index f9f531d92800..6753626aa567 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -1,10 +1,8 @@ .. _MEP-index: -################################ -Matplotlib Enhancement Proposals -################################ +.. include:: README.rst -.. htmlonly:: +.. only:: html :Release: |version| :Date: |today| @@ -13,20 +11,9 @@ Matplotlib Enhancement Proposals :maxdepth: 1 template - MEP08 - MEP09 - MEP10 - MEP11 - MEP12 - MEP13 - MEP14 - MEP15 - MEP19 - MEP21 - MEP22 - MEP23 - MEP24 - MEP25 - MEP26 - MEP27 - MEP28 + +.. toctree:: + :glob: + :maxdepth: 1 + + MEP* diff --git a/doc/devel/MEP/template.rst b/doc/devel/MEP/template.rst index fae4a5132275..00bdbc87a95e 100644 --- a/doc/devel/MEP/template.rst +++ b/doc/devel/MEP/template.rst @@ -1,3 +1,5 @@ +.. _MEP-template: + ============== MEP Template ============== @@ -20,14 +22,15 @@ MEPs go through a number of phases in their lifetime: discussion of the MEP should include the MEP number (MEPxxx) in the subject line so they can be easily related to the MEP. -- **Progress**: Consensus was reached on the mailing list and - implementation work has begun. +- **Progress**: Consensus was reached and implementation work has begun. -- **Completed**: The implementation has been merged into master. +- **Completed**: The implementation has been merged into main. - **Superseded**: This MEP has been abandoned in favor of another approach. +- **Rejected**: There are currently no plans to implement the proposal. + Branches and Pull requests ========================== diff --git a/doc/devel/add_new_projection.rst b/doc/devel/add_new_projection.rst deleted file mode 100644 index b05e05f5fc9a..000000000000 --- a/doc/devel/add_new_projection.rst +++ /dev/null @@ -1,135 +0,0 @@ -.. _adding-new-scales: - -*********************************************** -Adding new scales and projections to matplotlib -*********************************************** - -.. ::author Michael Droettboom - -Matplotlib supports the addition of custom procedures that transform -the data before it is displayed. - -There is an important distinction between two kinds of -transformations. Separable transformations, working on a single -dimension, are called "scales", and non-separable transformations, -that handle data in two or more dimensions at a time, are called -"projections". - -From the user's perspective, the scale of a plot can be set with -:meth:`~matplotlib.axes.Axes.set_xscale` and -:meth:`~matplotlib.axes.Axes.set_yscale`. Projections can be chosen -using the ``projection`` keyword argument to the -:func:`~matplotlib.pylab.plot` or :func:`~matplotlib.pylab.subplot` -functions, e.g.:: - - plot(x, y, projection="custom") - -This document is intended for developers and advanced users who need -to create new scales and projections for matplotlib. The necessary -code for scales and projections can be included anywhere: directly -within a plot script, in third-party code, or in the matplotlib source -tree itself. - -.. _creating-new-scale: - -Creating a new scale -==================== - -Adding a new scale consists of defining a subclass of -:class:`matplotlib.scale.ScaleBase`, that includes the following -elements: - - - A transformation from data coordinates into display coordinates. - - - An inverse of that transformation. This is used, for example, to - convert mouse positions from screen space back into data space. - - - A function to limit the range of the axis to acceptable values - (``limit_range_for_scale()``). A log scale, for instance, would - prevent the range from including values less than or equal to - zero. - - - Locators (major and minor) that determine where to place ticks in - the plot, and optionally, how to adjust the limits of the plot to - some "good" values. Unlike ``limit_range_for_scale()``, which is - always enforced, the range setting here is only used when - automatically setting the range of the plot. - - - Formatters (major and minor) that specify how the tick labels - should be drawn. - -Once the class is defined, it must be registered with matplotlib so -that the user can select it. - -A full-fledged and heavily annotated example is in -:file:`examples/api/custom_scale_example.py`. There are also some classes -in :mod:`matplotlib.scale` that may be used as starting points. - - -.. _creating-new-projection: - -Creating a new projection -========================= - -Adding a new projection consists of defining a projection axes which -subclasses :class:`matplotlib.axes.Axes` and includes the following -elements: - - - A transformation from data coordinates into display coordinates. - - - An inverse of that transformation. This is used, for example, to - convert mouse positions from screen space back into data space. - - - Transformations for the gridlines, ticks and ticklabels. Custom - projections will often need to place these elements in special - locations, and matplotlib has a facility to help with doing so. - - - Setting up default values (overriding - :meth:`~matplotlib.axes.Axes.cla`), since the defaults for a - rectilinear axes may not be appropriate. - - - Defining the shape of the axes, for example, an elliptical axes, - that will be used to draw the background of the plot and for - clipping any data elements. - - - Defining custom locators and formatters for the projection. For - example, in a geographic projection, it may be more convenient to - display the grid in degrees, even if the data is in radians. - - - Set up interactive panning and zooming. This is left as an - "advanced" feature left to the reader, but there is an example of - this for polar plots in :mod:`matplotlib.projections.polar`. - - - Any additional methods for additional convenience or features. - -Once the projection axes is defined, it can be used in one of two ways: - - - By defining the class attribute ``name``, the projection axes can be - registered with :func:`matplotlib.projections.register_projection` - and subsequently simply invoked by name:: - - plt.axes(projection='my_proj_name') - - - For more complex, parameterisable projections, a generic "projection" - object may be defined which includes the method ``_as_mpl_axes``. - ``_as_mpl_axes`` should take no arguments and return the projection's - axes subclass and a dictionary of additional arguments to pass to the - subclass' ``__init__`` method. Subsequently a parameterised projection - can be initialised with:: - - plt.axes(projection=MyProjection(param1=param1_value)) - - where MyProjection is an object which implements a ``_as_mpl_axes`` method. - - -A full-fledged and heavily annotated example is in -:file:`examples/api/custom_projection_example.py`. The polar plot -functionality in :mod:`matplotlib.projections.polar` may also be of -interest. - -API documentation -================= - -* :mod:`matplotlib.scale` -* :mod:`matplotlib.projections` -* :mod:`matplotlib.projections.polar` diff --git a/doc/devel/api_changes.rst b/doc/devel/api_changes.rst new file mode 100644 index 000000000000..5fed9f683a48 --- /dev/null +++ b/doc/devel/api_changes.rst @@ -0,0 +1,333 @@ +.. _api_changes: + +API guidelines +============== + +API consistency and stability are of great value; Therefore, API changes +(e.g. signature changes, behavior changes, removals) will only be conducted +if the added benefit is worth the effort of adapting existing code. + +Because we are a visualization library, our primary output is the final +visualization the user sees; therefore, the appearance of the figure is part of +the API and any changes, either semantic or aesthetic, are backwards-incompatible +API changes. + + +Add new API +----------- + +Every new function, parameter and attribute that is not explicitly marked as +private (i.e., starts with an underscore) becomes part of Matplotlib's public +API. As discussed above, changing the existing API is cumbersome. Therefore, +take particular care when adding new API: + +- Mark helper functions and internal attributes as private by prefixing them + with an underscore. +- Carefully think about good names for your functions and variables. +- Try to adopt patterns and naming conventions from existing parts of the + Matplotlib API. +- Consider making as many arguments keyword-only as possible. See also + `API Evolution the Right Way -- Add Parameters Compatibly`__. + + __ https://emptysqua.re/blog/api-evolution-the-right-way/#adding-parameters + + +Add or change colormaps, color sequences, and styles +---------------------------------------------------- +Visual changes are considered an API break. Therefore, we generally do not modify +existing colormaps, color sequences, or styles. + +We put a high bar on adding new colormaps and styles to prevent excessively growing +them. While the decision is case-by-case, evaluation criteria include: + +- novelty: Does it support a new use case? e.g. slight variations of existing maps, + sequences and styles are likely not accepted. +- usability and accessibility: Are colors of sequences sufficiently distinct? Has + colorblindness been considered? +- evidence of wide spread usage: for example academic papers, industry blogs and + whitepapers, or inclusion in other visualization libraries or domain specific tools +- open license: colormaps, sequences, and styles must have a BSD compatible license + (see :ref:`license-discussion`) + +.. _deprecation-guidelines: + +Deprecate API +------------- + +When deciding to deprecate API we carefully consider the balance between the advantages +(clearer interfaces, better usability, less maintenance) and the disadvantages (users +have to learn new API and have to modify existing code). + +.. tip:: + + A rough estimate on the current usage of an API can be obtained by a GitHub code + search. A good search pattern is typically + ``[expression] language:Python NOT is:fork``. ``[expression]`` may be a simple + string, but often regular expressions are helpful to exclude incorrect matches. + You can start simple and look at the search results, if there are too many + incorrect matches, gradually refine your search criteria. + + It can also be helpful to add ``NOT path:**/matplotlib/** NOT path:**/site-packages/**`` + to exclude matches where the matplotlib codebase is checked into another repo, + either as direct sources or as part of an environment. + + *Example*: Calls of the method ``Figure.draw()`` could be matched using + ``/\bfig(ure)?\.draw\(/``. This expression employs a number of patterns: + + - Add the opening bracket ``(`` after the method name to only find method calls. + - Include a common object name if there are otherwise too many false positives. + There are many ``draw()`` functions out there, but the ones we are looking for + are likely called via ``fig.draw()`` or ``figure.draw()``. + - Use the word boundary marker ``\b`` to make sure your expression is not a + matching as part of a longer word. + + `Link to the resulting GitHub search `_ + + +API changes in Matplotlib have to be performed following the deprecation process +below, except in very rare circumstances as deemed necessary by the development +team. Generally API deprecation happens in two stages: + +* **introduce:** warn users that the API *will* change +* **expire:** API *is* changed as described in the introduction period + +This ensures that users are notified before the change will take effect and thus +prevents unexpected breaking of code. Occasionally deprecations are marked as +**pending**, which means that the deprecation will be introduced in a future release. + +Rules +^^^^^ +- Deprecations are targeted at the next :ref:`meso release ` (e.g. 3.Y) +- Deprecated API is generally removed (expired) two point-releases after introduction + of the deprecation. Longer deprecations can be imposed by core developers on + a case-by-case basis to give more time for the transition +- The old API must remain fully functional during the deprecation period +- If alternatives to the deprecated API exist, they should be available + during the deprecation period +- If in doubt, decisions about API changes are finally made by the + `API consistency lead `_ developer. + + +.. _intro-deprecation: + +Introduce deprecation +^^^^^^^^^^^^^^^^^^^^^ + +Deprecations are introduced to warn users that the API will change. The deprecation +notice describes how the API will change. When alternatives to the deprecated API exist, +they are also listed in the notice and decorators. + +#. Create a :ref:`deprecation notice ` + +#. If possible, issue a `~matplotlib.MatplotlibDeprecationWarning` when the + deprecated API is used. There are a number of helper tools for this: + + - Use ``_api.warn_deprecated()`` for general deprecation warnings + - Use the decorator ``@_api.deprecated`` to deprecate classes, functions, + methods, or properties + - Use ``@_api.deprecate_privatize_attribute`` to annotate deprecation of + attributes while keeping the internal private version. + - To warn on changes of the function signature, use the decorators + ``@_api.delete_parameter``, ``@_api.rename_parameter``, and + ``@_api.make_keyword_only`` + + All these helpers take a first parameter *since*, which should be set to + the next point release, e.g. "3.x". + + You can use standard rst cross references in *alternative*. + +#. Make appropriate changes to the type hints in the associated ``.pyi`` file. + The general guideline is to match runtime reported behavior. + + - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` + are generally kept during the expiry period, and thus no changes are needed on + introduction. + - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` + report the *new* (post deprecation) signature at runtime, and thus *should* be + updated on introduction. + - Items decorated with ``@_api.delete_parameter`` should include a default value hint + for the deleted parameter, even if it did not previously have one (e.g. + ``param: = ...``). + +.. _expire-deprecation: + +Expire deprecation +^^^^^^^^^^^^^^^^^^ +The API changes described in the introduction notice are only implemented after the +introduction period has expired. + +#. Create a :ref:`deprecation announcement `. For the content, + you can usually copy the deprecation notice and adapt it slightly. + +#. Change the code functionality and remove any related deprecation warnings. + +#. Make appropriate changes to the type hints in the associated ``.pyi`` file. + + - Items marked with ``@_api.deprecated`` or ``@_api.deprecate_privatize_attribute`` + are to be removed on expiry. + - Items decorated with ``@_api.rename_parameter`` or ``@_api.make_keyword_only`` + will have been updated at introduction, and require no change now. + - Items decorated with ``@_api.delete_parameter`` will need to be updated to the + final signature, in the same way as the ``.py`` file signature is updated. + - Any entries in :file:`ci/mypy-stubtest-allowlist.txt` which indicate a deprecation + version should be double checked. In most cases this is not needed, though some + items were never type hinted in the first place and were added to this file + instead. For removed items that were not in the stub file, only deleting from the + allowlist is required. + +.. _pending-deprecation: + +Pending deprecation +^^^^^^^^^^^^^^^^^^^ + +A pending deprecation is an announcement that a deprecation will be introduced in the +future. By default, pending deprecations do not raise a warning to the user; however, +pending deprecations are rendered in the documentation and listed in the release notes. +Pending notices are primarily intended to give downstream library and tool developers +time to adapt their code so that it does not raise a deprecation +warning. This is because their users cannot act on warnings triggered by how the tools +and libraries use Matplotlib. It's also possible to run Python in dev mode to raise +`PendingDeprecationWarning`. + +To mark a deprecation as pending, set the following parameters on the appropriate +deprecation decorator: +* the *pending* parameter is set to ``True`` +* the *removal* parameter is left blank + +When converting a pending deprecation to an introduced deprecation, update the +decorator such that: +* *pending* is set to ``False`` +* *since* is set to the next meso release (3.Y+1) +* *removal* is set to at least 2 meso releases after (3.Y+3) introduction. + +Pending deprecations are documented in the :ref:`API change notes ` in +the same manner as introduced and expired deprecations. The notice should include +*pending deprecation* in the title. + + +.. redirect-from:: /devel/coding_guide#new-features-and-api-changes + +.. _api_whats_new: + +Announce new and deprecated API +------------------------------- + +When adding or changing the API in a backward in-compatible way, please add the +appropriate :ref:`versioning directive ` and document it +in the :ref:`release notes ` by adding an entry to the appropriate +folder: + ++-------------------+-----------------------------+----------------------------------------------+ +| | versioning directive | announcement folder | ++===================+=============================+==============================================+ +| new feature | ``.. versionadded:: 3.N`` | :file:`doc/release/next_whats_new/` | ++-------------------+-----------------------------+----------------------------------------------+ +| API change | ``.. versionchanged:: 3.N`` | :file:`doc/api/next_api_changes/[kind]` | ++-------------------+-----------------------------+----------------------------------------------+ + +When deprecating API, please add a notice as described in the +:ref:`deprecation guidelines ` and summarized here: + ++--------------------------------------------------+----------------------------------------------+ +| stage | announcement folder | ++===========+======================================+==============================================+ +| :ref:`introduce deprecation ` | :file:`doc/api/next_api_changes/deprecation` | ++-----------+--------------------------------------+----------------------------------------------+ +| :ref:`expire deprecation ` | :file:`doc/api/next_api_changes/[kind]` | ++-----------+--------------------------------------+----------------------------------------------+ + +Generally the introduction notices can be repurposed for the expiration notice as they +are expected to be describing the same API changes and removals. + +.. _versioning-directives: + +Versioning directives +^^^^^^^^^^^^^^^^^^^^^ + +When making a backward incompatible change, please add a versioning directive in +the docstring. The directives should be placed at the end of a description block. +For example:: + + class Foo: + """ + This is the summary. + + Followed by a longer description block. + + Consisting of multiple lines and paragraphs. + + .. versionadded:: 3.5 + + Parameters + ---------- + a : int + The first parameter. + b: bool, default: False + This was added later. + + .. versionadded:: 3.6 + """ + + def set_b(b): + """ + Set b. + + .. versionadded:: 3.6 + + Parameters + ---------- + b: bool + +For classes and functions, the directive should be placed before the +*Parameters* section. For parameters, the directive should be placed at the +end of the parameter description. The micro release version is omitted and +the directive should not be added to entire modules. + +.. _release-notes: + +Release notes +^^^^^^^^^^^^^ + +For both change notes and what's new, please avoid using cross-references in section +titles as it causes links to be confusing in the table of contents. Instead, ensure that +a cross-reference is included in the descriptive text. + +.. _api-change-notes: + +API change notes +"""""""""""""""" + +.. include:: ../api/next_api_changes/README.rst + :start-after: api-change-guide-start + :end-before: api-change-guide-end + +.. _whats-new-notes: + +What's new notes +"""""""""""""""" + +.. include:: ../release/next_whats_new/README.rst + :start-after: whats-new-guide-start + :end-before: whats-new-guide-end + +Discourage API +-------------- + +We have API that we do not recommend anymore for new code, but that cannot be +deprecated because its removal would be breaking backward-compatibility and too +disruptive. In such a case we can formally discourage API. This can cover +specific parameters, call patterns, whole methods etc. + +To do so, add a note to the docstring :: + + .. admonition:: Discouraged + + [description and suggested alternative] + +You find several examples for good descriptions if you search the codebase for +``.. admonition:: Discouraged``. + +Additionally, if a whole function is discouraged, prefix the summary line with +``[*Discouraged*]`` so that it renders in the API overview like this + + [*Discouraged*] Return the XAxis instance. diff --git a/doc/devel/codespaces.md b/doc/devel/codespaces.md new file mode 100644 index 000000000000..cb002c9b2e6e --- /dev/null +++ b/doc/devel/codespaces.md @@ -0,0 +1,9 @@ +# Contributing to Matplotlib using GitHub codespaces + +* For a general overview of contributing to Matplotlib, see https://matplotlib.org/devdocs/devel/index.html + +* For instructions on how to submit Pull Requests using GitHub codespaces, see https://matplotlib.org/devdocs/devel/contribute.html#contributing-code + +* For instructions on running tests to verify your changes, see https://matplotlib.org/devdocs/devel/testing.html + +* For instructions on building the Matplotlib documentation, see https://matplotlib.org/devdocs/devel/document.html#documenting-matplotlib diff --git a/doc/devel/coding_guide.rst b/doc/devel/coding_guide.rst index 04edfb919417..fe7769909368 100644 --- a/doc/devel/coding_guide.rst +++ b/doc/devel/coding_guide.rst @@ -1,266 +1,163 @@ -.. _coding-guide: +.. _coding_guidelines: -************ -Coding guide -************ +***************** +Coding guidelines +***************** -.. _pull-request-checklist: +We appreciate these guidelines being followed because it improves the readability, +consistency, and maintainability of the code base. -Pull request checklist -====================== +.. admonition:: API guidelines + :class: seealso -This checklist should be consulted when creating pull requests to make -sure they are complete before merging. These are not intended to be -rigidly followed---it's just an attempt to list in one place all of -the items that are necessary for a good pull request. Of course, some -items will not always apply. + If adding new features, changing behavior or function signatures, or removing + public interfaces, please consult the :ref:`api_changes`. -Branch selection ----------------- +.. _code-style: -* In general, simple bugfixes that are unlikely to introduce new bugs - of their own should be merged onto the maintenance branch. New - features, or anything that changes the API, should be made against - master. The rules are fuzzy here -- when in doubt, try to get some - consensus. +PEP8, as enforced by ruff +========================= - * Once changes are merged into the maintenance branch, they should - be merged into master. +Formatting should follow the recommendations of PEP8_, as enforced by ruff_. +Matplotlib modifies PEP8 to extend the maximum line length to 88 +characters. You can check PEP8 compliance from the command line with :: -Style ------ + python -m pip install ruff + ruff check /path/to/module.py -* Formatting should follow `PEP8 - `_. Exceptions to these - rules are acceptable if it makes the code objectively more readable. +or your editor may provide integration with it. To check all files, +and fix any errors in-place (where possible) run :: - - You should consider installing/enabling automatic PEP8 checking in your - editor. Part of the test suite is checking PEP8 compliance, things - go smoother if the code is mostly PEP8 compliant to begin with. + ruff check --fix -* No tabs (only spaces). No trailing whitespace. - - Configuring your editor to remove these things upon saving will - save a lot of trouble. +Matplotlib intentionally does not use the black_ auto-formatter (1__), +in particular due to its inability to understand the semantics of +mathematical expressions (2__, 3__). -* Import the following modules using the standard scipy conventions:: +.. _PEP8: https://www.python.org/dev/peps/pep-0008/ +.. _ruff: https://docs.astral.sh/ruff/ +.. _black: https://black.readthedocs.io/ +.. __: https://github.com/matplotlib/matplotlib/issues/18796 +.. __: https://github.com/psf/black/issues/148 +.. __: https://github.com/psf/black/issues/1984 - import numpy as np - import numpy.ma as ma - import matplotlib as mpl - from matplotlib import pyplot as plt - import matplotlib.cbook as cbook - import matplotlib.collections as mcol - import matplotlib.patches as mpatches -* See below for additional points about - :ref:`keyword-argument-processing`, if code in your pull request - does that. +Package imports +=============== -* Adding a new pyplot function involves generating code. See - :ref:`new-pyplot-function` for more information. +Import the following modules using the standard scipy conventions:: -Documentation -------------- + import numpy as np + import numpy.ma as ma + import matplotlib as mpl + import matplotlib.pyplot as plt + import matplotlib.cbook as cbook + import matplotlib.patches as mpatches -* Every new feature should be documented. If it's a new module, don't - forget to add a new rst file to the API docs. +In general, Matplotlib modules should **not** import `.rcParams` using ``from +matplotlib import rcParams``, but rather access it as ``mpl.rcParams``. This +is because some modules are imported very early, before the `.rcParams` +singleton is constructed. -* Docstrings should be in `numpydoc format - `_. - Don't be thrown off by the fact that many of the existing docstrings - are not in that format; we are working to standardize on - `numpydoc`. +Variable names +============== - Docstrings should look like (at a minimum):: +When feasible, please use our internal variable naming convention for objects +of a given class and objects of any child class: - def foo(bar, baz=None): - """ - This is a prose description of foo and all the great - things it does. ++------------------------------------+---------------+------------------------------------------+ +| base class | variable | multiples | ++====================================+===============+==========================================+ +| `~matplotlib.figure.FigureBase` | ``fig`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.axes.Axes` | ``ax`` | | ++------------------------------------+---------------+------------------------------------------+ +| `~matplotlib.transforms.Transform` | ``trans`` | ``trans__`` | ++ + + + +| | | ``trans_`` when target is screen | ++------------------------------------+---------------+------------------------------------------+ - Parameters - ---------- - bar : (type of bar) - A description of bar +Generally, denote more than one instance of the same class by adding suffixes to +the variable names. If a format isn't specified in the table, use numbers or +letters as appropriate. - baz : (type of baz), optional - A description of baz +.. _type-hints: - Returns - ------- - foobar : (type of foobar) - A description of foobar - foobaz : (type of foobaz) - A description of foobaz - """ - # some very clever code - return foobar, foobaz +Type hints +========== +If you add new public API or change public API, update or add the +corresponding `mypy `_ type hints. +We generally use `stub files +`_ +(``*.pyi``) to store the type information; for example ``colors.pyi`` contains +the type information for ``colors.py``. A notable exception is ``pyplot.py``, +which is type hinted inline. -* Each high-level plotting function should have a simple example in - the `Example` section of the docstring. This should be as simple as - possible to demonstrate the method. More complex examples should go - in the `examples` tree. +Type hints can be validated by the `stubtest +`_ tool, which can be run +locally using ``tox -e stubtest`` and is a part of the :ref:`automated-tests` +suite. Type hints for existing functions are also checked by the mypy +:ref:`pre-commit hook `. -* Build the docs and make sure all formatting warnings are addressed. -* See :ref:`documenting-matplotlib` for our documentation style guide. +New modules and files: installation +=================================== -* If your changes are non-trivial, please make an entry in the - :file:`CHANGELOG`. - -* If your change is a major new feature, add an entry to - :file:`doc/users/whats_new.rst`. - -* If you change the API in a backward-incompatible way, please - document it in :file:`doc/api/api_changes.rst`. - -Testing -------- - -Using the test framework is discussed in detail in the section -:ref:`testing`. - -* If the PR is a bugfix, add a test that fails prior to the change and - passes with the change. Include any relevant issue numbers in the - docstring of the test. - -* If this is a new feature, add a test that exercises as much of the - new feature as possible. (The `--with-coverage` option may be - useful here). - -* Make sure the Travis tests are passing before merging. - - - The Travis tests automatically test on all of the Python versions - matplotlib supports whenever a pull request is created or updated. - The `tox` support in matplotlib may be useful for testing locally. - -Installation ------------- - -* If you have added new files or directories, or reorganized existing - ones, make sure the new files included in the match patterns in - :file:`MANIFEST.in`, and/or in `package_data` in `setup.py`. +* If you have added new files or directories, or reorganized existing ones, make sure the + new files are included in the :file:`meson.build` in the corresponding directories. +* New modules *may* be typed inline or using parallel stub file like existing modules. C/C++ extensions ----------------- +================ * Extensions may be written in C or C++. * Code style should conform to PEP7 (understanding that PEP7 doesn't address C++, but most of its admonitions still apply). -* Interfacing with Python may be done either with the raw Python/C API - or Cython. - * Python/C interface code should be kept separate from the core C/C++ - code. The interface code should be named `FOO_wrap.cpp` or - `FOO_wrapper.cpp`. + code. The interface code should be named :file:`FOO_wrap.cpp` or + :file:`FOO_wrapper.cpp`. * Header file documentation (aka docstrings) should be in Numpydoc format. We don't plan on using automated tools for these docstrings, and the Numpydoc format is well understood in the scientific Python community. - - -PR Review guidelines -==================== - -* If you have a commit bit, then you are trusted to use it. Please - help review and merge PRs! - -* Two developers (those with commit rights) should review all pull - requests. If you are the first to review a PR please and approve of - the changes please edit the title to include ``'[MRG+1]'`` and use - the github `'approve review' - `__ - tool to mark it as such. If you are a subsequent reviewer and you - approve either merge (and backport if needed) or increment the - number in the title to ask for further review (and trigger the gh - 'approve review'). If you do the merge please removed the - ``'[MRG+N']`` prefix. - -* Do not self merge, except for 'small' patches to un-break the CI. - -* Squashing is case-by-case. The balance is between burden on the - contributor, keeping a relatively clean history, and keeping a - history usable for bisecting. The only time we are really strict - about it is to eliminate binary files (ex multiple test image - re-generations) and to remove upstream merges. - -* Be patient with new contributors. - -* Do not let perfect be the enemy of the good, particularly for - documentation or example PRs. If you find yourself making many - small suggestions, either open a PR against the original branch or - merge the PR and then open a new PR against upstream. - - - -Backports -========= - - -When doing backports please include the branch you backported the -commit to along with the SHA in a comment on the original PR. - -Assuming we have ``matplotlib`` as a read-only remote to the -matplotlib/matplotlib repo and ``DANGER`` as a read/write remote to -the matplotlib/matplotlib repo, we do a backport from master to 2.x. -The ``TARGET_SHA`` is the hash of the merge commit you would like to -backport. This can be read off of the github PR page (in the UI with -the merge notification) or through the git CLI tools.:: - - git fetch matplotlib - git checkout v2.x - git merge --ff-only matplotlib/v2.x - git cherry-pick -m 1 TARGET_SHA - gitk # to look at it - # local tests? (use your judgment) - git push DANGER v2.x - # leave a comment on PR noting sha of the resulting commit - # from the cherry-pick + branch it was moved to - -These commands work on git 2.7.1. - - -Style guide -=========== +* C/C++ code in the :file:`extern/` directory is vendored, and should be kept + close to upstream whenever possible. It can be modified to fix bugs or + implement new features only if the required changes cannot be made elsewhere + in the codebase. In particular, avoid making style fixes to it. .. _keyword-argument-processing: Keyword argument processing ---------------------------- +=========================== -Matplotlib makes extensive use of ``**kwargs`` for pass-through -customizations from one function to another. A typical example is in -:func:`matplotlib.pylab.text`. The definition of the pylab text -function is a simple pass-through to -:meth:`matplotlib.axes.Axes.text`:: +Matplotlib makes extensive use of ``**kwargs`` for pass-through customizations +from one function to another. A typical example is +`~matplotlib.axes.Axes.text`. The definition of `matplotlib.pyplot.text` is a +simple pass-through to `matplotlib.axes.Axes.text`:: - # in pylab.py - def text(*args, **kwargs): - ret = gca().text(*args, **kwargs) - draw_if_interactive() - return ret + # in pyplot.py + def text(x, y, s, fontdict=None, **kwargs): + return gca().text(x, y, s, fontdict=fontdict, **kwargs) -:meth:`~matplotlib.axes.Axes.text` in simplified form looks like this, -i.e., it just passes all ``args`` and ``kwargs`` on to -:meth:`matplotlib.text.Text.__init__`:: +`matplotlib.axes.Axes.text` (simplified for illustration) just +passes all ``args`` and ``kwargs`` on to ``matplotlib.text.Text.__init__``:: - # in axes.py - def text(self, x, y, s, fontdict=None, withdash=False, **kwargs): + # in axes/_axes.py + def text(self, x, y, s, fontdict=None, **kwargs): t = Text(x=x, y=y, text=s, **kwargs) -and :meth:`~matplotlib.text.Text.__init__` (again with liberties for -illustration) just passes them on to the -:meth:`matplotlib.artist.Artist.update` method:: +and ``matplotlib.text.Text.__init__`` (again, simplified) +just passes them on to the `matplotlib.artist.Artist.update` method:: # in text.py def __init__(self, x=0, y=0, text='', **kwargs): - Artist.__init__(self) + super().__init__() self.update(kwargs) ``update`` does the work looking for methods named like @@ -276,113 +173,148 @@ on, use the key/value keyword args in the function definition rather than the ``**kwargs`` idiom. In some cases, you may want to consume some keys in the local -function, and let others pass through. You can ``pop`` the ones to be -used locally and pass on the rest. For example, in +function, and let others pass through. Instead of popping arguments to +use off ``**kwargs``, specify them as keyword-only arguments to the local +function. This makes it obvious at a glance which arguments will be +consumed in the function. For example, in :meth:`~matplotlib.axes.Axes.plot`, ``scalex`` and ``scaley`` are local arguments and the rest are passed on as :meth:`~matplotlib.lines.Line2D` keyword arguments:: - # in axes.py - def plot(self, *args, **kwargs): - scalex = kwargs.pop('scalex', True) - scaley = kwargs.pop('scaley', True) - if not self._hold: self.cla() + # in axes/_axes.py + def plot(self, *args, scalex=True, scaley=True, **kwargs): lines = [] for line in self._get_lines(*args, **kwargs): self.add_line(line) lines.append(line) -Note: there is a use case when ``kwargs`` are meant to be used locally -in the function (not passed on), but you still need the ``**kwargs`` -idiom. That is when you want to use ``*args`` to allow variable -numbers of non-keyword args. In this case, python will not allow you -to use named keyword args after the ``*args`` usage, so you will be -forced to use ``**kwargs``. An example is -:meth:`matplotlib.contour.ContourLabeler.clabel`:: +.. _using_logging: + +Using logging for debug messages +================================ + +Matplotlib uses the standard Python `logging` library to write verbose +warnings, information, and debug messages. Please use it! In all those places +you write `print` calls to do your debugging, try using `logging.debug` +instead! + + +To include `logging` in your module, at the top of the module, you need to +``import logging``. Then calls in your code like:: + + _log = logging.getLogger(__name__) # right after the imports - # in contour.py - def clabel(self, *args, **kwargs): - fontsize = kwargs.get('fontsize', None) - inline = kwargs.get('inline', 1) - self.fmt = kwargs.get('fmt', '%1.3f') - colors = kwargs.get('colors', None) - if len(args) == 0: - levels = self.levels - indices = range(len(self.levels)) - elif len(args) == 1: - ...etc... + # code + # more code + _log.info('Here is some information') + _log.debug('Here is some more detailed information') + +will log to a logger named ``matplotlib.yourmodulename``. + +If an end-user of Matplotlib sets up `logging` to display at levels more +verbose than ``logging.WARNING`` in their code with the Matplotlib-provided +helper:: + + plt.set_loglevel("DEBUG") + +or manually with :: + + import logging + logging.basicConfig(level=logging.DEBUG) + import matplotlib.pyplot as plt + +Then they will receive messages like + +.. code-block:: none + + DEBUG:matplotlib.backends:backend MacOSX version unknown + DEBUG:matplotlib.yourmodulename:Here is some information + DEBUG:matplotlib.yourmodulename:Here is some more detailed information + +Avoid using pre-computed strings (``f-strings``, ``str.format``,etc.) for logging because +of security and performance issues, and because they interfere with style handlers. For +example, use ``_log.error('hello %s', 'world')`` rather than ``_log.error('hello +{}'.format('world'))`` or ``_log.error(f'hello {s}')``. + +Which logging level to use? +--------------------------- -Hints -===== +There are five levels at which you can emit messages. -This section describes how to add certain kinds of new features to -matplotlib. +- `logging.critical` and `logging.error` are really only there for errors that + will end the use of the library but not kill the interpreter. +- `logging.warning` and `._api.warn_external` are used to warn the user, + see below. +- `logging.info` is for information that the user may want to know if the + program behaves oddly. They are not displayed by default. For instance, if + an object isn't drawn because its position is ``NaN``, that can usually + be ignored, but a mystified user could call + ``logging.basicConfig(level=logging.INFO)`` and get an error message that + says why. +- `logging.debug` is the least likely to be displayed, and hence can be the + most verbose. "Expected" code paths (e.g., reporting normal intermediate + steps of layouting or rendering) should only log at this level. -.. _custom_backend: +By default, `logging` displays all log messages at levels higher than +``logging.WARNING`` to `sys.stderr`. -Developing a new backend ------------------------- +The `logging tutorial`_ suggests that the difference between `logging.warning` +and `._api.warn_external` (which uses `warnings.warn`) is that +`._api.warn_external` should be used for things the user must change to stop +the warning (typically in the source), whereas `logging.warning` can be more +persistent. Moreover, note that `._api.warn_external` will by default only +emit a given warning *once* for each line of user code, whereas +`logging.warning` will display the message every time it is called. -If you are working on a custom backend, the *backend* setting in -:file:`matplotlibrc` (:ref:`customizing-matplotlib`) supports an -external backend via the ``module`` directive. if -:file:`my_backend.py` is a matplotlib backend in your -:envvar:`PYTHONPATH`, you can set use it on one of several ways +By default, `warnings.warn` displays the line of code that has the ``warn`` +call. This usually isn't more informative than the warning message itself. +Therefore, Matplotlib uses `._api.warn_external` which uses `warnings.warn`, +but goes up the stack and displays the first line of code outside of +Matplotlib. For example, for the module:: -* in matplotlibrc:: + # in my_matplotlib_module.py + import warnings - backend : module://my_backend + def set_range(bottom, top): + if bottom == top: + warnings.warn('Attempting to set identical bottom==top') +running the script:: -* with the :envvar:`MPLBACKEND` environment variable:: + from matplotlib import my_matplotlib_module + my_matplotlib_module.set_range(0, 0) # set range - > export MPLBACKEND="module://my_backend" - > python simple_plot.py +will display -* from the command shell with the `-d` flag:: +.. code-block:: none - > python simple_plot.py -dmodule://my_backend + UserWarning: Attempting to set identical bottom==top + warnings.warn('Attempting to set identical bottom==top') -* with the use directive in your script:: +Modifying the module to use `._api.warn_external`:: - import matplotlib - matplotlib.use('module://my_backend') + from matplotlib import _api -.. _sample-data: + def set_range(bottom, top): + if bottom == top: + _api.warn_external('Attempting to set identical bottom==top') -Writing examples ----------------- +and running the same script will display -We have hundreds of examples in subdirectories of -:file:`matplotlib/examples`, and these are automatically generated -when the website is built to show up both in the `examples -<../examples/index.html>`_ and `gallery -<../gallery.html>`_ sections of the website. +.. code-block:: none -Any sample data that the example uses should be kept small and -distributed with matplotlib in the -`lib/matplotlib/mpl-data/sample_data/` directory. Then in your -example code you can load it into a file handle with:: + UserWarning: Attempting to set identical bottom==top + my_matplotlib_module.set_range(0, 0) # set range - import matplotlib.cbook as cbook - fh = cbook.get_sample_data('mydata.dat') +.. _logging tutorial: https://docs.python.org/3/howto/logging.html#logging-basic-tutorial -.. _new-pyplot-function: -Writing a new pyplot function ------------------------------ +.. _licence-coding-guide: -A large portion of the pyplot interface is automatically generated by the -`boilerplate.py` script (in the root of the source tree). To add or remove -a plotting method from pyplot, edit the appropriate list in `boilerplate.py` -and then run the script which will update the content in -`lib/matplotlib/pyplot.py`. Both the changes in `boilerplate.py` and -`lib/matplotlib/pyplot.py` should be checked into the repository. +.. include:: license.rst + :start-line: 2 -Note: boilerplate.py looks for changes in the installed version of matplotlib -and not the source tree. If you expect the pyplot.py file to show your new -changes, but they are missing, this might be the cause. +.. toctree:: + :hidden: -Install your new files by running `python setup.py build` and `python setup.py -install` followed by `python boilerplate.py`. The new pyplot.py file should now -have the latest changes. + license.rst diff --git a/doc/devel/color_changes.rst b/doc/devel/color_changes.rst deleted file mode 100644 index 5bb13c37fc05..000000000000 --- a/doc/devel/color_changes.rst +++ /dev/null @@ -1,135 +0,0 @@ -.. _color_changes: - -********************* -Default Color changes -********************* - -As discussed at length elsewhere [insert links], ``jet`` is an -empirically bad color map and should not be the default color map. -Due to the position that changing the appearance of the plot breaks -backward compatibility, this change has been put off for far longer -than it should have been. In addition to changing the default color -map we plan to take the chance to change the default color-cycle on -plots and to adopt a different color map for filled plots (``imshow``, -``pcolor``, ``contourf``, etc) and for scatter like plots. - - -Default Heat Map Colormap -------------------------- - -The choice of a new color map is fertile ground to bike-shedding ("No, -it should be _this_ color") so we have a proposed set criteria (via -Nathaniel Smith) to evaluate proposed color maps. - -- it should be a sequential colormap, because diverging colormaps are - really misleading unless you know where the "center" of the data is, - and for a default colormap we generally won't. - -- it should be perceptually uniform, i.e., human subjective judgments - of how far apart nearby colors are should correspond as linearly as - possible to the difference between the numerical values they - represent, at least locally. - -- it should have a perceptually uniform luminance ramp, i.e. if you - convert to greyscale it should still be uniform. This is useful both - in practical terms (greyscale printers are still a thing!) and - because luminance is a very strong and natural cue to magnitude. - -- it should also have some kind of variation in hue, because hue - variation is a really helpful additional cue to perception, having - two cues is better than one, and there's no reason not to do it. - -- the hue variation should be chosen to produce reasonable results - even for viewers with the more common types of - colorblindness. (Which rules out things like red-to-green.) - -- For bonus points, it would be nice to choose a hue ramp that still - works if you throw away the luminance variation, because then we - could use the version with varying luminance for 2d plots, and the - version with just hue variation for 3d plots. (In 3d plots you - really want to reserve the luminance channel for lighting/shading, - because your brain is *really* good at extracting 3d shape from - luminance variation. If the 3d surface itself has massively varying - luminance then this screws up the ability to see shape.) - -- Not infringe any existing IP - -Example script -++++++++++++++ - -Proposed Colormaps -++++++++++++++++++ - -Default Scatter Colormap ------------------------- - -For heat-map like applications it can be desirable to cover as much of -the luminence scale as possible, however when color mapping markers, -having markers too close to white can be a problem. For that reason -we propose using a different (but maybe related) color map to the -heat map for marker-based. The design parameters are the same as -above, only with a more limited luminence variation. - - -Example script -++++++++++++++ -:: - - import numpy as np - import matplotlib.pyplot as plt - - np.random.seed(1234) - - fig, (ax1, ax2) = plt.subplots(1, 2) - - N = 50 - x = np.random.rand(N) - y = np.random.rand(N) - colors = np.random.rand(N) - area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radiuses - - ax1.scatter(x, y, s=area, c=colors, alpha=0.5) - - - X,Y = np.meshgrid(np.arange(0, 2*np.pi, .2), - np.arange(0, 2*np.pi, .2)) - U = np.cos(X) - V = np.sin(Y) - Q = ax2.quiver(X, Y, U, V, units='width') - qd = np.random.rand(np.prod(X.shape)) - Q.set_array(qd) - -Proposed Colormaps -++++++++++++++++++ - -Color Cycle / Qualitative color map ------------------------------------ - -When plotting lines it is frequently desirable to plot multiple lines -or artists which need to be distinguishable, but there is no inherent -ordering. - - -Example script -++++++++++++++ -:: - - import numpy as np - import matplotlib.pyplot as plt - - fig, (ax1, ax2) = plt.subplots(1, 2) - - x = np.linspace(0, 1, 10) - - for j in range(10): - ax1.plot(x, x * j) - - - th = np.linspace(0, 2*np.pi, 1024) - for j in np.linspace(0, np.pi, 10): - ax2.plot(th, np.sin(th + j)) - - ax2.set_xlim(0, 2*np.pi) - -Proposed Color cycle -++++++++++++++++++++ diff --git a/doc/devel/communication_guide.rst b/doc/devel/communication_guide.rst new file mode 100644 index 000000000000..c90d1d93b99d --- /dev/null +++ b/doc/devel/communication_guide.rst @@ -0,0 +1,269 @@ +.. _communications-guidelines: + +========================== +Community management guide +========================== + +These guidelines are applicable when **acting as a representative** of Matplotlib, +for example at sprints or when giving official talks or tutorials, and in any +community venue managed by Matplotlib. + +Our approach to community engagement is foremost guided by our :ref:`mission-statement`: + +* We demonstrate that we care about visualization as a practice. +* We deepen our practice and the community’s capacity to support users, + facilitate exploration, produce high quality visualizations, and be + understandable and extensible. +* We showcase advanced use of the library without adding maintenance burden to + the documentation and recognize contributions that happen outside of the github + workflow. +* We use communications platforms to maintain relationships with contributors + who may no longer be active on GitHub, build relationships with potential + contributors, and connect with other projects and communities who use + Matplotlib. +* In prioritizing understandability and extensibility, we recognize that people + using Matplotlib, in whatever capacity, are part of our community. Doing so + empowers our community members to build community with each other, for example + by creating educational resources, building third party tools, and building + informal mentoring networks. + +.. _communication-channels: + +Official communication channels +=============================== +The Scientific Python community uses various communications platforms to stay +updated on new features and projects, to contribute by telling us what is on +their mind and suggest issues and bugs, and to showcase their use cases and the +tools they have built. + +The following venues are managed by Matplotlib maintainers and contributors: + +* library and docs: https://github.com/matplotlib/matplotlib +* forum: https://discourse.matplotlib.org/ +* chat: `https://matrix.to/#/#matplotlib:matrix.org `_ +* blog: https://blog.scientific-python.org/ + +.. _social-media: + +Social media +------------ + +Active social media +^^^^^^^^^^^^^^^^^^^ + +* https://bsky.app/profile/matplotlib.bsky.social +* https://fosstodon.org/@matplotlib +* https://x.com/matplotlib +* https://instagram.com/matplotart/ + +Official accounts +^^^^^^^^^^^^^^^^^ + +* https://www.tiktok.com/@matplotart +* https://www.youtube.com/matplotlib + + +.. _mailing-lists: + +Mailing lists +------------- + +* `matplotlib-announce@python.org `_ +* `matplotlib-users@python.org `_ +* `matplotlib-devel@python.org `_ + +.. _social-media-coordination: + +Social media coordination +------------------------- +* Team mailing list: matplotlib-social@numfocus.org +* Public chat room: `https://matrix.to/#/#matplotlib_community:gitter.im `_ + + +Maintenance +----------- + +If you are interested in moderating the chat or forum or accessing the social +media accounts: + +* Matplotlib maintainers should reach out to the `community-manager`_. + +* Everyone else should send an email to matplotlib-social-admin@numfocus.org: + + * Introduce yourself - GitHub handle and participation in the community. + * Describe the reason for wanting to moderate or contribute to social. + + +Content guidelines +================== + +Communication on official channels, such as the Matplotlib homepage or on +Matplotlib social accounts, should conform to the following standards. If you +are unsure if content that you would like to post or share meets these +guidelines, ask on the :ref:`social-media-coordination` channels before posting. + +General guidelines +------------------ + +* Do not share information that violates Matplotlib's :ref:`code of conduct ` or does not align with Matplotlib's :ref:`mission-statement`. + +* Focus on Matplotlib, 3rd party packages, and visualizations made with Matplotlib. +* These are also acceptable topics: + + * Visualization best practices and libraries. + * Projects and initiatives by NumFOCUS and Scientific Python. + * How to contribute to open source projects. + * Projects, such as scientific papers, that use Matplotlib. + +* No gratuitous disparaging of other visualization libraries and tools, but + criticism is acceptable so long as it serves a constructive purpose. + +* Follow communication best practices: + + * Do not share non-expert visualizations when it could cause harm, e.g.: + + * Could the information affect someone's decisions in a way that impacts their personal health or safety? + * Could the information be used as part of a politicised debate? + + * Clearly state when the visualization data/conclusions cannot be verified. + * Do not rely on machine translations for sensitive visualization. + +* Verify sourcing of content (especially on Instagram & blog): + + * Instagram/blog: ensure mpl has right to repost/share content + * Make sure content is clearly cited: + + * e.g. a tutorial reworking an example must credit the original example + +* Limited self/corporate promotion is acceptable. + + * Should be no more than about a quarter of the content. + +Visual media guidelines +----------------------- + +Visual media, such as images and videos, must not violate the +:ref:`code of conduct `, nor any platform's rules. +Specifically: + +* Visual media must conform to the guidelines of all sites it may be posted on: + + * https://help.x.com/en/rules-and-policies/x-rules + * https://help.instagram.com/477434105621119 + +* Emphasize the visualization techniques demonstrated by the visual media. +* Clearly state that sharing is not an endorsement of the content. + + * e.g. bitcoin related visualizations + +Accessibility +^^^^^^^^^^^^^ + +Visual media in communications should be made as accessible as possible: + +* Add alt text to images and videos when the platform allows: + + * `alt text for data viz `_ + * `general alt text guide `_ + +* Warn on bright, strobing, images & turn off autoplay if possible. +* For images and videos made by the social media team: + + * Make graphic perceivable to people who cannot perceive color well due to + color-blindness, low vision, or any other reason. + + * Do not make bright, strobing images. + * More guidelines at https://webaim.org/techniques/images/. + +.. _social-media-brand: + +Social media +============ + +Matplotlib aims for a single voice across all social media platforms to build and +maintain a consistent brand identity for Matplotlib as an organization. This +depersonalization is the norm on social media platforms because it enables +constructive and productive conversations; People generally feel more comfortable +giving negative and constructive feedback to a brand than to specific contributors. + +The current Matplotlib voice and persona aims to be kind, patient, supportive and +educational. This is so that it can de-escalate tensions and facilitate +constructive conversations; being perceived as negative or +argumentative can escalate very fast into long-lasting brand damage, being +perceived as personal leads to aggression and accusations faster than an +impersonal account, and being perceived as friendly and approachable leads to +higher engagement. Instead of speaking with a directive authority, which can be +intimidating and lead to negative engagement, it speaks as a peer or educator to +empower participation. The current voice encourages more input from folks we +engage with, and also makes it possible for folks who are not in the core team +to participate in managing the account. + +While the :ref:`brand identity ` is casual, the showcased +content is high quality, peer-led resource building. Please follow these +guidelines to maintain a consistent brand identity across platforms. + +Persona +------- +On social media, Matplotlib: + +* Acts as a sentient visualization library, so talks about itself as a we, us, + our, and it. Avoids talking about itself in the 3rd person. Never uses 1st person. +* Is very earnest, eager to please, and aims to be patient & painfully oblivious + to snark and sarcasm. +* Gets over-excited over shiny visualizations - lots of emojis and the like - + and encourages folks to share their work. +* Highlights various parts of the library, especially the more obscure bits and + bobbles. +* Acknowledges that it is a sometimes frustrating tangle of bits & bobbles that + can confuse even the folks who work on it & signal boosts their confuzzlement. + + +Behavior +-------- +When acting as a representative of the library, keep responses polite and assume +user statements are in good faith unless they violate the :ref:`code of conduct `. + +Social graph +------------ + +Only follow **organizations and projects**, do not follow individual accounts for +any reason, even maintainers/project leads/famous Python people! + +Following these types of accounts is encouraged: + +* NumFocus and Scientific Python projects +* 3rd party packages +* Visualization related projects and organizations +* Open Source community projects +* Sponsors + +Recurring campaigns +------------------- + +Typically the social media accounts will promote the following: + +* Matplotlib releases: + + * Highlight new features & major deprecations + * Link to download/install instructions + * Ask folks to try it out. + +* `third party packages `_ +* NumFocus/Scientific Python/open source visualization project releases +* GSOC/GSOD recruiting and progress + +Retired campaigns +^^^^^^^^^^^^^^^^^ +* John Hunter Excellence in Plotting, submission and winners + + +Changing the guidelines +======================= + +As the person tasked with implementing these guidelines, the `community-manager`_ +should be alerted to proposed changes. Similarly, specific platform guidelines +(e.g. X, Instagram) should be reviewed by the person responsible for that +platform, when different from the community manager. If there is no consensus, +decisions about guidelines revert to the community manager. + +.. _community-manager: https://matplotlib.org/governance/people.html#deputy-project-leads diff --git a/doc/devel/contribute.rst b/doc/devel/contribute.rst new file mode 100644 index 000000000000..bd4fe8e64c52 --- /dev/null +++ b/doc/devel/contribute.rst @@ -0,0 +1,357 @@ +.. redirect-from:: /devel/contributing + +.. _contributing: + +****************** +Contributing guide +****************** +You've discovered a bug or something else you want to change +in Matplotlib — excellent! + +You've worked out a way to fix it — even better! + +You want to tell us about it — best of all! + +Below, you can find a number of ways to contribute, and how to connect with the +Matplotlib community. + +Ways to contribute +================== +.. dropdown:: Do I really have something to contribute to Matplotlib? + :open: + :icon: person-fill + + 100% yes! There are so many ways to contribute to our community. Take a look + at the following sections to learn more. + + There are a few typical new contributor profiles: + + * **You are a Matplotlib user, and you see a bug, a potential improvement, or + something that annoys you, and you can fix it.** + + You can search our `issue tracker `__ + for an existing issue that describes your problem or + open a new issue to inform us of the problem you observed and discuss the best approach + to fix it. If your contributions would not be captured on GitHub (social media, + communication, educational content), you can also reach out to us on gitter_, + `Discourse `__ or attend any of our `community + meetings `__. + + * **You are not a regular Matplotlib user but a domain expert: you know about + visualization, 3D plotting, design, technical writing, statistics, or some + other field where Matplotlib could be improved.** + + Awesome — you have a focus on a specific application and domain and can + start there. In this case, maintainers can help you figure out the best + implementation; `open an issue `__ + in our issue tracker, and we'll be happy to discuss technical approaches. + + If you can implement the solution yourself, even better! Consider contributing + the change as a :ref:`pull request ` right away. + + * **You are new to Matplotlib, both as a user and contributor, and want to start + contributing but have yet to develop a particular interest.** + + Having some previous experience or relationship with the library can be very + helpful when making open-source contributions. It helps you understand why + things are the way they are and how they *should* be. Having first-hand + experience and context is valuable both for what you can bring to the + conversation (and given the breadth of Matplotlib's usage, there is a good + chance it is a unique context in any given conversation) and make it easier to + understand where other people are coming from. + + Understanding the entire codebase is a long-term project, and nobody expects + you to do this right away. If you are determined to get started with + Matplotlib and want to learn, going through the basic functionality, + choosing something to focus on (3d, testing, documentation, animations, etc.) + and gaining context on this area by reading the issues and pull requests + touching these subjects is a reasonable approach. + +.. _contribute_code: + +Code +---- +You want to implement a feature or fix a bug or help with maintenance - much +appreciated! Our library source code is found in: + +* Python library code: :file:`lib/` +* C-extension code: :file:`src/` +* Tests: :file:`lib/matplotlib/tests/` + +Because many people use and work on Matplotlib, we have guidelines for keeping +our code consistent and mitigating the impact of changes. + +* :ref:`coding_guidelines` +* :ref:`api_changes` +* :ref:`pr-guidelines` + +Code is contributed through pull requests, so we recommend that you start at +:ref:`how-to-pull-request` If you get stuck, please reach out on the +:ref:`contributor_incubator` + +.. _contribute_documentation: + +Documentation +------------- + +You, as an end-user of Matplotlib can make a valuable contribution because you can +more clearly see the potential for improvement than a core developer. For example, +you can: + +- Fix a typo +- Clarify a docstring +- Write or update an :ref:`example plot ` +- Write or update a comprehensive :ref:`tutorial ` + +Our code is documented inline in the source code files in :file:`matplotlib/lib`. +Our website structure mirrors our folder structure, meaning that a narrative +document's URL roughly corresponds to its location in our folder structure: + +.. grid:: 1 1 2 2 + + .. grid-item:: using the library + + * :file:`galleries/plot_types/` + * :file:`users/getting_started/` + * :file:`galleries/user_explain/` + * :file:`galleries/tutorials/` + * :file:`galleries/examples/` + * :file:`doc/api/` + + .. grid-item:: information about the library + + * :file:`doc/install/` + * :file:`doc/project/` + * :file:`doc/devel/` + * :file:`doc/users/resources/index.rst` + * :file:`doc/users/faq.rst` + + +Other documentation is generated from the following external sources: + +* matplotlib.org homepage: https://github.com/matplotlib/mpl-brochure-site +* cheat sheets: https://github.com/matplotlib/cheatsheets +* third party packages: https://github.com/matplotlib/mpl-third-party + +Instructions and guidelines for contributing documentation are found in: + +* :doc:`document` +* :doc:`style_guide` +* :doc:`tag_guidelines` + +Documentation is contributed through pull requests, so we recommend that you start +at :ref:`how-to-pull-request`. If that feels intimidating, we encourage you to +`open an issue`_ describing what improvements you would make. If you get stuck, +please reach out on the :ref:`contributor_incubator` + +.. _`open an issue`: https://github.com/matplotlib/matplotlib/issues/new?assignees=&labels=Documentation&projects=&template=documentation.yml&title=%5BDoc%5D%3A+ + +.. _contribute_triage: + +Triage +------ +We appreciate your help keeping the `issue tracker `_ +organized because it is our centralized location for feature requests, +bug reports, tracking major projects, and discussing priorities. Some examples of what +we mean by triage are: + +* labeling issues and pull requests +* verifying bug reports +* debugging and resolving issues +* linking to related issues, discussion, and external work + +Our triage process is discussed in detail in :ref:`bug_triaging`. + +If you have any questions about the process, please reach out on the +:ref:`contributor_incubator` + +.. _other_ways_to_contribute: + +Community +--------- +Matplotlib's community is built by its members, if you would like to help out +see our :ref:`communications-guidelines`. + +It helps us if you spread the word: reference the project from your blog +and articles or link to it from your website! + +If Matplotlib contributes to a project that leads to a scientific publication, +please cite us following the :doc:`/project/citing` guidelines. + +If you have developed an extension to Matplotlib, please consider adding it to our +`third party package `_ list. + + +.. _generative_ai: + + +Restrictions on Generative AI Usage +=================================== + +We expect authentic engagement in our community. + +- Do not post output from Large Language Models or similar generative AI as + comments on GitHub or our discourse server, as such comments tend to be + formulaic and low content. +- If you use generative AI tools as an aid in developing code or documentation + changes, ensure that you fully understand the proposed changes and can + explain why they are the correct approach. + +Make sure you have added value based on your personal competency to your +contributions. Just taking some input, feeding it to an AI and posting the +result is not of value to the project. To preserve precious core developer +capacity, we reserve the right to rigorously reject seemingly AI generated +low-value contributions. + +.. _new_contributors: + +New contributors +================ + +Everyone comes to the project from a different place — in terms of experience +and interest — so there is no one-size-fits-all path to getting involved. We +recommend looking at existing issue or pull request discussions, and following +the conversations during pull request reviews to get context. Or you can +deep-dive into a subset of the code-base to understand what is going on. + +.. _new_contributors_meeting: + +New contributors meeting +------------------------ + +Once a month, we host a meeting to discuss topics that interest new +contributors. Anyone can attend, present, or sit in and listen to the call. +Among our attendees are fellow new contributors, as well as maintainers, and +veteran contributors, who are keen to support onboarding of new folks and +share their experience. You can find our community calendar link at the +`Scientific Python website `_, and +you can browse previous meeting notes on `GitHub +`_. +We recommend joining the meeting to clarify any doubts, or lingering +questions you might have, and to get to know a few of the people behind the +GitHub handles 😉. You can reach out to us on gitter_ for any clarifications or +suggestions. We ❤ feedback! + +.. _contributor_incubator: + +Contributor incubator +--------------------- + +The incubator is our non-public communication channel for new contributors. It +is a private gitter_ (chat) room moderated by core Matplotlib developers where +you can get guidance and support for your first few PRs. It's a place where you +can ask questions about anything: how to use git, GitHub, how our PR review +process works, technical questions about the code, what makes for good +documentation or a blog post, how to get involved in community work, or get a +"pre-review" on your PR. + +To join, please go to our public `community gitter`_ channel, and ask to be added to +``#incubator``. One of our core developers will see your message and will add you. + +.. _gitter: https://gitter.im/matplotlib/matplotlib +.. _community gitter: https://gitter.im/matplotlib/community + +.. _good_first_issues: + +Good first issues +----------------- + +While any contributions are welcome, we have marked some issues as +particularly suited for new contributors by the label `good first issue +`_. These +are well documented issues, that do not require a deep understanding of the +internals of Matplotlib. The issues may additionally be tagged with a +difficulty. ``Difficulty: Easy`` is suited for people with little Python +experience. ``Difficulty: Medium`` and ``Difficulty: Hard`` require more +programming experience. This could be for a variety of reasons, among them, +though not necessarily all at the same time: + +- The issue is in areas of the code base which have more interdependencies, + or legacy code. +- It has less clearly defined tasks, which require some independent + exploration, making suggestions, or follow-up discussions to clarify a good + path to resolve the issue. +- It involves Python features such as decorators and context managers, which + have subtleties due to our implementation decisions. + +.. _first_contribution: + +First contributions +------------------- + +If this is your first open source contribution, or your first time contributing to Matplotlib, +and you need help or guidance finding a good first issue, look no further. This section will +guide you through each step: + +1. Navigate to the `issues page `_. +2. Filter labels with `"Difficulty: Easy" `_ + & `"Good first Issue" `_ (optional). +3. Click on an issue you would like to work on, and check to see if the issue has a pull request opened to resolve it. + + * A good way to judge if you chose a suitable issue is by asking yourself, "Can I independently submit a PR in 1-2 weeks?" +4. Check existing pull requests (e.g., :ghpull:`28476`) and filter by the issue number to make sure the issue is not in progress: + + * If the issue has a pull request (is in progress), tag the user working on the issue, and ask to collaborate (optional). + * If there is no pull request, :ref:`create a new pull request `. +5. Please familiarize yourself with the pull request template (see below), + and ensure you understand/are able to complete the template when you open your pull request. + Additional information can be found in the `pull request guidelines `_. + +.. dropdown:: `Pull request template `_ + :open: + + .. literalinclude:: ../../.github/PULL_REQUEST_TEMPLATE.md + :language: markdown + +.. _get_connected: + +Get connected +============= + +When in doubt, we recommend going together! Get connected with our community of +active contributors, many of whom felt just like you when they started out and +are happy to welcome you and support you as you get to know how we work, and +where things are. You can reach out on any of our :ref:`communication-channels`. +For development questions we recommend reaching out on our development gitter_ +chat room and for community questions reach out at `community gitter`_. + +.. _managing_issues_prs: + +Choose an issue +=============== + +In general, the Matplotlib project does not assign issues. Issues are +"assigned" or "claimed" by opening a PR; there is no other assignment +mechanism. If you have opened such a PR, please comment on the issue thread to +avoid duplication of work. Please check if there is an existing PR for the +issue you are addressing. If there is, try to work with the author by +submitting reviews of their code or commenting on the PR rather than opening +a new PR; duplicate PRs are subject to being closed. However, if the existing +PR is an outline, unlikely to work, or stalled, and the original author is +unresponsive, feel free to open a new PR referencing the old one. + +.. _how-to-pull-request: + +Start a pull request +==================== + +The preferred way to contribute to Matplotlib is to fork the `main +repository `__ on GitHub, +then submit a "pull request" (PR). To work on a pull request: + +#. **First** set up a development environment, either by cloning a copy of the + Matplotlib repository to your own computer or by using Github codespaces, by + following the instructions in :ref:`installing_for_devs` + +#. **Then** start solving the issue, following the guidance in + :ref:`development workflow ` + +#. **As part of verifying your changes** check that your contribution meets + the :ref:`pull request guidelines ` + and then :ref:`open a pull request `. + +#. **Finally** follow up with maintainers on the PR if waiting more than a few days for + feedback. :ref:`Update the pull request ` as needed. + +If you have questions of any sort, reach out on the :ref:`contributor_incubator` and join +the :ref:`new_contributors_meeting`. diff --git a/doc/devel/development_setup.rst b/doc/devel/development_setup.rst new file mode 100644 index 000000000000..4e452fb3bfe7 --- /dev/null +++ b/doc/devel/development_setup.rst @@ -0,0 +1,356 @@ +.. highlight:: bash + +.. redirect-from:: /devel/gitwash/configure_git +.. redirect-from:: /devel/gitwash/dot2_dot3 +.. redirect-from:: /devel/gitwash/following_latest +.. redirect-from:: /devel/gitwash/forking_hell +.. redirect-from:: /devel/gitwash/git_development +.. redirect-from:: /devel/gitwash/git_install +.. redirect-from:: /devel/gitwash/git_intro +.. redirect-from:: /devel/gitwash/git_resources +.. redirect-from:: /devel/gitwash/patching +.. redirect-from:: /devel/gitwash/set_up_fork +.. redirect-from:: /devel/gitwash/index + +.. _installing_for_devs: + +===================================== +Setting up Matplotlib for development +===================================== + +To set up Matplotlib for development follow these steps: + +.. contents:: + :local: + +Fork the Matplotlib repository +============================== + +Matplotlib is hosted at https://github.com/matplotlib/matplotlib.git. If you +plan on solving issues or submitting pull requests to the main Matplotlib +repository, you should first fork this repository by *clicking* the +:octicon:`repo-forked` **Fork** button near the top of the `project repository `_ page. + +This creates a copy of the code under your account on the GitHub server. See `the GitHub +documentation `__ for more details. + +Set up development environment +============================== + +You can either work locally on your machine, or online in +`GitHub Codespaces`_, a cloud-based in-browser development +environment. + + +:local: If you are making extensive or frequent contributions to Matplotlib then it + is probably worth taking the time to set up on your local machine: As well as + having the convenience of your local familiar tools, you will not need to worry + about Codespace's monthly usage limits. + +:codespaces: If you are making a one-off, relatively simple, change then working in + GitHub Codespaces can be a good option because most of the setting + up is done for you and you can skip the next few sections. + +If you want to use Codespaces, skip to :ref:`development-codespaces`, +otherwise, continue with the next section. + +Create local environment +------------------------ + +Get most recent code +^^^^^^^^^^^^^^^^^^^^ + +Now that your fork of the repository lives under your GitHub username, you can +retrieve the most recent version of the source code with one of the following +commands (replace ```` with your GitHub username): + +.. tab-set:: + + .. tab-item:: https + + .. code-block:: bash + + git clone https://github.com//matplotlib.git + + .. tab-item:: ssh + + .. code-block:: bash + + git clone git@github.com:/matplotlib.git + + This requires you to setup an `SSH key`_ in advance, but saves you from + typing your password at every connection. + + .. _SSH key: https://docs.github.com/en/authentication/connecting-to-github-with-ssh + + +This will place the sources in a directory :file:`matplotlib` below your +current working directory and set the remote name ``origin`` to point to your +fork. Change into this directory before continuing:: + + cd matplotlib + +Now set the remote name ``upstream`` to point to the Matplotlib main repository: + +.. tab-set:: + + .. tab-item:: https + + .. code-block:: bash + + git remote add upstream https://github.com/matplotlib/matplotlib.git + + .. tab-item:: ssh + + .. code-block:: bash + + git remote add upstream git@github.com:matplotlib/matplotlib.git + +You can now use ``upstream`` to retrieve the most current snapshot of the source +code, as described in :ref:`development-workflow`. + +.. dropdown:: Additional ``git`` and ``GitHub`` resources + :color: info + :open: + + For more information on ``git`` and ``GitHub``, see: + + * `Git documentation `_ + * `GitHub-Contributing to a Project + `_ + * `GitHub Skills `_ + * :external+scipy:ref:`using-git` + * :external+scipy:ref:`git-resources` + * `Installing git `_ + * `Managing remote repositories + `_ + * https://tacaswell.github.io/think-like-git.html + * https://tom.preston-werner.com/2009/05/19/the-git-parable.html + +.. _dev-environment: + +Create a dedicated environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You should set up a dedicated environment to decouple your Matplotlib +development from other Python and Matplotlib installations on your system. + +We recommend using one of the following options for a dedicated development environment +because these options are configured to install the Python dependencies as part of their +setup. + +.. _venv: https://docs.python.org/3/library/venv.html +.. _conda: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html + +.. tab-set:: + + .. tab-item:: venv environment + + Create a new `venv`_ environment with :: + + python -m venv + + and activate it with one of the following : + + .. tab-set:: + + .. tab-item:: Linux and macOS + + .. code-block:: bash + + source /bin/activate # Linux/macOS + + .. tab-item:: Windows cmd.exe + + .. code-block:: bat + + \Scripts\activate.bat + + .. tab-item:: Windows PowerShell + + .. code-block:: ps1con + + \Scripts\Activate.ps1 + + On some systems, you may need to type ``python3`` instead of ``python``. + For a discussion of the technical reasons, see `PEP-394 `_. + + Install the Python dependencies with :: + + pip install -r requirements/dev/dev-requirements.txt + + Remember to activate the environment whenever you start working on Matplotlib! + + .. tab-item:: conda environment + + Create a new `conda`_ environment and install the Python dependencies with :: + + conda env create -f environment.yml + + You can use ``mamba`` instead of ``conda`` in the above command if + you have `mamba`_ installed. + + .. _mamba: https://mamba.readthedocs.io/en/latest/ + + Activate the environment using :: + + conda activate mpl-dev + + Remember to activate the environment whenever you start working on Matplotlib! + + +Install external dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Python dependencies were installed as part of :ref:`setting up the environment `. +Additionally, the following non-Python dependencies must also be installed locally: + +.. rst-class:: checklist + +* :ref:`compile-build-dependencies` +* :ref:`external tools used by the documentation build ` + + +For a full list of dependencies, see :ref:`dependencies`. External dependencies do not +need to be installed when working in codespaces. + +.. _development-codespaces: + +Create GitHub Codespace :octicon:`codespaces` +--------------------------------------------- + +`GitHub Codespaces`_ is a cloud-based +in-browser development environment that comes with the appropriate setup to +contribute to Matplotlib. + +#. Open codespaces on your fork by clicking on the green :octicon:`code` ``Code`` + button on the GitHub web interface and selecting the ``Codespaces`` tab. + +#. Next, click on "Open codespaces on ". You will be + able to change branches later, so you can select the default + ``main`` branch. + +#. After the codespace is created, you will be taken to a new browser + tab where you can use the terminal to activate a pre-defined conda + environment called ``mpl-dev``:: + + conda activate mpl-dev + +Remember to activate the *mpl-dev* environment whenever you start working on +Matplotlib. + +If you need to open a GUI window with Matplotlib output on Codespaces, our +configuration includes a `light-weight Fluxbox-based desktop +`_. +You can use it by connecting to this desktop via your web browser. To do this: + +#. Press ``F1`` or ``Ctrl/Cmd+Shift+P`` and select + ``Ports: Focus on Ports View`` in the VSCode session to bring it into + focus. Open the ports view in your tool, select the ``noVNC`` port, and + click the Globe icon. +#. In the browser that appears, click the Connect button and enter the desktop + password (``vscode`` by default). + +Check the `GitHub instructions +`_ +for more details on connecting to the desktop. + +If you also built the documentation pages, you can view them using Codespaces. +Use the "Extensions" icon in the activity bar to install the "Live Server" +extension. Locate the ``doc/build/html`` folder in the Explorer, right click +the file you want to open and select "Open with Live Server." + +.. _Github Codespaces: https://docs.github.com/codespaces + +.. _development-install: + +Install Matplotlib in editable mode +=================================== + +Install Matplotlib in editable mode from the :file:`matplotlib` directory using the +command :: + + python -m pip install --verbose --no-build-isolation --editable ".[dev]" + +The 'editable/develop mode' builds everything and places links in your Python environment +so that Python will be able to import Matplotlib from your development source directory. +This allows you to import your modified version of Matplotlib without having to +re-install after changing a ``.py`` or compiled extension file. + +When working on a branch that does not have Meson enabled, meaning it does not +have :ghpull:`26621` in its history (log), you will have to reinstall from source +each time you change any compiled extension code. + +If the installation is not working, please consult the :ref:`troubleshooting guide `. +If the guide does not offer a solution, please reach out via `chat `_ +or :ref:`open an issue `. + + +Build options +------------- +If you are working heavily with files that need to be compiled, you may want to +inspect the compilation log. This can be enabled by setting the environment +variable :envvar:`MESONPY_EDITABLE_VERBOSE` or by setting the ``editable-verbose`` +config during installation :: + + python -m pip install --no-build-isolation --config-settings=editable-verbose=true --editable . + +For more information on installation and other configuration options, see the +Meson Python :external+meson-python:ref:`editable installs guide `. + +For a list of the other environment variables you can set before install, see :ref:`environment-variables`. + + +Verify the Installation +======================= + +Run the following command to make sure you have correctly installed Matplotlib in +editable mode. The command should be run when the virtual environment is activated:: + + python -c "import matplotlib; print(matplotlib.__file__)" + +This command should return : ``\lib\matplotlib\__init__.py`` + +We encourage you to run tests and build docs to verify that the code installed correctly +and that the docs build cleanly, so that when you make code or document related changes +you are aware of the existing issues beforehand. + +* Run test cases to verify installation :ref:`testing` +* Verify documentation build :ref:`documenting-matplotlib` + +.. _pre-commit-hooks: + +Install pre-commit hooks +======================== +`pre-commit `_ hooks save time in the review process by +identifying issues with the code before a pull request is formally opened. Most +hooks can also aide in fixing the errors, and the checks should have +corresponding :ref:`development workflow ` and +:ref:`pull request ` guidelines. Hooks are configured in +`.pre-commit-config.yaml `_ +and include checks for spelling and formatting, flake 8 conformity, accidentally +committed files, import order, and incorrect branching. + +Install pre-commit hooks :: + + python -m pip install pre-commit + pre-commit install + +Hooks are run automatically after the ``git commit`` stage of the +:ref:`editing workflow`. When a hook has found and fixed an error in a +file, that file must be *staged and committed* again. + +Hooks can also be run manually. All the hooks can be run, in order as +listed in ``.pre-commit-config.yaml``, against the full codebase with :: + + pre-commit run --all-files + +To run a particular hook manually, run ``pre-commit run`` with the hook id :: + + pre-commit run --all-files + + +Please note that the ``mypy`` pre-commit hook cannot check the :ref:`type-hints` +for new functions; instead the stubs for new functions are checked using the +``stubtest`` :ref:`CI check ` and can be checked locally using +``tox -e stubtest``. diff --git a/doc/devel/development_workflow.rst b/doc/devel/development_workflow.rst new file mode 100644 index 000000000000..c26b5eb9ae4e --- /dev/null +++ b/doc/devel/development_workflow.rst @@ -0,0 +1,595 @@ +.. highlight:: bash + +.. redirect-from:: /devel/gitwash/development_workflow +.. redirect-from:: /devel/gitwash/maintainer_workflow + +.. _development-workflow: + +#################### +Development workflow +#################### + +Workflow summary +================ + +To keep your work well organized, with readable history, and in turn make it +easier for project maintainers (that might be you) to see what you've done, and +why you did it, we recommend the following: + +* Don't make changes in your local ``main`` branch! +* Before starting a new set of changes, fetch all changes from + ``upstream/main``, and start a new *feature branch* from that. +* Make a new branch for each feature or bug fix — "one task, one branch". +* Name your branch for the purpose of the changes - e.g. + ``bugfix-for-issue-14`` or ``refactor-database-code``. +* If you get stuck, reach out on Gitter or + `discourse `__. +* When you're ready or need feedback on your code, open a pull request so that the + Matplotlib developers can give feedback and eventually include your suggested + code into the ``main`` branch. + +Overview +-------- + +After :ref:`setting up a development environment `, the typical +workflow is: + +#. Fetch all changes from ``upstream/main``:: + + git fetch upstream + +#. Start a new *feature branch* from ``upstream/main``:: + + git checkout -b my-new-feature upstream/main + +#. When you're done editing, e.g., ``lib/matplotlib/collections.py``, record your changes in Git:: + + git add lib/matplotlib/collections.py + git commit -m 'a commit message' + +#. Push the changes to your GitHub fork:: + + git push -u origin my-new-feature + + +.. _update-mirror-main: + +Update the ``main`` branch +========================== + +First make sure you have followed :ref:`installing_for_devs`. + +From time to time you should fetch the upstream changes from GitHub:: + + git fetch upstream + +This will pull down any commits you don't have, and set the remote branches to +point to the right commit. + +.. _make-feature-branch: + +Make a new feature branch +========================= + +When you are ready to make some changes to the code, you should start a new +branch. Branches that are for a collection of related edits are often called +'feature branches'. Making a new branch for each set of related changes will make it +easier for someone reviewing your branch to see what you are doing. + +Choose an informative name for the branch to remind yourself and the rest of us +what the changes in the branch are for. For example ``add-ability-to-fly``, or +``bugfix-for-issue-42``. + +The process for creating a new feature branch is:: + + # Update the main branch + git fetch upstream + # Make new feature branch starting at current main + git branch my-new-feature upstream/main + git checkout my-new-feature + +If you started making changes on your local ``main`` branch, you can convert the +branch to a feature branch by renaming it:: + + git branch -m + +Generally, you will want to keep your feature branches on your public GitHub +fork of Matplotlib. To do this, you ``git push`` this new branch up to your +GitHub repo. Generally, if you followed the instructions in these pages, and by +default, git will have a link to your fork of the GitHub repo, called +``origin``. You push up to your own fork with:: + + git push origin my-new-feature + + +.. _edit-flow: + +The editing workflow +==================== + +#. Make some changes +#. Save the changes +#. See which files have changed with ``git status``. + You'll see a listing like this one: + + .. code-block:: none + + # On branch ny-new-feature + # Changed but not updated: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: README + # + # Untracked files: + # (use "git add ..." to include in what will be committed) + # + # INSTALL + no changes added to commit (use "git add" and/or "git commit -a") + +#. Check what the actual changes are with ``git diff``. +#. Add any new files to version control ``git add new_file_name``. +#. To commit **all** modified files into the local copy of your repo, type: + + .. code-block:: bash + + git commit -am 'A commit message' + + Note the ``-am`` options to ``commit``. The ``m`` flag signals that you are + going to type a message on the command line. The ``a`` flag stages every + file that has been modified, except files listed in ``.gitignore``. For more + information, see the `git commit `_ manual page. +#. To push the changes up to your forked repo on GitHub, do a ``git + push``. + + +Verify your changes +=================== + +Check that your change does what you intend. For code changes: + +* If the issue you are working on provided a code example, run that example + against your branch and check that you now get the desired result. Note that + adapting the issue example is often a good way to create a new test. + +* Run the tests to check that your change has not had unintended consequences + on existing functionality. See :ref:`run_tests`. + +For documentation changes, build the documentation locally to check that +it renders how you intended and that any new links work correctly. See +:ref:`build_docs`. + +This is also a good time to look through the :ref:`pr-author-guidelines` and +address as many of the relevant points as you can. + +.. _open-pull-request: + +Open a pull request +=================== + +When you are ready to ask for someone to review your code and consider a merge, +`submit your Pull Request (PR) `_. + +Go to the web page of *your fork* of the Matplotlib repo, and click +``Compare & pull request`` to send your changes to the maintainers for review. +The base repository is ``matplotlib/matplotlib`` and the base branch is +generally ``main``. + +Enter a title for the set of changes with some explanation of what you've done. +Mention anything you'd like particular attention for - such as a +complicated change or some code you are not happy with. + +If you don't think your request is ready to be merged, make a +:ref:`draft pull request ` and state what aspects you want to have +feedback on. This is a good way of getting some preliminary code review. + +For more guidance on the mechanics of making a pull request, see GitHub's +`pull request tutorial `_. + +.. _update-pull-request: + +Update a pull request +===================== + +When updating your pull request after making revisions, instead of adding new +commits, please consider amending your initial commit(s) to keep the commit +history clean. + +You can achieve this by using + +.. code-block:: bash + + git commit -a --amend --no-edit + git push [your-remote-repo] [your-branch] --force-with-lease + +.. tip:: + Instead of typing your branch name every time, you only need to type the following once to link the remote branch to the local branch:: + + git push --set-upstream origin my-new-feature + + From now on git will know that ``my-new-feature`` is related to the + ``my-new-feature`` branch in the GitHub repo. After this, you will be able to + push your changes with:: + + git push + + +Manage commit history +===================== + +Explore your repository +----------------------- + +To see a graphical representation of the repository branches and +commits:: + + gitk --all + +To see a linear list of commits for this branch:: + + git log + + +.. _recovering-from-mess-up: + +Recover from mistakes +--------------------- + +Sometimes, you mess up merges or rebases. Luckily, in git it is +relatively straightforward to recover from such mistakes. + +If you mess up during a rebase:: + + git rebase --abort + +If you notice you messed up after the rebase:: + + # reset branch back to the saved point + git reset --hard tmp + +If you forgot to make a backup branch:: + + # look at the reflog of the branch + git reflog show my-new-feature + + 8630830 my-new-feature@{0}: commit: BUG: io: close file handles immediately + 278dd2a my-new-feature@{1}: rebase finished: refs/heads/my-new-feature onto 11ee694744f2552d + 26aa21a my-new-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj + ... + + # reset the branch to where it was before the botched rebase + git reset --hard my-new-feature@{2} + +.. _rewriting-commit-history: + +Rewrite commit history +---------------------- + +.. note:: + + Do this only for your own feature branches. + +Is there an embarrassing typo in a commit you made? Or perhaps you +made several false starts you don't want posterity to see. + +This can be done via *interactive rebasing*. + +Suppose that the commit history looks like this:: + + $ git log --oneline + b7e99a8659 (HEAD -> my-new-feature) Fix some remaining bugs + 8a5de78b17 Modify it so that it works + 34448c69eb Fix a few bugs + disable + 9a5d1ca186 First implementation + d1da6fbf0b (upstream/main) Merge pull request #30778 from timhoffm/decorator-tracebackhide + 6ad937ad83 Merge pull request #30838 from has2k1/fix-numpy-integer-markers + ... + +and ``b7e99a8659`` is the most recent commit in the ``my-new-feature`` branch. Suppose we +want to make the following changes: + +* Rewrite the commit message for ``9a5d1ca186`` to something more specific. +* Combine the commits ``34448c69eb``, ``8a5de78b17``, ``b7e99a8659`` into a single one. + +We do as follows:: + + # make a backup of the current state + git branch tmp HEAD + # interactive rebase + git rebase -i d1da6fbf0b + +This will open an editor with the following text in it:: + + pick 9a5d1ca186 First implementation + pick 34448c69eb Fix a few bugs + disable + pick 8a5de78b17 Modify it so that it works + pick b7e99a8659 Fix some remaining bugs + + # Rebase d1da6fbf0b..b7e99a8659 onto d1da6fbf0b (4 commands) + # + # Commands: + # p, pick = use commit + # r, reword = use commit, but edit the commit message + # e, edit = use commit, but stop for amending + # s, squash = use commit, but meld into previous commit + # f, fixup [-C | -c] = like "squash" but keep only the previous + # commit's log message, unless -C is used, in which case + # keep only this commit's message; -c is same as -C but + # opens the editor + # x, exec = run command (the rest of the line) using shell + # b, break = stop here (continue rebase later with 'git rebase --continue') + # d, drop = remove commit + # l, label ` +for examples. + + +Titles, ticks, and labels +========================= + +Align labels to Axes edges +-------------------------- + +`~.axes.Axes.set_xlabel`, `~.axes.Axes.set_ylabel` and +``ColorbarBase.set_label`` support a parameter ``loc`` for simplified +positioning. For the xlabel, the supported values are 'left', 'center', or +'right'. For the ylabel, the supported values are 'bottom', 'center', or +'top'. + +The default is controlled via :rc:`xaxis.labellocation` and +:rc:`yaxis.labellocation`; the Colorbar label takes the rcParam based on its +orientation. + +.. plot:: + + options = ['left', 'center', 'right'] + fig, axs = plt.subplots(len(options), 1, constrained_layout=True) + for ax, loc in zip(axs, options): + ax.plot([1, 2, 3]) + ax.set_xlabel(f'xlabel loc={loc!r}', loc=loc) + + options = ['bottom', 'center', 'top'] + fig, axs = plt.subplots(1, len(options), constrained_layout=True) + for ax, loc in zip(axs, options): + ax.plot([1, 2, 3]) + ax.set_ylabel(f'ylabel loc={loc!r}', loc=loc) + +Allow tick formatters to be set with str or function inputs +----------------------------------------------------------- + +`~.Axis.set_major_formatter` and `~.Axis.set_minor_formatter` +now accept `str` or function inputs in addition to `~.ticker.Formatter` +instances. For a `str` a `~.ticker.StrMethodFormatter` is automatically +generated and used. For a function a `~.ticker.FuncFormatter` is automatically +generated and used. In other words, +:: + + ax.xaxis.set_major_formatter('{x} km') + ax.xaxis.set_minor_formatter(lambda x, pos: str(x-5)) + +are shortcuts for:: + + import matplotlib.ticker as mticker + + ax.xaxis.set_major_formatter(mticker.StrMethodFormatter('{x} km')) + ax.xaxis.set_minor_formatter( + mticker.FuncFormatter(lambda x, pos: str(x-5)) + +.. plot:: + + from matplotlib import ticker + + titles = ["'{x} km'", "lambda x, pos: str(x-5)"] + formatters = ['{x} km', lambda x, pos: str(x-5)] + + fig, axs = plt.subplots(2, 1, figsize=(8, 2), constrained_layout=True) + + for ax, title, formatter in zip(axs, titles, formatters): + # only show the bottom spine + ax.yaxis.set_major_locator(ticker.NullLocator()) + for spine in ['top', 'left', 'right']: + ax.spines[spine].set_visible(False) + + # define tick positions + ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00)) + ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) + + ax.tick_params(which='major', width=1.00, length=5) + ax.tick_params(which='minor', width=0.75, length=2.5, labelsize=10) + ax.set_xlim(0, 5) + ax.set_ylim(0, 1) + ax.text(0.0, 0.2, f'ax.xaxis.set_major_formatter({title})', + transform=ax.transAxes, fontsize=14, fontname='Monospace', + color='tab:blue') + + ax.xaxis.set_major_formatter(formatter) + +``Axes.set_title`` gains a *y* keyword argument to control auto positioning +--------------------------------------------------------------------------- + +`~.axes.Axes.set_title` tries to auto-position the title to avoid any +decorators on the top x-axis. This is not always desirable so now *y* is an +explicit keyword argument of `~.axes.Axes.set_title`. It defaults to *None* +which means to use auto-positioning. If a value is supplied (i.e. the pre-3.0 +default was ``y=1.0``) then auto-positioning is turned off. This can also be +set with the new rcParameter :rc:`axes.titley`. + +.. plot:: + + fig, axs = plt.subplots(1, 2, constrained_layout=True, figsize=(5, 2)) + axs[0].set_title('y=0.7\n$\\sum_{j_n} x_j$', y=0.7) + axs[1].set_title('y=None\n$\\sum_{j_n} x_j$') + plt.show() + +Offset text is now set to the top when using ``axis.tick_top()`` +---------------------------------------------------------------- + +Solves the issue that the power indicator (e.g., 1e4) stayed on the bottom, +even if the ticks were on the top. + +Set zorder of contour labels +---------------------------- + +`~.axes.Axes.clabel` now accepts a *zorder* keyword argument making it easier +to set the *zorder* of contour labels. If not specified, the default *zorder* +of clabels used to always be 3 (i.e. the default *zorder* of `~.text.Text`) +irrespective of the *zorder* passed to +`~.axes.Axes.contour`/`~.axes.Axes.contourf`. The new default *zorder* for +clabels has been changed to (``2 + zorder`` passed to `~.axes.Axes.contour` / +`~.axes.Axes.contourf`). + + +Other changes +============= + +New ``Axes.axline`` method +-------------------------- + +A new `~.axes.Axes.axline` method has been added to draw infinitely long lines +that pass through two points. + +.. plot:: + :include-source: True + + fig, ax = plt.subplots() + + ax.axline((.1, .1), slope=5, color='C0', label='by slope') + ax.axline((.1, .2), (.8, .7), color='C3', label='by points') + + ax.legend() + +``imshow`` now coerces 3D arrays with depth 1 to 2D +--------------------------------------------------- + +Starting from this version arrays of size MxNx1 will be coerced into MxN +for displaying. This means commands like ``plt.imshow(np.random.rand(3, 3, 1))`` +will no longer return an error message that the image shape is invalid. + +Better control of ``Axes.pie`` normalization +-------------------------------------------- + +Previously, `.Axes.pie` would normalize its input *x* if ``sum(x) > 1``, but +would do nothing if the sum were less than 1. This can be confusing, so an +explicit keyword argument *normalize* has been added. By default, the old +behavior is preserved. + +By passing *normalize*, one can explicitly control whether any rescaling takes +place or whether partial pies should be created. If normalization is disabled, +and ``sum(x) > 1``, then an error is raised. + +.. plot:: + + def label(x): + return [str(v) for v in x] + + x = np.array([0.25, 0.3, 0.3]) + fig, ax = plt.subplots(2, 2, constrained_layout=True) + + ax[0, 0].pie(x, autopct='%1.1f%%', labels=label(x), normalize=False) + ax[0, 0].set_title('normalize=False') + ax[0, 1].pie(x, autopct='%1.2f%%', labels=label(x), normalize=True) + ax[0, 1].set_title('normalize=True') + + # This is supposed to show the 'old' behavior of not passing *normalize* + # explicitly, but for the purposes of keeping the documentation build + # warning-free, and future proof for when the deprecation is made + # permanent, we pass *normalize* here explicitly anyway. + ax[1, 0].pie(x, autopct='%1.2f%%', labels=label(x), normalize=False) + ax[1, 0].set_title('normalize unspecified\nsum(x) < 1') + ax[1, 1].pie(x * 10, autopct='%1.2f%%', labels=label(x * 10), + normalize=True) + ax[1, 1].set_title('normalize unspecified\nsum(x) > 1') + +Dates use a modern epoch +------------------------ + +Matplotlib converts dates to days since an epoch using `.dates.date2num` (via +`matplotlib.units`). Previously, an epoch of ``0000-12-31T00:00:00`` was used +so that ``0001-01-01`` was converted to 1.0. An epoch so distant in the past +meant that a modern date was not able to preserve microseconds because 2000 +years times the 2^(-52) resolution of a 64-bit float gives 14 microseconds. + +Here we change the default epoch to the more reasonable UNIX default of +``1970-01-01T00:00:00`` which for a modern date has 0.35 microsecond +resolution. (Finer resolution is not possible because we rely on +`datetime.datetime` for the date locators). Access to the epoch is provided by +`~.dates.get_epoch`, and there is a new :rc:`date.epoch` rcParam. The user may +also call `~.dates.set_epoch`, but it must be set *before* any date conversion +or plotting is used. + +If you have data stored as ordinal floats in the old epoch, you can convert +them to the new ordinal using the following formula:: + + new_ordinal = old_ordinal + mdates.date2num(np.datetime64('0000-12-31')) + +Lines now accept ``MarkerStyle`` instances as input +--------------------------------------------------- + +Similar to `~.Axes.scatter`, `~.Axes.plot` and `~.lines.Line2D` now accept +`~.markers.MarkerStyle` instances as input for the *marker* parameter:: + + plt.plot(..., marker=matplotlib.markers.MarkerStyle("D")) + + +Fonts +===== + +Simple syntax to select fonts by absolute path +---------------------------------------------- + +Fonts can now be selected by passing an absolute `pathlib.Path` to the *font* +keyword argument of `.Text`. + +Improved font weight detection +------------------------------ + +Matplotlib is now better able to determine the weight of fonts from their +metadata, allowing to differentiate between fonts within the same family more +accurately. + + +rcParams improvements +===================== + +``matplotlib.rc_context`` can be used as a decorator +---------------------------------------------------- + +`matplotlib.rc_context` can now be used as a decorator (technically, it is now +implemented as a `contextlib.contextmanager`), e.g., :: + + @rc_context({"lines.linewidth": 2}) + def some_function(...): + ... + +rcParams for controlling default "raise window" behavior +-------------------------------------------------------- + +The new config option :rc:`figure.raise_window` allows disabling of the raising +of the plot window when calling `~.pyplot.show` or `~.pyplot.pause`. The +``MacOSX`` backend is currently not supported. + +Add generalized ``mathtext.fallback`` to rcParams +------------------------------------------------- + +New :rc:`mathtext.fallback` rcParam. Takes "cm", "stix", "stixsans" +or "none" to turn fallback off. The rcParam *mathtext.fallback_to_cm* is +deprecated, but if used, will override new fallback. + +Add ``contour.linewidth`` to rcParams +------------------------------------- + +The new config option :rc:`contour.linewidth` allows to control the default +line width of contours as a float. When set to ``None``, the line widths fall +back to :rc:`lines.linewidth`. The config value is overridden as usual by the +*linewidths* argument passed to `~.axes.Axes.contour` when it is not set to +``None``. + + +3D Axes improvements +==================== + +``Axes3D`` no longer distorts the 3D plot to match the 2D aspect ratio +---------------------------------------------------------------------- + +Plots made with :class:`~mpl_toolkits.mplot3d.axes3d.Axes3D` were previously +stretched to fit a square bounding box. As this stretching was done after the +projection from 3D to 2D, it resulted in distorted images if non-square +bounding boxes were used. As of 3.3, this no longer occurs. + +Currently, modes of setting the aspect (via +`~mpl_toolkits.mplot3d.axes3d.Axes3D.set_aspect`) in data space are not +supported for Axes3D but may be in the future. If you want to simulate having +equal aspect in data space, set the ratio of your data limits to match the +value of `~.get_box_aspect`. To control these ratios use the +`~mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect` method which accepts the +ratios as a 3-tuple of X:Y:Z. The default aspect ratio is 4:4:3. + +3D axes now support minor ticks +------------------------------- + +.. plot:: + :include-source: True + + ax = plt.figure().add_subplot(projection='3d') + + ax.scatter([0, 1, 2], [1, 3, 5], [30, 50, 70]) + + ax.set_xticks([0.25, 0.75, 1.25, 1.75], minor=True) + ax.set_xticklabels(['a', 'b', 'c', 'd'], minor=True) + + ax.set_yticks([1.5, 2.5, 3.5, 4.5], minor=True) + ax.set_yticklabels(['A', 'B', 'C', 'D'], minor=True) + + ax.set_zticks([35, 45, 55, 65], minor=True) + ax.set_zticklabels([r'$\alpha$', r'$\beta$', r'$\delta$', r'$\gamma$'], + minor=True) + + ax.tick_params(which='major', color='C0', labelcolor='C0', width=5) + ax.tick_params(which='minor', color='C1', labelcolor='C1', width=3) + +Home/Forward/Backward buttons now work with 3D axes +--------------------------------------------------- + + +Interactive tool improvements +============================= + +More consistent toolbar behavior across backends +------------------------------------------------ + +Toolbar features are now more consistent across backends. The history buttons +will auto-disable when there is no further action in a direction. The pan and +zoom buttons will be marked active when they are in use. + +In NbAgg and WebAgg, the toolbar buttons are now grouped similarly to other +backends. The WebAgg toolbar now uses the same icons as other backends. + +Toolbar icons are now styled for dark themes +-------------------------------------------- + +On dark themes, toolbar icons will now be inverted. When using the GTK3Agg +backend, toolbar icons are now symbolic, and both foreground and background +colors will follow the theme. Tooltips should also behave correctly. + +Cursor text now uses a number of significant digits matching pointing precision +------------------------------------------------------------------------------- + +Previously, the x/y position displayed by the cursor text would usually include +far more significant digits than the mouse pointing precision (typically one +pixel). This is now fixed for linear scales. + +GTK / Qt zoom rectangle now black and white +------------------------------------------- + +This makes it visible even over a dark background. + +Event handler simplifications +----------------------------- + +The `.backend_bases.key_press_handler` and +`.backend_bases.button_press_handler` event handlers can now be directly +connected to a canvas with ``canvas.mpl_connect("key_press_event", +key_press_handler)`` and ``canvas.mpl_connect("button_press_event", +button_press_handler)``, rather than having to write wrapper functions that +fill in the (now optional) *canvas* and *toolbar* parameters. + + +Functions to compute a Path's size +================================== + +Various functions were added to `~.bezier.BezierSegment` and `~.path.Path` to +allow computation of the shape/size of a `~.path.Path` and its composite Bezier +curves. + +In addition to the fixes below, `~.bezier.BezierSegment` has gained more +documentation and usability improvements, including properties that contain its +dimension, degree, control_points, and more. + +Better interface for Path segment iteration +------------------------------------------- + +`~.path.Path.iter_bezier` iterates through the `~.bezier.BezierSegment`'s that +make up the Path. This is much more useful typically than the existing +`~.path.Path.iter_segments` function, which returns the absolute minimum amount +of information possible to reconstruct the Path. + +Fixed bug that computed a Path's Bbox incorrectly +------------------------------------------------- + +Historically, `~.path.Path.get_extents` has always simply returned the Bbox of +a curve's control points, instead of the Bbox of the curve itself. While this is +a correct upper bound for the path's extents, it can differ dramatically from +the Path's actual extents for non-linear Bezier curves. + + +Backend-specific improvements +============================= + +``savefig()`` gained a *backend* keyword argument +------------------------------------------------- + +The *backend* keyword argument to ``savefig`` can now be used to pick the +rendering backend without having to globally set the backend; e.g., one can +save PDFs using the pgf backend with ``savefig("file.pdf", backend="pgf")``. + +The SVG backend can now render hatches with transparency +-------------------------------------------------------- + +The SVG backend now respects the hatch stroke alpha. Useful applications are, +among others, semi-transparent hatches as a subtle way to differentiate columns +in bar plots. + +SVG supports URLs on more artists +--------------------------------- + +URLs on more artists (i.e., from `.Artist.set_url`) will now be saved in +SVG files, namely, ``Tick``\s and ``Line2D``\s are now supported. + +Images in SVG will no longer be blurred in some viewers +------------------------------------------------------- + +A style is now supplied to images without interpolation (``imshow(..., +interpolation='none'``) so that SVG image viewers will no longer perform +interpolation when rendering themselves. + +Saving SVG now supports adding metadata +--------------------------------------- + +When saving SVG files, metadata can now be passed which will be saved in the +file using `Dublin Core`_ and `RDF`_. A list of valid metadata can be found in +the documentation for `.FigureCanvasSVG.print_svg`. + +.. _Dublin Core: https://www.dublincore.org/specifications/dublin-core/ +.. _RDF: https://www.w3.org/1999/.status/PR-rdf-syntax-19990105/status + +Saving PDF metadata via PGF now consistent with PDF backend +----------------------------------------------------------- + +When saving PDF files using the PGF backend, passed metadata will be +interpreted in the same way as with the PDF backend. Previously, this metadata +was only accepted by the PGF backend when saving a multi-page PDF with +`.backend_pgf.PdfPages`, but is now allowed when saving a single figure, as +well. + +NbAgg and WebAgg no longer use jQuery & jQuery UI +------------------------------------------------- + +Instead, they are implemented using vanilla JavaScript. Please report any +issues with browsers. diff --git a/doc/release/prev_whats_new/whats_new_3.4.0.rst b/doc/release/prev_whats_new/whats_new_3.4.0.rst new file mode 100644 index 000000000000..3cddee85b56e --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.4.0.rst @@ -0,0 +1,1041 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.4.0 + +.. _whats-new-3-4-0: + +============================================= +What's new in Matplotlib 3.4.0 (Mar 26, 2021) +============================================= + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Figure and Axes creation / management +===================================== + +New subfigure functionality +--------------------------- + +New `.figure.Figure.add_subfigure` and `.figure.Figure.subfigures` +functionalities allow creating virtual figures within figures. Similar nesting +was previously done with nested gridspecs (see +:doc:`/gallery/subplots_axes_and_figures/gridspec_nested`). However, this did +not allow localized figure artists (e.g., a colorbar or suptitle) that only +pertained to each subgridspec. + +The new methods `.figure.Figure.add_subfigure` and `.figure.Figure.subfigures` +are meant to rhyme with `.figure.Figure.add_subplot` and +`.figure.Figure.subplots` and have most of the same arguments. + +See :doc:`/gallery/subplots_axes_and_figures/subfigures` for further details. + +.. note:: + + The subfigure functionality is experimental API as of v3.4. + +.. plot:: + + def example_plot(ax, fontsize=12, hide_labels=False): + pc = ax.pcolormesh(np.random.randn(30, 30)) + if not hide_labels: + ax.set_xlabel('x-label', fontsize=fontsize) + ax.set_ylabel('y-label', fontsize=fontsize) + ax.set_title('Title', fontsize=fontsize) + return pc + + np.random.seed(19680808) + fig = plt.figure(constrained_layout=True, figsize=(10, 4)) + subfigs = fig.subfigures(1, 2, wspace=0.07) + + axsLeft = subfigs[0].subplots(1, 2, sharey=True) + subfigs[0].set_facecolor('#eee') + for ax in axsLeft: + pc = example_plot(ax) + subfigs[0].suptitle('Left plots', fontsize='x-large') + subfigs[0].colorbar(pc, shrink=0.6, ax=axsLeft, location='bottom') + + axsRight = subfigs[1].subplots(3, 1, sharex=True) + for nn, ax in enumerate(axsRight): + pc = example_plot(ax, hide_labels=True) + if nn == 2: + ax.set_xlabel('xlabel') + if nn == 1: + ax.set_ylabel('ylabel') + subfigs[1].colorbar(pc, shrink=0.6, ax=axsRight) + subfigs[1].suptitle('Right plots', fontsize='x-large') + + fig.suptitle('Figure suptitle', fontsize='xx-large') + + plt.show() + +Single-line string notation for ``subplot_mosaic`` +-------------------------------------------------- + +`.Figure.subplot_mosaic` and `.pyplot.subplot_mosaic` now accept a single-line +string, using semicolons to delimit rows. Namely, :: + + plt.subplot_mosaic( + """ + AB + CC + """) + +may be written as the shorter: + +.. plot:: + :include-source: + + plt.subplot_mosaic("AB;CC") + +Changes to behavior of Axes creation methods (``gca``, ``add_axes``, ``add_subplot``) +------------------------------------------------------------------------------------- + +The behavior of the functions to create new Axes (`.pyplot.axes`, +`.pyplot.subplot`, `.figure.Figure.add_axes`, `.figure.Figure.add_subplot`) has +changed. In the past, these functions would detect if you were attempting to +create Axes with the same keyword arguments as already-existing Axes in the +current Figure, and if so, they would return the existing Axes. Now, +`.pyplot.axes`, `.figure.Figure.add_axes`, and `.figure.Figure.add_subplot` +will always create new Axes. `.pyplot.subplot` will continue to reuse an +existing Axes with a matching subplot spec and equal *kwargs*. + +Correspondingly, the behavior of the functions to get the current Axes +(`.pyplot.gca`, `.figure.Figure.gca`) has changed. In the past, these functions +accepted keyword arguments. If the keyword arguments matched an +already-existing Axes, then that Axes would be returned, otherwise new Axes +would be created with those keyword arguments. Now, the keyword arguments are +only considered if there are no Axes at all in the current figure. In a future +release, these functions will not accept keyword arguments at all. + +``add_subplot``/``add_axes`` gained an *axes_class* parameter +------------------------------------------------------------- + +In particular, ``mpl_toolkits`` Axes subclasses can now be idiomatically used +using, e.g., ``fig.add_subplot(axes_class=mpl_toolkits.axislines.Axes)`` + +Subplot and subplot2grid can now work with constrained layout +------------------------------------------------------------- + +``constrained_layout`` depends on a single `.GridSpec` for each logical layout +on a figure. Previously, `.pyplot.subplot` and `.pyplot.subplot2grid` added a +new ``GridSpec`` each time they were called and were therefore incompatible +with ``constrained_layout``. + +Now ``subplot`` attempts to reuse the ``GridSpec`` if the number of rows and +columns is the same as the top level GridSpec already in the figure, i.e., +``plt.subplot(2, 1, 2)`` will use the same GridSpec as ``plt.subplot(2, 1, 1)`` +and the ``constrained_layout=True`` option to `~.figure.Figure` will work. + +In contrast, mixing *nrows* and *ncols* will *not* work with +``constrained_layout``: ``plt.subplot(2, 2, 1)`` followed by ``plt.subplots(2, +1, 2)`` will still produce two GridSpecs, and ``constrained_layout=True`` will +give bad results. In order to get the desired effect, the second call can +specify the cells the second Axes is meant to cover: ``plt.subplots(2, 2, (2, +4))``, or the more Pythonic ``plt.subplot2grid((2, 2), (0, 1), rowspan=2)`` can +be used. + + +Plotting methods +================ + +``axline`` supports *transform* parameter +----------------------------------------- + +`~.Axes.axline` now supports the *transform* parameter, which applies to the +points *xy1*, *xy2*. The *slope* (if given) is always in data coordinates. + +For example, this can be used with ``ax.transAxes`` for drawing lines with a +fixed slope. In the following plot, the line appears through the same point on +both Axes, even though they show different data limits. + +.. plot:: + :include-source: + + fig, axs = plt.subplots(1, 2) + + for i, ax in enumerate(axs): + ax.axline((0.25, 0), slope=2, transform=ax.transAxes) + ax.set(xlim=(i, i+5), ylim=(i, i+5)) + +New automatic labeling for bar charts +------------------------------------- + +A new `.Axes.bar_label` method has been added for auto-labeling bar charts. + +.. figure:: /gallery/lines_bars_and_markers/images/sphx_glr_bar_label_demo_001.png + :target: ../../gallery/lines_bars_and_markers/bar_label_demo.html + + Example of the new automatic labeling. + +A list of hatches can be specified to `~.axes.Axes.bar` and `~.axes.Axes.barh` +------------------------------------------------------------------------------ + +Similar to some other rectangle properties, it is now possible to hand a list +of hatch styles to `~.axes.Axes.bar` and `~.axes.Axes.barh` in order to create +bars with different hatch styles, e.g. + +.. plot:: + + fig, ax = plt.subplots() + ax.bar([1, 2], [2, 3], hatch=['+', 'o']) + plt.show() + +Setting ``BarContainer`` orientation +------------------------------------ + +`.BarContainer` now accepts a new string argument *orientation*. It can be +either ``'vertical'`` or ``'horizontal'``, default is ``None``. + +Contour plots now default to using ScalarFormatter +-------------------------------------------------- + +Pass ``fmt="%1.3f"`` to the contouring call to restore the old default label +format. + +``Axes.errorbar`` cycles non-color properties correctly +------------------------------------------------------- + +Formerly, `.Axes.errorbar` incorrectly skipped the Axes property cycle if a +color was explicitly specified, even if the property cycler was for other +properties (such as line style). Now, `.Axes.errorbar` will advance the Axes +property cycle as done for `.Axes.plot`, i.e., as long as all properties in the +cycler are not explicitly passed. + +For example, the following will cycle through the line styles: + +.. plot:: + :include-source: + + x = np.arange(0.1, 4, 0.5) + y = np.exp(-x) + offsets = [0, 1] + + plt.rcParams['axes.prop_cycle'] = plt.cycler('linestyle', ['-', '--']) + + fig, ax = plt.subplots() + for offset in offsets: + ax.errorbar(x, y + offset, xerr=0.1, yerr=0.3, fmt='tab:blue') + +``errorbar`` *errorevery* parameter matches *markevery* +------------------------------------------------------- + +Similar to the *markevery* parameter to `~.Axes.plot`, the *errorevery* +parameter of `~.Axes.errorbar` now accept slices and NumPy fancy indexes (which +must match the size of *x*). + +.. plot:: + + x = np.linspace(0, 1, 15) + y = x * (1-x) + yerr = y/6 + + fig, ax = plt.subplots(2, constrained_layout=True) + ax[0].errorbar(x, y, yerr, capsize=2) + ax[0].set_title('errorevery unspecified') + + ax[1].errorbar(x, y, yerr, capsize=2, + errorevery=[False, True, True, False, True] * 3) + ax[1].set_title('errorevery=[False, True, True, False, True] * 3') + +``hexbin`` supports data reference for *C* parameter +---------------------------------------------------- + +As with the *x* and *y* parameters, `.Axes.hexbin` now supports passing the *C* +parameter using a data reference. + +.. plot:: + :include-source: + + data = { + 'a': np.random.rand(1000), + 'b': np.random.rand(1000), + 'c': np.random.rand(1000), + } + + fig, ax = plt.subplots() + ax.hexbin('a', 'b', C='c', data=data, gridsize=10) + +Support callable for formatting of Sankey labels +------------------------------------------------ + +The `format` parameter of `matplotlib.sankey.Sankey` can now accept callables. + +This allows the use of an arbitrary function to label flows, for example +allowing the mapping of numbers to emoji. + +.. plot:: + + from matplotlib.sankey import Sankey + import math + + + def display_in_cats(values, min_cats, max_cats): + def display_in_cat_scale(value): + max_value = max(values, key=abs) + number_cats_to_show = \ + max(min_cats, math.floor(abs(value) / max_value * max_cats)) + return str(number_cats_to_show * '🐱') + + return display_in_cat_scale + + + flows = [35, 15, 40, -20, -15, -5, -40, -10] + orientations = [-1, 1, 0, 1, 1, 1, -1, -1] + + # Cats are good, we want a strictly positive number of them + min_cats = 1 + # More than four cats might be too much for some people + max_cats = 4 + + cats_format = display_in_cats(flows, min_cats, max_cats) + + sankey = Sankey(flows=flows, orientations=orientations, format=cats_format, + offset=.1, head_angle=180, shoulder=0, scale=.010) + + diagrams = sankey.finish() + + diagrams[0].texts[2].set_text('') + + plt.title(f'Sankey flows measured in cats \n' + f'🐱 = {max(flows, key=abs) / max_cats}') + + plt.show() + +``Axes.spines`` access shortcuts +-------------------------------- + +``Axes.spines`` is now a dedicated container class `.Spines` for a set of +`.Spine`\s instead of an ``OrderedDict``. On top of dict-like access, +``Axes.spines`` now also supports some ``pandas.Series``-like features. + +Accessing single elements by item or by attribute:: + + ax.spines['top'].set_visible(False) + ax.spines.top.set_visible(False) + +Accessing a subset of items:: + + ax.spines[['top', 'right']].set_visible(False) + +Accessing all items simultaneously:: + + ax.spines[:].set_visible(False) + +New ``stairs`` method and ``StepPatch`` artist +---------------------------------------------- + +`.pyplot.stairs` and the underlying artist `~.matplotlib.patches.StepPatch` +provide a cleaner interface for plotting stepwise constant functions for the +common case that you know the step edges. This supersedes many use cases of +`.pyplot.step`, for instance when plotting the output of `numpy.histogram`. + +For both the artist and the function, the x-like edges input is one element +longer than the y-like values input + +.. plot:: + + np.random.seed(0) + h, edges = np.histogram(np.random.normal(5, 2, 5000), + bins=np.linspace(0,10,20)) + + fig, ax = plt.subplots(constrained_layout=True) + + ax.stairs(h, edges) + + plt.show() + +See :doc:`/gallery/lines_bars_and_markers/stairs_demo` for examples. + +Added *orientation* parameter for stem plots +-------------------------------------------- + +By default, stem lines are vertical. They can be changed to horizontal using +the *orientation* parameter of `.Axes.stem` or `.pyplot.stem`: + +.. plot:: + + locs = np.linspace(0.1, 2 * np.pi, 25) + heads = np.cos(locs) + + fig, ax = plt.subplots() + ax.stem(locs, heads, orientation='horizontal') + +Angles on Bracket arrow styles +------------------------------ + +Angles specified on the *Bracket* arrow styles (``]-[``, ``]-``, ``-[``, or +``|-|`` passed to *arrowstyle* parameter of `.FancyArrowPatch`) are now +applied. Previously, the *angleA* and *angleB* options were allowed, but did +nothing. + +.. plot:: + + import matplotlib.patches as mpatches + + fig, ax = plt.subplots() + ax.set(xlim=(0, 1), ylim=(-1, 4)) + + for i, stylename in enumerate((']-[', '|-|')): + for j, angle in enumerate([-30, 60]): + arrowstyle = f'{stylename},angleA={angle},angleB={-angle}' + patch = mpatches.FancyArrowPatch((0.1, 2*i + j), (0.9, 2*i + j), + arrowstyle=arrowstyle, + mutation_scale=25) + ax.text(0.5, 2*i + j, arrowstyle, + verticalalignment='bottom', horizontalalignment='center') + ax.add_patch(patch) + +``TickedStroke`` patheffect +--------------------------- + +The new `.TickedStroke` patheffect can be used to produce lines with a ticked +style. This can be used to, e.g., distinguish the valid and invalid sides of +the constraint boundaries in the solution space of optimizations. + +.. figure:: /gallery/misc/images/sphx_glr_tickedstroke_demo_002.png + :target: ../../gallery/misc/tickedstroke_demo.html + + +Colors and colormaps +==================== + +Collection color specification and mapping +------------------------------------------ + +Reworking the handling of color mapping and the keyword arguments for +*facecolor* and *edgecolor* has resulted in three behavior changes: + +1. Color mapping can be turned off by calling ``Collection.set_array(None)``. + Previously, this would have no effect. +2. When a mappable array is set, with ``facecolor='none'`` and + ``edgecolor='face'``, both the faces and the edges are left uncolored. + Previously the edges would be color-mapped. +3. When a mappable array is set, with ``facecolor='none'`` and + ``edgecolor='red'``, the edges are red. This addresses Issue #1302. + Previously the edges would be color-mapped. + +Transparency (alpha) can be set as an array in collections +---------------------------------------------------------- + +Previously, the alpha value controlling transparency in collections could be +specified only as a scalar applied to all elements in the collection. For +example, all the markers in a `~.Axes.scatter` plot, or all the quadrilaterals +in a `~.Axes.pcolormesh` plot, would have the same alpha value. + +Now it is possible to supply alpha as an array with one value for each element +(marker, quadrilateral, etc.) in a collection. + +.. plot:: + + x = np.arange(5, dtype=float) + y = np.arange(5, dtype=float) + # z and zalpha for demo pcolormesh + z = x[1:, np.newaxis] + y[np.newaxis, 1:] + zalpha = np.ones_like(z) + zalpha[::2, ::2] = 0.3 # alternate patches are partly transparent + # s and salpha for demo scatter + s = x + salpha = np.linspace(0.1, 0.9, len(x)) # just a ramp + + fig, axs = plt.subplots(2, 2, constrained_layout=True) + axs[0, 0].pcolormesh(x, y, z, alpha=zalpha) + axs[0, 0].set_title("pcolormesh") + axs[0, 1].scatter(x, y, c=s, alpha=salpha) + axs[0, 1].set_title("color-mapped") + axs[1, 0].scatter(x, y, c='k', alpha=salpha) + axs[1, 0].set_title("c='k'") + axs[1, 1].scatter(x, y, c=['r', 'g', 'b', 'c', 'm'], alpha=salpha) + axs[1, 1].set_title("c=['r', 'g', 'b', 'c', 'm']") + +pcolormesh has improved transparency handling by enabling snapping +------------------------------------------------------------------ + +Due to how the snapping keyword argument was getting passed to the Agg backend, +previous versions of Matplotlib would appear to show lines between the grid +edges of a mesh with transparency. This version now applies snapping by +default. To restore the old behavior (e.g., for test images), you may set +:rc:`pcolormesh.snap` to `False`. + +.. plot:: + + # Use old pcolormesh snapping values + plt.rcParams['pcolormesh.snap'] = False + fig, ax = plt.subplots() + xx, yy = np.meshgrid(np.arange(10), np.arange(10)) + z = (xx + 1) * (yy + 1) + mesh = ax.pcolormesh(xx, yy, z, shading='auto', alpha=0.5) + fig.colorbar(mesh, orientation='vertical') + ax.set_title('Before (pcolormesh.snap = False)') + +Note that there are lines between the grid boundaries of the main plot which +are not the same transparency. The colorbar also shows these lines when a +transparency is added to the colormap because internally it uses pcolormesh to +draw the colorbar. With snapping on by default (below), the lines at the grid +boundaries disappear. + +.. plot:: + + fig, ax = plt.subplots() + xx, yy = np.meshgrid(np.arange(10), np.arange(10)) + z = (xx + 1) * (yy + 1) + mesh = ax.pcolormesh(xx, yy, z, shading='auto', alpha=0.5) + fig.colorbar(mesh, orientation='vertical') + ax.set_title('After (default: pcolormesh.snap = True)') + +IPython representations for Colormap objects +-------------------------------------------- + +The `matplotlib.colors.Colormap` object now has image representations for +IPython / Jupyter backends. Cells returning a colormap on the last line will +display an image of the colormap. + +.. only:: html + + .. code-block:: ipython + + In[1]: cmap = plt.get_cmap('viridis').with_extremes(bad='r', under='g', over='b') + + In[2]: cmap + Out[2]: + +.. raw:: html + +
+ viridis +
+
+ viridis colormap +
+
+
+
+ under +
+
+ bad +
+
+
+ over +
+
+ +``Colormap.set_extremes`` and ``Colormap.with_extremes`` +-------------------------------------------------------- + +Because the `.Colormap.set_bad`, `.Colormap.set_under` and `.Colormap.set_over` +methods modify the colormap in place, the user must be careful to first make a +copy of the colormap if setting the extreme colors e.g. for a builtin colormap. + +The new ``Colormap.with_extremes(bad=..., under=..., over=...)`` can be used to +first copy the colormap and set the extreme colors on that copy. + +The new `.Colormap.set_extremes` method is provided for API symmetry with +`.Colormap.with_extremes`, but note that it suffers from the same issue as the +earlier individual setters. + +Get under/over/bad colors of Colormap objects +--------------------------------------------- + +`matplotlib.colors.Colormap` now has methods `~.colors.Colormap.get_under`, +`~.colors.Colormap.get_over`, `~.colors.Colormap.get_bad` for the colors used +for out-of-range and masked values. + +New ``cm.unregister_cmap`` function +----------------------------------- + +``matplotlib.cm.unregister_cmap`` allows users to remove a colormap that they have +previously registered. + +New ``CenteredNorm`` for symmetrical data around a center +--------------------------------------------------------- + +In cases where data is symmetrical around a center, for example, positive and +negative anomalies around a center zero, `~.matplotlib.colors.CenteredNorm` is +a new norm that automatically creates a symmetrical mapping around the center. +This norm is well suited to be combined with a divergent colormap which uses an +unsaturated color in its center. + +.. plot:: + + from matplotlib.colors import CenteredNorm + + np.random.seed(20201004) + data = np.random.normal(size=(3, 4), loc=1) + + fig, ax = plt.subplots() + pc = ax.pcolormesh(data, cmap=plt.get_cmap('RdGy'), norm=CenteredNorm()) + fig.colorbar(pc) + ax.set_title('data centered around zero') + + # add text annotation + for irow, data_row in enumerate(data): + for icol, val in enumerate(data_row): + ax.text(icol + 0.5, irow + 0.5, f'{val:.2f}', color='C0', + size=16, va='center', ha='center') + plt.show() + +If the center of symmetry is different from 0, it can be set with the *vcenter* +argument. To manually set the range of `~.matplotlib.colors.CenteredNorm`, use +the *halfrange* argument. + +See :ref:`colormapnorms` for an example and more details +about data normalization. + +New ``FuncNorm`` for arbitrary normalizations +--------------------------------------------- + +The `.FuncNorm` allows for arbitrary normalization using functions for the +forward and inverse. + +.. plot:: + + from matplotlib.colors import FuncNorm + + def forward(x): + return x**2 + def inverse(x): + return np.sqrt(x) + + norm = FuncNorm((forward, inverse), vmin=0, vmax=3) + + np.random.seed(20201004) + data = np.random.normal(size=(3, 4), loc=1) + + fig, ax = plt.subplots() + pc = ax.pcolormesh(data, norm=norm) + fig.colorbar(pc) + ax.set_title('squared normalization') + + # add text annotation + for irow, data_row in enumerate(data): + for icol, val in enumerate(data_row): + ax.text(icol + 0.5, irow + 0.5, f'{val:.2f}', color='C0', + size=16, va='center', ha='center') + plt.show() + +See :ref:`colormapnorms` for an example and more details about data +normalization. + +GridSpec-based colorbars can now be positioned above or to the left of the main axes +------------------------------------------------------------------------------------ + +... by passing ``location="top"`` or ``location="left"`` to the ``colorbar()`` +call. + + +Titles, ticks, and labels +========================= + +supxlabel and supylabel +----------------------- + +It is possible to add x- and y-labels to a whole figure, analogous to +`.Figure.suptitle` using the new `.Figure.supxlabel` and +`.Figure.supylabel` methods. + +.. plot:: + + np.random.seed(19680801) + fig, axs = plt.subplots(3, 2, figsize=(5, 5), constrained_layout=True, + sharex=True, sharey=True) + + for nn, ax in enumerate(axs.flat): + ax.set_title(f'Channel {nn}') + ax.plot(np.cumsum(np.random.randn(50))) + + fig.supxlabel('Time [s]') + fig.supylabel('Data [V]') + +Shared-axes ``subplots`` tick label visibility is now correct for top or left labels +------------------------------------------------------------------------------------ + +When calling ``subplots(..., sharex=True, sharey=True)``, Matplotlib +automatically hides x tick labels for Axes not in the first column and y tick +labels for Axes not in the last row. This behavior is incorrect if rcParams +specify that Axes should be labeled on the top (``rcParams["xtick.labeltop"] = +True``) or on the right (``rcParams["ytick.labelright"] = True``). + +Cases such as the following are now handled correctly (adjusting visibility as +needed on the first row and last column of Axes): + +.. plot:: + :include-source: + + plt.rcParams["xtick.labelbottom"] = False + plt.rcParams["xtick.labeltop"] = True + plt.rcParams["ytick.labelleft"] = False + plt.rcParams["ytick.labelright"] = True + + fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) + +An iterable object with labels can be passed to `.Axes.plot` +------------------------------------------------------------ + +When plotting multiple datasets by passing 2D data as *y* value to +`~.Axes.plot`, labels for the datasets can be passed as a list, the length +matching the number of columns in *y*. + +.. plot:: + :include-source: + + x = [1, 2, 3] + + y = [[1, 2], + [2, 5], + [4, 9]] + + plt.plot(x, y, label=['low', 'high']) + plt.legend() + + +Fonts and Text +============== + +Text transform can rotate text direction +---------------------------------------- + +The new `.Text` parameter ``transform_rotates_text`` now sets whether rotations +of the transform affect the text direction. + +.. figure:: /gallery/text_labels_and_annotations/images/sphx_glr_text_rotation_relative_to_line_001.png + :target: ../../gallery/text_labels_and_annotations/text_rotation_relative_to_line.html + + Example of the new *transform_rotates_text* parameter + +``matplotlib.mathtext`` now supports *overset* and *underset* LaTeX symbols +--------------------------------------------------------------------------- + +`.mathtext` now supports *overset* and *underset*, called as +``\overset{annotation}{body}`` or ``\underset{annotation}{body}``, where +*annotation* is the text "above" or "below" the *body*. + +.. plot:: + + math_expr = r"$ x \overset{f}{\rightarrow} y \underset{f}{\leftarrow} z $" + plt.text(0.4, 0.5, math_expr, usetex=False) + +*math_fontfamily* parameter to change ``Text`` font family +---------------------------------------------------------- + +The new *math_fontfamily* parameter may be used to change the family of fonts +for each individual text element in a plot. If no parameter is set, the global +value :rc:`mathtext.fontset` will be used. + +.. figure:: /gallery/text_labels_and_annotations/images/sphx_glr_mathtext_fontfamily_example_001.png + :target: ../../gallery/text_labels_and_annotations/mathtext_fontfamily_example.html + +``TextArea``/``AnchoredText`` support *horizontalalignment* +----------------------------------------------------------- + +The horizontal alignment of text in a `.TextArea` or `.AnchoredText` may now be +specified, which is mostly effective for multiline text: + +.. plot:: + + from matplotlib.offsetbox import AnchoredText + + fig, ax = plt.subplots() + + text0 = AnchoredText("test\ntest long text", loc="center left", + pad=0.2, prop={"ha": "left"}) + ax.add_artist(text0) + + text1 = AnchoredText("test\ntest long text", loc="center", + pad=0.2, prop={"ha": "center"}) + ax.add_artist(text1) + + text2 = AnchoredText("test\ntest long text", loc="center right", + pad=0.2, prop={"ha": "right"}) + ax.add_artist(text2) + +PDF supports URLs on ``Text`` artists +------------------------------------- + +URLs on `.text.Text` artists (i.e., from `.Artist.set_url`) will now be saved +in PDF files. + + +rcParams improvements +===================== + +New rcParams for dates: set converter and whether to use interval_multiples +--------------------------------------------------------------------------- + +The new :rc:`date.converter` allows toggling between +`matplotlib.dates.DateConverter` and `matplotlib.dates.ConciseDateConverter` +using the strings 'auto' and 'concise' respectively. + +The new :rc:`date.interval_multiples` allows toggling between the dates locator +trying to pick ticks at set intervals (i.e., day 1 and 15 of the month), versus +evenly spaced ticks that start wherever the timeseries starts: + +.. plot:: + :include-source: + + dates = np.arange('2001-01-10', '2001-05-23', dtype='datetime64[D]') + y = np.sin(dates.astype(float) / 10) + fig, axs = plt.subplots(nrows=2, constrained_layout=True) + + plt.rcParams['date.converter'] = 'concise' + plt.rcParams['date.interval_multiples'] = True + axs[0].plot(dates, y) + + plt.rcParams['date.converter'] = 'auto' + plt.rcParams['date.interval_multiples'] = False + axs[1].plot(dates, y) + +Date formatters now respect *usetex* rcParam +-------------------------------------------- + +The `.AutoDateFormatter` and `.ConciseDateFormatter` now respect +:rc:`text.usetex`, and will thus use fonts consistent with TeX rendering of the +default (non-date) formatter. TeX rendering may also be enabled/disabled by +passing the *usetex* parameter when creating the formatter instance. + +In the following plot, both the x-axis (dates) and y-axis (numbers) now use the +same (TeX) font: + +.. plot:: + + from datetime import datetime, timedelta + from matplotlib.dates import ConciseDateFormatter + + plt.rc('text', usetex=True) + + t0 = datetime(1968, 8, 1) + ts = [t0 + i * timedelta(days=1) for i in range(10)] + + fig, ax = plt.subplots() + ax.plot(ts, range(10)) + ax.xaxis.set_major_formatter(ConciseDateFormatter(ax.xaxis.get_major_locator())) + ax.set_xlabel('Date') + ax.set_ylabel('Value') + +Setting *image.cmap* to a ``Colormap`` +-------------------------------------- + +It is now possible to set :rc:`image.cmap` to a `.Colormap` instance, such as a +colormap created with the new `~.Colormap.set_extremes` above. (This can only +be done from Python code, not from the :file:`matplotlibrc` file.) + +Tick and tick label colors can be set independently using rcParams +------------------------------------------------------------------ + +Previously, :rc:`xtick.color` defined both the tick color and the label color. +The label color can now be set independently using :rc:`xtick.labelcolor`. It +defaults to ``'inherit'`` which will take the value from :rc:`xtick.color`. The +same holds for ``ytick.[label]color``. For instance, to set the ticks to light +grey and the tick labels to black, one can use the following code in a script:: + + import matplotlib as mpl + + mpl.rcParams['xtick.labelcolor'] = 'lightgrey' + mpl.rcParams['xtick.color'] = 'black' + mpl.rcParams['ytick.labelcolor'] = 'lightgrey' + mpl.rcParams['ytick.color'] = 'black' + +Or by adding the following lines to the :ref:`matplotlibrc +` file, or a Matplotlib style file: + +.. code-block:: none + + xtick.labelcolor : lightgrey + xtick.color : black + ytick.labelcolor : lightgrey + ytick.color : black + + +3D Axes improvements +==================== + +Errorbar method in 3D Axes +-------------------------- + +The errorbar function `.Axes.errorbar` is ported into the 3D Axes framework in +its entirety, supporting features such as custom styling for error lines and +cap marks, control over errorbar spacing, upper and lower limit marks. + +.. figure:: /gallery/mplot3d/images/sphx_glr_errorbar3d_001.png + :target: ../../gallery/mplot3d/errorbar3d.html + +Stem plots in 3D Axes +--------------------- + +Stem plots are now supported on 3D Axes. Much like 2D stems, +`~.axes3d.Axes3D.stem` supports plotting the stems in various orientations: + +.. plot:: + + theta = np.linspace(0, 2*np.pi) + x = np.cos(theta - np.pi/2) + y = np.sin(theta - np.pi/2) + z = theta + directions = ['z', 'x', 'y'] + names = [r'$\theta$', r'$\cos\theta$', r'$\sin\theta$'] + + fig, axs = plt.subplots(1, 3, figsize=(8, 4), + constrained_layout=True, + subplot_kw={'projection': '3d'}) + for ax, zdir, name in zip(axs, directions, names): + ax.stem(x, y, z, orientation=zdir) + ax.set_title(name) + fig.suptitle(r'A parametric circle: $(x, y) = (\cos\theta, \sin\theta)$') + +See also the :doc:`/gallery/mplot3d/stem3d_demo` demo. + +3D Collection properties are now modifiable +------------------------------------------- + +Previously, properties of a 3D Collection that were used for 3D effects (e.g., +colors were modified to produce depth shading) could not be changed after it +was created. + +Now it is possible to modify all properties of 3D Collections at any time. + +Panning in 3D Axes +------------------ + +Click and drag with the middle mouse button to pan 3D Axes. + + +Interactive tool improvements +============================= + +New ``RangeSlider`` widget +-------------------------- + +`.widgets.RangeSlider` allows for creating a slider that defines +a range rather than a single value. + +.. plot:: + + fig, ax = plt.subplots(2, 1, figsize=(5, 1)) + fig.subplots_adjust(left=0.2, right=0.8) + + from matplotlib.widgets import Slider, RangeSlider + Slider(ax[0], 'Slider', 0, 1) + RangeSlider(ax[1], 'RangeSlider', 0, 1) + +Sliders can now snap to arbitrary values +---------------------------------------- + +The `~matplotlib.widgets.Slider` UI widget now accepts arrays for *valstep*. +This generalizes the previous behavior by allowing the slider to snap to +arbitrary values. + +Pausing and Resuming Animations +------------------------------- + +The `.animation.Animation.pause` and `.animation.Animation.resume` methods +allow you to pause and resume animations. These methods can be used as +callbacks for event listeners on UI elements so that your plots can have some +playback control UI. + + +Sphinx extensions +================= + +``plot_directive`` *caption* option +----------------------------------- + +Captions were previously supported when using the ``plot_directive`` directive +with an external source file by specifying content:: + + .. plot:: path/to/plot.py + + This is the caption for the plot. + +The ``:caption:`` option allows specifying the caption for both external:: + + .. plot:: path/to/plot.py + :caption: This is the caption for the plot. + +and inline plots:: + + .. plot:: + :caption: This is a caption for the plot. + + plt.plot([1, 2, 3]) + + +Backend-specific improvements +============================= + +Consecutive rasterized draws now merged +--------------------------------------- + +Elements of a vector output can be individually set to rasterized, using the +*rasterized* keyword argument, or `~.artist.Artist.set_rasterized()`. This can +be useful to reduce file sizes. For figures with multiple raster elements they +are now automatically merged into a smaller number of bitmaps where this will +not effect the visual output. For cases with many elements this can result in +significantly smaller file sizes. + +To ensure this happens do not place vector elements between raster ones. + +To inhibit this merging set ``Figure.suppressComposite`` to True. + +Support raw/rgba frame format in ``FFMpegFileWriter`` +----------------------------------------------------- + +When using `.FFMpegFileWriter`, the *frame_format* may now be set to ``"raw"`` +or ``"rgba"``, which may be slightly faster than an image format, as no +encoding/decoding need take place between Matplotlib and FFmpeg. + +nbAgg/WebAgg support middle-click and double-click +-------------------------------------------------- + +Double click events are now supported by the nbAgg and WebAgg backends. +Formerly, WebAgg would report middle-click events as right clicks, but now +reports the correct button type. + +nbAgg support binary communication +---------------------------------- + +If the web browser and notebook support binary websockets, nbAgg will now use +them for slightly improved transfer of figure display. + +Indexed color for PNG images in PDF files when possible +------------------------------------------------------- + +When PNG images have 256 colors or fewer, they are converted to indexed color +before saving them in a PDF. This can result in a significant reduction in file +size in some cases. This is particularly true for raster data that uses a +colormap but no interpolation, such as Healpy mollview plots. Currently, this +is only done for RGB images. + +Improved font subsettings in PDF/PS +----------------------------------- + +Font subsetting in PDF and PostScript has been re-written from the embedded +``ttconv`` C code to Python. Some composite characters and outlines may have +changed slightly. This fixes ttc subsetting in PDF, and adds support for +subsetting of type 3 OTF fonts, resulting in smaller files (much smaller when +using CJK fonts), and avoids running into issues with type 42 embedding and +certain PDF readers such as Acrobat Reader. + +Kerning added to strings in PDFs +-------------------------------- + +As with text produced in the Agg backend (see :ref:`the previous what's new +entry ` for examples), PDFs now include kerning in +text strings. + +Fully-fractional HiDPI in QtAgg +------------------------------- + +Fully-fractional HiDPI (that is, HiDPI ratios that are not whole integers) was +added in Qt 5.14, and is now supported by the QtAgg backend when using this +version of Qt or newer. + +wxAgg supports fullscreen toggle +-------------------------------- + +The wxAgg backend supports toggling fullscreen using the :kbd:`f` shortcut, or +the manager function `.FigureManagerBase.full_screen_toggle`. diff --git a/doc/release/prev_whats_new/whats_new_3.5.0.rst b/doc/release/prev_whats_new/whats_new_3.5.0.rst new file mode 100644 index 000000000000..d43a390d2db9 --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.5.0.rst @@ -0,0 +1,683 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.5.0 + +============================================= +What's new in Matplotlib 3.5.0 (Nov 15, 2021) +============================================= + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Figure and Axes creation / management +===================================== + +``subplot_mosaic`` supports simple Axes sharing +----------------------------------------------- + +`.Figure.subplot_mosaic`, `.pyplot.subplot_mosaic` support *simple* Axes +sharing (i.e., only `True`/`False` may be passed to *sharex*/*sharey*). When +`True`, tick label visibility and Axis units will be shared. + +.. plot:: + :include-source: + + mosaic = [ + ['A', [['B', 'C'], + ['D', 'E']]], + ['F', 'G'], + ] + fig = plt.figure(constrained_layout=True) + ax_dict = fig.subplot_mosaic(mosaic, sharex=True, sharey=True) + # All Axes use these scales after this call. + ax_dict['A'].set(xscale='log', yscale='logit') + +Figure now has ``draw_without_rendering`` method +------------------------------------------------ + +Some aspects of a figure are only determined at draw-time, such as the exact +position of text artists or deferred computation like automatic data limits. +If you need these values, you can use ``figure.canvas.draw()`` to force a full +draw. However, this has side effects, sometimes requires an open file, and is +doing more work than is needed. + +The new `.Figure.draw_without_rendering` method runs all the updates that +``draw()`` does, but skips rendering the figure. It's thus more efficient if +you need the updated values to configure further aspects of the figure. + +Figure ``__init__`` passes keyword arguments through to set +----------------------------------------------------------- + +Similar to many other sub-classes of `~.Artist`, the `~.FigureBase`, +`~.SubFigure`, and `~.Figure` classes will now pass any additional keyword +arguments to `~.Artist.set` to allow properties of the newly created object to +be set at initialization time. For example:: + + from matplotlib.figure import Figure + fig = Figure(label='my figure') + +Plotting methods +================ + +Add ``Annulus`` patch +--------------------- + +`.Annulus` is a new class for drawing elliptical annuli. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.patches import Annulus + + fig, ax = plt.subplots() + cir = Annulus((0.5, 0.5), 0.2, 0.05, fc='g') # circular annulus + ell = Annulus((0.5, 0.5), (0.5, 0.3), 0.1, 45, # elliptical + fc='m', ec='b', alpha=0.5, hatch='xxx') + ax.add_patch(cir) + ax.add_patch(ell) + ax.set_aspect('equal') + +``set_data`` method for ``FancyArrow`` patch +-------------------------------------------- + +`.FancyArrow`, the patch returned by ``ax.arrow``, now has a ``set_data`` +method that allows modifying the arrow after creation, e.g., for animation. + +New arrow styles in ``ArrowStyle`` and ``ConnectionPatch`` +---------------------------------------------------------- + +The new *arrow* parameter to `.ArrowStyle` substitutes the use of the +*beginarrow* and *endarrow* parameters in the creation of arrows. It receives +arrows strings like ``'<-'``, ``']-[``' and ``']->``' instead of individual +booleans. + +Two new styles ``']->'`` and ``'<-['`` are also added via this mechanism. +`.ConnectionPatch`, which accepts arrow styles though its *arrowstyle* +parameter, also accepts these new styles. + +.. plot:: + + import matplotlib.patches as mpatches + + fig, ax = plt.subplots(figsize=(4, 4)) + + ax.plot([0.75, 0.75], [0.25, 0.75], 'ok') + ax.set(xlim=(0, 1), ylim=(0, 1), title='New ArrowStyle options') + + ax.annotate(']->', (0.75, 0.25), (0.25, 0.25), + arrowprops=dict( + arrowstyle=']->', connectionstyle="arc3,rad=-0.05", + shrinkA=5, shrinkB=5, + ), + bbox=dict(boxstyle='square', fc='w'), size='large') + + ax.annotate('<-[', (0.75, 0.75), (0.25, 0.75), + arrowprops=dict( + arrowstyle='<-[', connectionstyle="arc3,rad=-0.05", + shrinkA=5, shrinkB=5, + ), + bbox=dict(boxstyle='square', fc='w'), size='large') + +Setting collection offset transform after initialization +-------------------------------------------------------- + +The added `.collections.Collection.set_offset_transform` may be used to set the +offset transform after initialization. This can be helpful when creating a +`.collections.Collection` outside an Axes object, and later adding it with +`.Axes.add_collection()` and setting the offset transform to ``Axes.transData``. + +Colors and colormaps +==================== + +Colormap registry (experimental) +-------------------------------- + +Colormaps are now managed via `matplotlib.colormaps` (or `.pyplot.colormaps`), +which is a `.ColormapRegistry`. While we are confident that the API is final, +we formally mark it as experimental for 3.5 because we want to keep the option +to still modify the API for 3.6 should the need arise. + +Colormaps can be obtained using item access:: + + import matplotlib.pyplot as plt + cmap = plt.colormaps['viridis'] + +To register new colormaps use:: + + plt.colormaps.register(my_colormap) + +We recommend to use the new API instead of the ``matplotlib.cm.get_cmap`` and +``matplotlib.cm.register_cmap`` functions for new code. ``matplotlib.cm.get_cmap`` and +``matplotlib.cm.register_cmap`` will eventually be deprecated and removed. +Within `.pyplot`, ``plt.get_cmap()`` and ``plt.register_cmap()`` will continue +to be supported for backward compatibility. + +Image interpolation now possible at RGBA stage +---------------------------------------------- + +Images in Matplotlib created via `~.axes.Axes.imshow` are resampled to match +the resolution of the current canvas. It is useful to apply an auto-aliasing +filter when downsampling to reduce Moiré effects. By default, interpolation is +done on the data, a norm applied, and then the colormapping performed. + +However, it is often desirable for the anti-aliasing interpolation to happen +in RGBA space, where the colors are interpolated rather than the data. This +usually leads to colors outside the colormap, but visually blends adjacent +colors, and is what browsers and other image processing software do. + +A new keyword argument *interpolation_stage* is provided for +`~.axes.Axes.imshow` to set the stage at which the anti-aliasing interpolation +happens. The default is the current behaviour of "data", with the alternative +being "rgba" for the newly-available behavior. + +.. figure:: /gallery/images_contours_and_fields/images/sphx_glr_image_antialiasing_001.png + :target: ../../gallery/images_contours_and_fields/image_antialiasing.html + + Example of the interpolation stage options. + +For more details see the discussion of the new keyword argument in +:doc:`/gallery/images_contours_and_fields/image_antialiasing`. + +``imshow`` supports half-float arrays +------------------------------------- + +The `~.axes.Axes.imshow` method now supports half-float arrays, i.e., NumPy +arrays with dtype ``np.float16``. + +A callback registry has been added to Normalize objects +------------------------------------------------------- + +`.colors.Normalize` objects now have a callback registry, ``callbacks``, that +can be connected to by other objects to be notified when the norm is updated. +The callback emits the key ``changed`` when the norm is modified. +`.cm.ScalarMappable` is now a listener and will register a change when the +norm's vmin, vmax or other attributes are changed. + +Titles, ticks, and labels +========================= + +Settings tick positions and labels simultaneously in ``set_ticks`` +------------------------------------------------------------------ + +`.Axis.set_ticks` (and the corresponding `.Axes.set_xticks` / +`.Axes.set_yticks`) has a new parameter *labels* allowing to set tick positions +and labels simultaneously. + +Previously, setting tick labels was done using `.Axis.set_ticklabels` (or +the corresponding `.Axes.set_xticklabels` / `.Axes.set_yticklabels`); this +usually only makes sense if tick positions were previously fixed with +`~.Axis.set_ticks`:: + + ax.set_xticks([1, 2, 3]) + ax.set_xticklabels(['a', 'b', 'c']) + +The combined functionality is now available in `~.Axis.set_ticks`:: + + ax.set_xticks([1, 2, 3], ['a', 'b', 'c']) + +The use of `.Axis.set_ticklabels` is discouraged, but it will stay available +for backward compatibility. + +Note: This addition makes the API of `~.Axis.set_ticks` also more similar to +`.pyplot.xticks` / `.pyplot.yticks`, which already had the additional *labels* +parameter. + +Fonts and Text +============== + +Triple and quadruple dot mathtext accents +----------------------------------------- + +In addition to single and double dot accents, mathtext now supports triple and +quadruple dot accents. + +.. plot:: + :include-source: + + fig = plt.figure(figsize=(3, 1)) + fig.text(0.5, 0.5, r'$\dot{a} \ddot{b} \dddot{c} \ddddot{d}$', fontsize=40, + horizontalalignment='center', verticalalignment='center') + +Font properties of legend title are configurable +------------------------------------------------ + +Title's font properties can be set via the *title_fontproperties* keyword +argument, for example: + +.. plot:: + + fig, ax = plt.subplots(figsize=(4, 3)) + ax.plot(range(10), label='point') + ax.legend(title='Points', + title_fontproperties={'family': 'serif', 'size': 20}) + +``Text`` and ``TextBox`` added *parse_math* option +-------------------------------------------------- + +`.Text` and `.TextBox` objects now allow a *parse_math* keyword-only argument +which controls whether math should be parsed from the displayed string. If +*True*, the string will be parsed as a math text object. If *False*, the string +will be considered a literal and no parsing will occur. + +Text can be positioned inside TextBox widget +-------------------------------------------- + +A new parameter called *textalignment* can be used to control for the position +of the text inside the Axes of the `.TextBox` widget. + +.. plot:: + + from matplotlib import pyplot as plt + from matplotlib.widgets import TextBox + + fig = plt.figure(figsize=(4, 3)) + for i, alignment in enumerate(['left', 'center', 'right']): + box_input = fig.add_axes((0.1, 0.7 - i*0.3, 0.8, 0.2)) + text_box = TextBox(ax=box_input, initial=f'{alignment} alignment', + label='', textalignment=alignment) + +Simplifying the font setting for usetex mode +-------------------------------------------- + +Now the :rc:`font.family` accepts some font names as value for a more +user-friendly setup. + +.. code-block:: python + + plt.rcParams.update({ + "text.usetex": True, + "font.family": "Helvetica" + }) + +Type 42 subsetting is now enabled for PDF/PS backends +----------------------------------------------------- + +`~matplotlib.backends.backend_pdf` and `~matplotlib.backends.backend_ps` now +use a unified Type 42 font subsetting interface, with the help of `fontTools +`_ + +Set :rc:`pdf.fonttype` or :rc:`ps.fonttype` to ``42`` to trigger this +workflow:: + + # for PDF backend + plt.rcParams['pdf.fonttype'] = 42 + + # for PS backend + plt.rcParams['ps.fonttype'] = 42 + + fig, ax = plt.subplots() + ax.text(0.4, 0.5, 'subsetted document is smaller in size!') + + fig.savefig("document.pdf") + fig.savefig("document.ps") + +rcParams improvements +===================== + +Allow setting default legend labelcolor globally +------------------------------------------------ + +A new :rc:`legend.labelcolor` sets the default *labelcolor* argument for +`.Figure.legend`. The special values 'linecolor', 'markerfacecolor' (or +'mfc'), or 'markeredgecolor' (or 'mec') will cause the legend text to match the +corresponding color of marker. + +.. plot:: + + plt.rcParams['legend.labelcolor'] = 'linecolor' + + # Make some fake data. + a = np.arange(0, 3, .02) + c = np.exp(a) + d = c[::-1] + + fig, ax = plt.subplots() + ax.plot(a, c, 'g--', label='Model length') + ax.plot(a, d, 'r:', label='Data length') + + ax.legend() + + plt.show() + +3D Axes improvements +==================== + +Axes3D now allows manual control of draw order +---------------------------------------------- + +The `~mpl_toolkits.mplot3d.axes3d.Axes3D` class now has *computed_zorder* +parameter. When set to False, Artists are drawn using their ``zorder`` +attribute. + +.. plot:: + + import matplotlib.patches as mpatches + from mpl_toolkits.mplot3d import art3d + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6.4, 3), + subplot_kw=dict(projection='3d')) + + ax1.set_title('computed_zorder = True (default)') + ax2.set_title('computed_zorder = False') + ax2.computed_zorder = False + + corners = ((0, 0, 0), (0, 5, 0), (5, 5, 0), (5, 0, 0)) + for ax in (ax1, ax2): + tri = art3d.Poly3DCollection([corners], + facecolors='white', + edgecolors='black', + zorder=1) + ax.add_collection3d(tri) + line, = ax.plot((2, 2), (2, 2), (0, 4), c='red', zorder=2, + label='zorder=2') + points = ax.scatter((3, 3), (1, 3), (1, 3), c='red', zorder=10, + label='zorder=10') + + ax.set_xlim(0, 5) + ax.set_ylim(0, 5) + ax.set_zlim(0, 2.5) + + plane = mpatches.Patch(facecolor='white', edgecolor='black', + label='zorder=1') + fig.legend(handles=[plane, line, points], loc='lower center') + +Allow changing the vertical axis in 3d plots +---------------------------------------------- + +`~mpl_toolkits.mplot3d.axes3d.Axes3D.view_init` now has the parameter +*vertical_axis* which allows switching which axis is aligned vertically. + +.. plot:: + + Nphi, Nr = 18, 8 + phi = np.linspace(0, np.pi, Nphi) + r = np.arange(Nr) + phi = np.tile(phi, Nr).flatten() + r = np.repeat(r, Nphi).flatten() + + x = r * np.sin(phi) + y = r * np.cos(phi) + z = Nr - r + + fig, axs = plt.subplots(1, 3, figsize=(7, 3), + subplot_kw=dict(projection='3d'), + gridspec_kw=dict(wspace=0.4, left=0.08, right=0.98, + bottom=0, top=1)) + for vert_a, ax in zip(['z', 'y', 'x'], axs): + pc = ax.scatter(x, y, z, c=z) + ax.view_init(azim=30, elev=30, vertical_axis=vert_a) + ax.set(xlabel='x', ylabel='y', zlabel='z', + title=f'vertical_axis={vert_a!r}') + +``plot_surface`` supports masked arrays and NaNs +------------------------------------------------ + +`.axes3d.Axes3D.plot_surface` supports masked arrays and NaNs, and will now +hide quads that contain masked or NaN points. The behaviour is similar to +`.Axes.contour` with ``corner_mask=True``. + +.. plot:: + + import matplotlib + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(figsize=(6, 6), subplot_kw={'projection': '3d'}, + constrained_layout=True) + + x, y = np.mgrid[1:10:1, 1:10:1] + z = x ** 3 + y ** 3 - 500 + z = np.ma.masked_array(z, z < 0) + + ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=0, cmap='inferno') + ax.view_init(35, -90) + +3D plotting methods support *data* keyword argument +--------------------------------------------------- + +To match all 2D plotting methods, the 3D Axes now support the *data* keyword +argument. This allows passing arguments indirectly from a DataFrame-like +structure. :: + + data = { # A labelled data set, or e.g., Pandas DataFrame. + 'x': ..., + 'y': ..., + 'z': ..., + 'width': ..., + 'depth': ..., + 'top': ..., + } + + fig, ax = plt.subplots(subplot_kw={'projection': '3d') + ax.bar3d('x', 'y', 'z', 'width', 'depth', 'top', data=data) + +Interactive tool improvements +============================= + +Colorbars now have pan and zoom functionality +--------------------------------------------- + +Interactive plots with colorbars can now be zoomed and panned on the colorbar +axis. This adjusts the *vmin* and *vmax* of the ``ScalarMappable`` associated +with the colorbar. This is currently only enabled for continuous norms. Norms +used with contourf and categoricals, such as ``BoundaryNorm`` and ``NoNorm``, +have the interactive capability disabled by default. ``cb.ax.set_navigate()`` +can be used to set whether a colorbar axes is interactive or not. + +Updated the appearance of Slider widgets +---------------------------------------- + +The appearance of `~.Slider` and `~.RangeSlider` widgets were updated and given +new styling parameters for the added handles. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.widgets import Slider + + plt.figure(figsize=(4, 2)) + ax_old = plt.axes([0.2, 0.65, 0.65, 0.1]) + ax_new = plt.axes([0.2, 0.25, 0.65, 0.1]) + Slider(ax_new, "New", 0, 1) + + ax = ax_old + valmin = 0 + valinit = 0.5 + ax.set_xlim(0, 1) + ax_old.axvspan(valmin, valinit, 0, 1) + ax.axvline(valinit, 0, 1, color="r", lw=1) + ax.set_xticks([]) + ax.set_yticks([]) + ax.text( + -0.02, + 0.5, + "Old", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="right", + ) + + ax.text( + 1.02, + 0.5, + "0.5", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="left", + ) + +Removing points on a PolygonSelector +------------------------------------ + +After completing a `~matplotlib.widgets.PolygonSelector`, individual points can +now be removed by right-clicking on them. + +Dragging selectors +------------------ + +The `~matplotlib.widgets.SpanSelector`, `~matplotlib.widgets.RectangleSelector` +and `~matplotlib.widgets.EllipseSelector` have a new keyword argument, +*drag_from_anywhere*, which when set to `True` allows you to click and drag +from anywhere inside the selector to move it. Previously it was only possible +to move it by either activating the move modifier button, or clicking on the +central handle. + +The size of the `~matplotlib.widgets.SpanSelector` can now be changed using the +edge handles. + +Clearing selectors +------------------ + +The selectors (`~.widgets.EllipseSelector`, `~.widgets.LassoSelector`, +`~.widgets.PolygonSelector`, `~.widgets.RectangleSelector`, and +`~.widgets.SpanSelector`) have a new method *clear*, which will clear the +current selection and get the selector ready to make a new selection. This is +equivalent to pressing the *escape* key. + +Setting artist properties of selectors +-------------------------------------- + +The artist properties of the `~.widgets.EllipseSelector`, +`~.widgets.LassoSelector`, `~.widgets.PolygonSelector`, +`~.widgets.RectangleSelector` and `~.widgets.SpanSelector` selectors can be +changed using the ``set_props`` and ``set_handle_props`` methods. + +Ignore events outside selection +------------------------------- + +The `~.widgets.EllipseSelector`, `~.widgets.RectangleSelector` and +`~.widgets.SpanSelector` selectors have a new keyword argument, +*ignore_event_outside*, which when set to `True` will ignore events outside of +the current selection. The handles or the new dragging functionality can instead +be used to change the selection. + +``CallbackRegistry`` objects gain a method to temporarily block signals +----------------------------------------------------------------------- + +The context manager `~matplotlib.cbook.CallbackRegistry.blocked` can be used +to block callback signals from being processed by the ``CallbackRegistry``. +The optional keyword, *signal*, can be used to block a specific signal +from being processed and let all other signals pass. + +.. code-block:: python + + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + ax.imshow([[0, 1], [2, 3]]) + + # Block all interactivity through the canvas callbacks + with fig.canvas.callbacks.blocked(): + plt.show() + + fig, ax = plt.subplots() + ax.imshow([[0, 1], [2, 3]]) + + # Only block key press events + with fig.canvas.callbacks.blocked(signal="key_press_event"): + plt.show() + +Directional sizing cursors +-------------------------- + +Canvases now support setting directional sizing cursors, i.e., horizontal and +vertical double arrows. These are used in e.g., selector widgets. Try the +:doc:`/gallery/widgets/mouse_cursor` example to see the cursor in your desired +backend. + +Sphinx extensions +================= + +More configuration of ``mathmpl`` sphinx extension +-------------------------------------------------- + +The `matplotlib.sphinxext.mathmpl` sphinx extension supports two new +configuration options that may be specified in your ``conf.py``: + +- ``mathmpl_fontsize`` (float), which sets the font size of the math text in + points; +- ``mathmpl_srcset`` (list of str), which provides a list of sizes to support + `responsive resolution images + `__ + The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``, etc.) + to generate (1x is the default and always included.) + +Backend-specific improvements +============================= + +GTK backend +----------- + +A backend supporting GTK4_ has been added. Both Agg and Cairo renderers are +supported. The GTK4 backends may be selected as GTK4Agg or GTK4Cairo. + +.. _GTK4: https://www.gtk.org/ + +Qt backends +----------- + +Support for Qt6 (using either PyQt6_ or PySide6_) has been added, with either +the Agg or Cairo renderers. Simultaneously, support for Qt4 has been dropped. +Both Qt6 and Qt5 are supported by a combined backend (QtAgg or QtCairo), and +the loaded version is determined by modules already imported, the +:envvar:`QT_API` environment variable, and available packages. See +:ref:`QT_bindings` for details. The versioned Qt5 backend names (Qt5Agg or +Qt5Cairo) remain supported for backwards compatibility. + +.. _PyQt6: https://www.riverbankcomputing.com/static/Docs/PyQt6/ +.. _PySide6: https://doc.qt.io/qtforpython/ + +HiDPI support in Cairo-based, GTK, and Tk backends +-------------------------------------------------- + +The GTK3 backends now support HiDPI fully, including mixed monitor cases (on +Wayland only). The newly added GTK4 backends also support HiDPI. + +The TkAgg backend now supports HiDPI **on Windows only**, including mixed +monitor cases. + +All Cairo-based backends correctly support HiDPI as well as their Agg +counterparts did (i.e., if the toolkit supports HiDPI, then the \*Cairo backend +will now support it, but not otherwise.) + +Qt figure options editor improvements +------------------------------------- + +The figure options editor in the Qt backend now also supports editing the left +and right titles (plus the existing centre title). Editing Axis limits is +better supported when using a date converter. The ``symlog`` option is now +available in Axis scaling options. All entries with the same label are now +shown in the Curves tab. + +WX backends support mouse navigation buttons +-------------------------------------------- + +The WX backends now support navigating through view states using the mouse +forward/backward buttons, as in other backends. + +WebAgg uses asyncio instead of Tornado +-------------------------------------- + +The WebAgg backend defaults to using `asyncio` over Tornado for timer support. +This allows using the WebAgg backend in JupyterLite. + +Version information +=================== + +We switched to the `release-branch-semver`_ version scheme of setuptools-scm. +This only affects the version information for development builds. Their version +number now describes the targeted release, i.e. 3.5.0.dev820+g6768ef8c4c is 820 +commits after the previous release and is scheduled to be officially released +as 3.5.0 later. + +In addition to the string ``__version__``, there is now a namedtuple +``__version_info__`` as well, which is modelled after `sys.version_info`_. Its +primary use is safely comparing version information, e.g. ``if +__version_info__ >= (3, 4, 2)``. + +.. _release-branch-semver: https://github.com/pypa/setuptools_scm#version-number-construction +.. _sys.version_info: https://docs.python.org/3/library/sys.html#sys.version_info diff --git a/doc/release/prev_whats_new/whats_new_3.5.2.rst b/doc/release/prev_whats_new/whats_new_3.5.2.rst new file mode 100644 index 000000000000..98880653c9d8 --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.5.2.rst @@ -0,0 +1,22 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.5.2 + +============================================= +What's new in Matplotlib 3.5.2 (May 02, 2022) +============================================= + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Windows on ARM support +---------------------- + +Preliminary support for Windows on arm64 target has been added; this requires +FreeType 2.11 or above. + +No binary wheels are available yet but it may be built from source. diff --git a/doc/release/prev_whats_new/whats_new_3.6.0.rst b/doc/release/prev_whats_new/whats_new_3.6.0.rst new file mode 100644 index 000000000000..57b162ca159d --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.6.0.rst @@ -0,0 +1,890 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.6.0 + +============================================= +What's new in Matplotlib 3.6.0 (Sep 15, 2022) +============================================= + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Figure and Axes creation / management +===================================== +``subplots``, ``subplot_mosaic`` accept *height_ratios* and *width_ratios* arguments +------------------------------------------------------------------------------------ + +The relative width and height of columns and rows in `~.Figure.subplots` and +`~.Figure.subplot_mosaic` can be controlled by passing *height_ratios* and +*width_ratios* keyword arguments to the methods: + +.. plot:: + :alt: A figure with three subplots in three rows and one column. The height of the subplot in the first row is three times than the subplots in the 2nd and 3rd row. + :include-source: true + + fig = plt.figure() + axs = fig.subplots(3, 1, sharex=True, height_ratios=[3, 1, 1]) + +Previously, this required passing the ratios in *gridspec_kw* arguments:: + + fig = plt.figure() + axs = fig.subplots(3, 1, sharex=True, + gridspec_kw=dict(height_ratios=[3, 1, 1])) + +Constrained layout is no longer considered experimental +------------------------------------------------------- + +The constrained layout engine and API is no longer considered experimental. +Arbitrary changes to behaviour and API are no longer permitted without a +deprecation period. + +New ``layout_engine`` module +---------------------------- + +Matplotlib ships with ``tight_layout`` and ``constrained_layout`` layout +engines. A new `.layout_engine` module is provided to allow downstream +libraries to write their own layout engines and `~.figure.Figure` objects can +now take a `.LayoutEngine` subclass as an argument to the *layout* parameter. + +Compressed layout for fixed-aspect ratio Axes +--------------------------------------------- + +Simple arrangements of Axes with fixed aspect ratios can now be packed together +with ``fig, axs = plt.subplots(2, 3, layout='compressed')``. + +With ``layout='tight'`` or ``'constrained'``, Axes with a fixed aspect ratio +can leave large gaps between each other: + +.. plot:: + :alt: A figure labelled "fixed-aspect plots, layout=constrained". Figure has subplots displayed in 2 rows and 2 columns; Subplots have large gaps between each other. + + fig, axs = plt.subplots(2, 2, figsize=(5, 3), + sharex=True, sharey=True, layout="constrained") + for ax in axs.flat: + ax.imshow([[0, 1], [2, 3]]) + fig.suptitle("fixed-aspect plots, layout='constrained'") + +Using the ``layout='compressed'`` layout reduces the space between the Axes, +and adds the extra space to the outer margins: + +.. plot:: + :alt: Four identical two by two heatmaps, each cell a different color: purple, blue, yellow, green going clockwise from upper left corner. The four heatmaps are laid out in a two by two grid with minimum white space between the heatmaps. + + fig, axs = plt.subplots(2, 2, figsize=(5, 3), + sharex=True, sharey=True, layout='compressed') + for ax in axs.flat: + ax.imshow([[0, 1], [2, 3]]) + fig.suptitle("fixed-aspect plots, layout='compressed'") + +See :ref:`compressed_layout` for further details. + +Layout engines may now be removed +--------------------------------- + +The layout engine on a Figure may now be removed by calling +`.Figure.set_layout_engine` with ``'none'``. This may be useful after computing +layout in order to reduce computations, e.g., for subsequent animation loops. + +A different layout engine may be set afterwards, so long as it is compatible +with the previous layout engine. + +``Axes.inset_axes`` flexibility +------------------------------- + +`matplotlib.axes.Axes.inset_axes` now accepts the *projection*, *polar* and +*axes_class* keyword arguments, so that subclasses of `matplotlib.axes.Axes` +may be returned. + +.. plot:: + :alt: Plot of a straight line y=x, with a small inset axes in the lower right corner that shows a circle with radial grid lines and a line plotted in polar coordinates. + :include-source: true + + fig, ax = plt.subplots() + + ax.plot([0, 2], [1, 2]) + + polar_ax = ax.inset_axes([0.75, 0.25, 0.2, 0.2], projection='polar') + polar_ax.plot([0, 2], [1, 2]) + +WebP is now a supported output format +------------------------------------- + +Figures may now be saved in WebP format by using the ``.webp`` file extension, +or passing ``format='webp'`` to `~.Figure.savefig`. This relies on `Pillow +`_ support for WebP. + +Garbage collection is no longer run on figure close +--------------------------------------------------- + +Matplotlib has a large number of circular references (between Figure and +Manager, between Axes and Figure, Axes and Artist, Figure and Canvas, etc.) so +when the user drops their last reference to a Figure (and clears it from +pyplot's state), the objects will not immediately be deleted. + +To account for this we have long (since before 2004) had a `gc.collect` (of the +lowest two generations only) in the closing code in order to promptly clean up +after ourselves. However this is both not doing what we want (as most of our +objects will actually survive) and due to clearing out the first generation +opened us up to having unbounded memory usage. + +In cases with a very tight loop between creating the figure and destroying it +(e.g. ``plt.figure(); plt.close()``) the first generation will never grow large +enough for Python to consider running the collection on the higher generations. +This will lead to unbounded memory usage as the long-lived objects are never +re-considered to look for reference cycles and hence are never deleted. + +We now no longer do any garbage collection when a figure is closed, and rely on +Python automatically deciding to run garbage collection periodically. If you +have strict memory requirements, you can call `gc.collect` yourself but this +may have performance impacts in a tight computation loop. + +Plotting methods +================ + +Striped lines (experimental) +---------------------------- + +The new *gapcolor* parameter to `~.Axes.plot` enables the creation of striped +lines. + +.. plot:: + :alt: Plot of x**3 where the line is an orange-blue striped line, achieved using the keywords linestyle='--', color='orange', gapcolor='blue' + :include-source: true + + x = np.linspace(1., 3., 10) + y = x**3 + + fig, ax = plt.subplots() + ax.plot(x, y, linestyle='--', color='orange', gapcolor='blue', + linewidth=3, label='a striped line') + ax.legend() + +Custom cap widths in box and whisker plots in ``bxp`` and ``boxplot`` +--------------------------------------------------------------------- + +The new *capwidths* parameter to `~.Axes.bxp` and `~.Axes.boxplot` allows +controlling the widths of the caps in box and whisker plots. + +.. plot:: + :alt: A box plot with capwidths 0.01 and 0.2 + :include-source: true + + x = np.linspace(-7, 7, 140) + x = np.hstack([-25, x, 25]) + capwidths = [0.01, 0.2] + + fig, ax = plt.subplots() + ax.boxplot([x, x], notch=True, capwidths=capwidths) + ax.set_title(f'{capwidths=}') + +Easier labelling of bars in bar plot +------------------------------------ + +The *label* argument of `~.Axes.bar` and `~.Axes.barh` can now be passed a list +of labels for the bars. The list must be the same length as *x* and labels the +individual bars. Repeated labels are not de-duplicated and will cause repeated +label entries, so this is best used when bars also differ in style (e.g., by +passing a list to *color*, as below.) + +.. plot:: + :alt: Bar chart: blue bar height 10, orange bar height 20, green bar height 15 legend with blue box labeled a, orange box labeled b, and green box labeled c + :include-source: true + + x = ["a", "b", "c"] + y = [10, 20, 15] + color = ['C0', 'C1', 'C2'] + + fig, ax = plt.subplots() + ax.bar(x, y, color=color, label=x) + ax.legend() + +New style format string for colorbar ticks +------------------------------------------ + +The *format* argument of `~.Figure.colorbar` (and other colorbar methods) now +accepts ``{}``-style format strings. + +.. code-block:: python + + fig, ax = plt.subplots() + im = ax.imshow(z) + fig.colorbar(im, format='{x:.2e}') # Instead of '%.2e' + +Linestyles for negative contours may be set individually +-------------------------------------------------------- + +The line style of negative contours may be set by passing the +*negative_linestyles* argument to `.Axes.contour`. Previously, this style could +only be set globally via :rc:`contour.negative_linestyle`. + +.. plot:: + :alt: Two contour plots, each showing two positive and two negative contours. The positive contours are shown in solid black lines in both plots. In one plot the negative contours are shown in dashed lines, which is the current styling. In the other plot they're shown in dotted lines, which is one of the new options. + :include-source: true + + delta = 0.025 + x = np.arange(-3.0, 3.0, delta) + y = np.arange(-2.0, 2.0, delta) + X, Y = np.meshgrid(x, y) + Z1 = np.exp(-X**2 - Y**2) + Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) + Z = (Z1 - Z2) * 2 + + fig, axs = plt.subplots(1, 2) + + CS = axs[0].contour(X, Y, Z, 6, colors='k') + axs[0].clabel(CS, fontsize=9, inline=True) + axs[0].set_title('Default negative contours') + + CS = axs[1].contour(X, Y, Z, 6, colors='k', negative_linestyles='dotted') + axs[1].clabel(CS, fontsize=9, inline=True) + axs[1].set_title('Dotted negative contours') + +Improved quad contour calculations via ContourPy +------------------------------------------------ + +The contouring functions `~.axes.Axes.contour` and `~.axes.Axes.contourf` have +a new keyword argument *algorithm* to control which algorithm is used to +calculate the contours. There is a choice of four algorithms to use, and the +default is to use ``algorithm='mpl2014'`` which is the same algorithm that +Matplotlib has been using since 2014. + +Previously Matplotlib shipped its own C++ code for calculating the contours of +quad grids. Now the external library `ContourPy +`_ is used instead. + +Other possible values of the *algorithm* keyword argument at this time are +``'mpl2005'``, ``'serial'`` and ``'threaded'``; see the `ContourPy +documentation `_ for further details. + +.. note:: + + Contour lines and polygons produced by ``algorithm='mpl2014'`` will be the + same as those produced before this change to within floating-point + tolerance. The exception is for duplicate points, i.e. contours containing + adjacent (x, y) points that are identical; previously the duplicate points + were removed, now they are kept. Contours affected by this will produce the + same visual output, but there will be a greater number of points in the + contours. + + The locations of contour labels obtained by using `~.axes.Axes.clabel` may + also be different. + +``errorbar`` supports *markerfacecoloralt* +------------------------------------------ + +The *markerfacecoloralt* parameter is now passed to the line plotter from +`.Axes.errorbar`. The documentation now accurately lists which properties are +passed to `.Line2D`, rather than claiming that all keyword arguments are passed +on. + +.. plot:: + :alt: Graph with error bar showing ±0.2 error on the x-axis, and ±0.4 error on the y-axis. Error bar marker is a circle radius 20. Error bar face color is blue. + :include-source: true + + x = np.arange(0.1, 4, 0.5) + y = np.exp(-x) + + fig, ax = plt.subplots() + ax.errorbar(x, y, xerr=0.2, yerr=0.4, + linestyle=':', color='darkgrey', + marker='o', markersize=20, fillstyle='left', + markerfacecolor='tab:blue', markerfacecoloralt='tab:orange', + markeredgecolor='tab:brown', markeredgewidth=2) + +``streamplot`` can disable streamline breaks +-------------------------------------------- + +It is now possible to specify that streamplots have continuous, unbroken +streamlines. Previously streamlines would end to limit the number of lines +within a single grid cell. See the difference between the plots below: + +.. plot:: + :alt: A figure with two streamplots. First streamplot has broken streamlines. Second streamplot has continuous streamlines. + + w = 3 + Y, X = np.mgrid[-w:w:100j, -w:w:100j] + U = -1 - X**2 + Y + V = 1 + X - Y**2 + speed = np.sqrt(U**2 + V**2) + + fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True) + + ax0.streamplot(X, Y, U, V, broken_streamlines=True) + ax0.set_title('broken_streamlines=True') + + ax1.streamplot(X, Y, U, V, broken_streamlines=False) + ax1.set_title('broken_streamlines=False') + +New axis scale ``asinh`` (experimental) +--------------------------------------- + +The new ``asinh`` axis scale offers an alternative to ``symlog`` that smoothly +transitions between the quasi-linear and asymptotically logarithmic regions of +the scale. This is based on an arcsinh transformation that allows plotting both +positive and negative values that span many orders of magnitude. + +.. plot:: + :alt: Figure with 2 subplots. Subplot on the left uses symlog scale on the y axis. The transition at -2 is not smooth. Subplot on the right use asinh scale. The transition at -2 is smooth. + + fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True) + x = np.linspace(-3, 6, 100) + + ax0.plot(x, x) + ax0.set_yscale('symlog') + ax0.grid() + ax0.set_title('symlog') + + ax1.plot(x, x) + ax1.set_yscale('asinh') + ax1.grid() + ax1.set_title(r'$sinh^{-1}$') + + for p in (-2, 2): + for ax in (ax0, ax1): + c = plt.Circle((p, p), radius=0.5, fill=False, + color='red', alpha=0.8, lw=3) + ax.add_patch(c) + +``stairs(..., fill=True)`` hides patch edge by setting linewidth +---------------------------------------------------------------- + +``stairs(..., fill=True)`` would previously hide Patch edges by setting +``edgecolor="none"``. Consequently, calling ``set_color()`` on the Patch later +would make the Patch appear larger. + +Now, by using ``linewidth=0``, this apparent size change is prevented. Likewise +calling ``stairs(..., fill=True, linewidth=3)`` will behave more transparently. + +Fix the dash offset of the Patch class +-------------------------------------- + +Formerly, when setting the line style on a `.Patch` object using a dash tuple, +the offset was ignored. Now the offset is applied to the Patch as expected and +it can be used as it is used with `.Line2D` objects. + +Rectangle patch rotation point +------------------------------ + +The rotation point of the `~matplotlib.patches.Rectangle` can now be set to +'xy', 'center' or a 2-tuple of numbers using the *rotation_point* argument. + +.. plot:: + :alt: Blue square that isn't rotated. Green square rotated 45 degrees relative to center. Orange square rotated 45 degrees relative to lower right corner. Red square rotated 45 degrees relative to point in upper right quadrant. + + fig, ax = plt.subplots() + + rect = plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor='C0') + ax.add_patch(rect) + ax.annotate('Unrotated', (1, 0), color='C0', + horizontalalignment='right', verticalalignment='top', + xytext=(0, -3), textcoords='offset points') + + for rotation_point, color in zip(['xy', 'center', (0.75, 0.25)], + ['C1', 'C2', 'C3']): + ax.add_patch( + plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor=color, + angle=45, rotation_point=rotation_point)) + + if rotation_point == 'center': + point = 0.5, 0.5 + elif rotation_point == 'xy': + point = 0, 0 + else: + point = rotation_point + ax.plot(point[:1], point[1:], color=color, marker='o') + + label = f'{rotation_point}' + if label == 'xy': + label += ' (default)' + ax.annotate(label, point, color=color, + xytext=(3, 3), textcoords='offset points') + + ax.set_aspect(1) + ax.set_title('rotation_point options') + +Colors and colormaps +==================== + +Color sequence registry +----------------------- + +The color sequence registry, `.ColorSequenceRegistry`, contains sequences +(i.e., simple lists) of colors that are known to Matplotlib by name. This will +not normally be used directly, but through the universal instance at +`matplotlib.color_sequences`. + +Colormap method for creating a different lookup table size +---------------------------------------------------------- + +The new method `.Colormap.resampled` creates a new `.Colormap` instance +with the specified lookup table size. This is a replacement for manipulating +the lookup table size via ``get_cmap``. + +Use:: + + get_cmap(name).resampled(N) + +instead of:: + + get_cmap(name, lut=N) + +Setting norms with strings +-------------------------- + +Norms can now be set (e.g. on images) using the string name of the +corresponding scale, e.g. ``imshow(array, norm="log")``. Note that in that +case, it is permissible to also pass *vmin* and *vmax*, as a new Norm instance +will be created under the hood. + +Titles, ticks, and labels +========================= + +``plt.xticks`` and ``plt.yticks`` support *minor* keyword argument +------------------------------------------------------------------ + +It is now possible to set or get minor ticks using `.pyplot.xticks` and +`.pyplot.yticks` by setting ``minor=True``. + +.. plot:: + :alt: Plot showing a line from 1,2 to 3.5,-0.5. X axis showing the 1, 2 and 3 minor ticks on the x axis as One, Zwei, Trois. + :include-source: true + + plt.figure() + plt.plot([1, 2, 3, 3.5], [2, 1, 0, -0.5]) + plt.xticks([1, 2, 3], ["One", "Zwei", "Trois"]) + plt.xticks([np.sqrt(2), 2.5, np.pi], + [r"$\sqrt{2}$", r"$\frac{5}{2}$", r"$\pi$"], minor=True) + +Legends +======= + +Legend can control alignment of title and handles +------------------------------------------------- + +`.Legend` now supports controlling the alignment of the title and handles via +the keyword argument *alignment*. You can also use `.Legend.set_alignment` to +control the alignment on existing Legends. + +.. plot:: + :alt: Figure with 3 subplots. All the subplots are titled test. The three subplots have legends titled alignment='left', alignment='center', alignment='right'. The legend texts are respectively aligned left, center and right. + :include-source: true + + fig, axs = plt.subplots(3, 1) + for i, alignment in enumerate(['left', 'center', 'right']): + axs[i].plot(range(10), label='test') + axs[i].legend(title=f'{alignment=}', alignment=alignment) + +*ncol* keyword argument to ``legend`` renamed to *ncols* +-------------------------------------------------------- + +The *ncol* keyword argument to `~.Axes.legend` for controlling the number of +columns is renamed to *ncols* for consistency with the *ncols* and *nrows* +keywords of `~.Figure.subplots` and `~.GridSpec`. *ncol* remains supported for +backwards compatibility, but is discouraged. + +Markers +======= + +``marker`` can now be set to the string "none" +---------------------------------------------- + +The string "none" means *no-marker*, consistent with other APIs which support +the lowercase version. Using "none" is recommended over using "None", to avoid +confusion with the None object. + +Customization of ``MarkerStyle`` join and cap style +--------------------------------------------------- + +New `.MarkerStyle` parameters allow control of join style and cap style, and +for the user to supply a transformation to be applied to the marker (e.g. a +rotation). + +.. plot:: + :alt: Three rows of markers, columns are blue, green, and purple. First row is y-shaped markers with different capstyles: butt, end is squared off at endpoint; projecting, end is squared off at short distance from endpoint; round, end is rounded. Second row is star-shaped markers with different join styles: miter, star points are sharp triangles; round, star points are rounded; bevel, star points are beveled. Last row shows stars rotated at different angles: small star rotated 0 degrees - top point vertical; medium star rotated 45 degrees - top point tilted right; large star rotated 90 degrees - top point tilted left. + :include-source: true + + from matplotlib.markers import CapStyle, JoinStyle, MarkerStyle + from matplotlib.transforms import Affine2D + + fig, axs = plt.subplots(3, 1, layout='constrained') + for ax in axs: + ax.axis('off') + ax.set_xlim(-0.5, 2.5) + + axs[0].set_title('Cap styles', fontsize=14) + for col, cap in enumerate(CapStyle): + axs[0].plot(col, 0, markersize=32, markeredgewidth=8, + marker=MarkerStyle('1', capstyle=cap)) + # Show the marker edge for comparison with the cap. + axs[0].plot(col, 0, markersize=32, markeredgewidth=1, + markerfacecolor='none', markeredgecolor='lightgrey', + marker=MarkerStyle('1')) + axs[0].annotate(cap.name, (col, 0), + xytext=(20, -5), textcoords='offset points') + + axs[1].set_title('Join styles', fontsize=14) + for col, join in enumerate(JoinStyle): + axs[1].plot(col, 0, markersize=32, markeredgewidth=8, + marker=MarkerStyle('*', joinstyle=join)) + # Show the marker edge for comparison with the join. + axs[1].plot(col, 0, markersize=32, markeredgewidth=1, + markerfacecolor='none', markeredgecolor='lightgrey', + marker=MarkerStyle('*')) + axs[1].annotate(join.name, (col, 0), + xytext=(20, -5), textcoords='offset points') + + axs[2].set_title('Arbitrary transforms', fontsize=14) + for col, (size, rot) in enumerate(zip([2, 5, 7], [0, 45, 90])): + t = Affine2D().rotate_deg(rot).scale(size) + axs[2].plot(col, 0, marker=MarkerStyle('*', transform=t)) + +Fonts and Text +============== + +Font fallback +------------- + +It is now possible to specify a list of fonts families and Matplotlib will try +them in order to locate a required glyph. + +.. plot:: + :caption: Demonstration of mixed English and Chinese text with font fallback. + :alt: The phrase "There are 几个汉字 in between!" rendered in various fonts. + :include-source: True + + plt.rcParams["font.size"] = 20 + fig = plt.figure(figsize=(4.75, 1.85)) + + text = "There are 几个汉字 in between!" + fig.text(0.05, 0.65, text, family=["Noto Sans CJK JP", "Noto Sans TC"]) + fig.text(0.05, 0.45, text, family=["DejaVu Sans", "Noto Sans CJK JP", "Noto Sans TC"]) + +This currently works with the Agg (and all of the GUI embeddings), svg, pdf, +ps, and inline backends. + +List of available font names +---------------------------- + +The list of available fonts are now easily accessible. To get a list of the +available font names in Matplotlib use: + +.. code-block:: python + + from matplotlib import font_manager + font_manager.get_font_names() + +``math_to_image`` now has a *color* keyword argument +---------------------------------------------------- + +To easily support external libraries that rely on the MathText rendering of +Matplotlib to generate equation images, a *color* keyword argument was added to +`~matplotlib.mathtext.math_to_image`. + +.. code-block:: python + + from matplotlib import mathtext + mathtext.math_to_image('$x^2$', 'filename.png', color='Maroon') + +Active URL area rotates with link text +-------------------------------------- + +When link text is rotated in a figure, the active URL area will now include the +rotated link area. Previously, the active area remained in the original, +non-rotated, position. + +rcParams improvements +===================== + +Allow setting figure label size and weight globally and separately from title +----------------------------------------------------------------------------- + +For figure labels, ``Figure.supxlabel`` and ``Figure.supylabel``, the size and +weight can be set separately from the figure title using :rc:`figure.labelsize` +and :rc:`figure.labelweight`. + +.. plot:: + :alt: A figure with 4 plots organised in 2 rows and 2 columns. The title of the figure is suptitle in bold and 64 points. The x axis is labelled supxlabel, and y axis is labelled subylabel. Both labels are 32 points and bold. + :include-source: true + + # Original (previously combined with below) rcParams: + plt.rcParams['figure.titlesize'] = 64 + plt.rcParams['figure.titleweight'] = 'bold' + + # New rcParams: + plt.rcParams['figure.labelsize'] = 32 + plt.rcParams['figure.labelweight'] = 'bold' + + fig, axs = plt.subplots(2, 2, layout='constrained') + for ax in axs.flat: + ax.set(xlabel='xlabel', ylabel='ylabel') + + fig.suptitle('suptitle') + fig.supxlabel('supxlabel') + fig.supylabel('supylabel') + +Note that if you have changed :rc:`figure.titlesize` or +:rc:`figure.titleweight`, you must now also change the introduced parameters +for a result consistent with past behaviour. + +Mathtext parsing can be disabled globally +----------------------------------------- + +The :rc:`text.parse_math` setting may be used to disable parsing of mathtext in +all `.Text` objects (most notably from the `.Axes.text` method). + +Double-quoted strings in matplotlibrc +------------------------------------- + +You can now use double-quotes around strings. This allows using the '#' +character in strings. Without quotes, '#' is interpreted as start of a comment. +In particular, you can now define hex-colors: + +.. code-block:: none + + grid.color: "#b0b0b0" + +3D Axes improvements +==================== + +Standardized views for primary plane viewing angles +--------------------------------------------------- + +When viewing a 3D plot in one of the primary view planes (i.e., perpendicular +to the XY, XZ, or YZ planes), the Axis will be displayed in a standard +location. For further information on 3D views, see +:ref:`toolkit_mplot3d-view-angles` and :doc:`/gallery/mplot3d/view_planes_3d`. + +Custom focal length for 3D camera +--------------------------------- + +The 3D Axes can now better mimic real-world cameras by specifying the focal +length of the virtual camera. The default focal length of 1 corresponds to a +Field of View (FOV) of 90°, and is backwards-compatible with existing 3D plots. +An increased focal length between 1 and infinity "flattens" the image, while a +decreased focal length between 1 and 0 exaggerates the perspective and gives +the image more apparent depth. + +The focal length can be calculated from a desired FOV via the equation: + +.. mathmpl:: + + focal\_length = 1/\tan(FOV/2) + +.. plot:: + :alt: A figure showing 3 basic 3D Wireframe plots. From left to right, the plots use focal length of 0.2, 1 and infinity. Focal length between 0.2 and 1 produce plot with depth while focal length between 1 and infinity show relatively flattened image. + :include-source: true + + from mpl_toolkits.mplot3d import axes3d + + X, Y, Z = axes3d.get_test_data(0.05) + + fig, axs = plt.subplots(1, 3, figsize=(7, 4), + subplot_kw={'projection': '3d'}) + + for ax, focal_length in zip(axs, [0.2, 1, np.inf]): + ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) + ax.set_proj_type('persp', focal_length=focal_length) + ax.set_title(f"{focal_length=}") + +3D plots gained a 3rd "roll" viewing angle +------------------------------------------ + +3D plots can now be viewed from any orientation with the addition of a 3rd roll +angle, which rotates the plot about the viewing axis. Interactive rotation +using the mouse still only controls elevation and azimuth, meaning that this +feature is relevant to users who create more complex camera angles +programmatically. The default roll angle of 0 is backwards-compatible with +existing 3D plots. + +.. plot:: + :alt: View of a wireframe of a 3D contour that is somewhat a thickened s shape. Elevation and azimuth are 0 degrees so the shape is viewed straight on, but tilted because the roll is 30 degrees. + :include-source: true + + from mpl_toolkits.mplot3d import axes3d + + X, Y, Z = axes3d.get_test_data(0.05) + + fig, ax = plt.subplots(subplot_kw={'projection': '3d'}) + + ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) + ax.view_init(elev=0, azim=0, roll=30) + ax.set_title('elev=0, azim=0, roll=30') + +Equal aspect ratio for 3D plots +------------------------------- + +Users can set the aspect ratio for the X, Y, Z axes of a 3D plot to be 'equal', +'equalxy', 'equalxz', or 'equalyz' rather than the default of 'auto'. + +.. plot:: + :alt: Five plots, each showing a different aspect option for a rectangle that has height 4, depth 1, and width 1. auto: none of the dimensions have equal aspect, depth and width form a rectangular and height appears shrunken in proportion. equal: all the dimensions have equal aspect. equalxy: width and depth equal, height not so looks shrunken in proportion. equalyz: depth and height equal, width not so elongated. equalxz: width and height equal, depth not so elongated. + :include-source: true + + from itertools import combinations, product + + aspects = [ + ['auto', 'equal', '.'], + ['equalxy', 'equalyz', 'equalxz'], + ] + fig, axs = plt.subplot_mosaic(aspects, figsize=(7, 6), + subplot_kw={'projection': '3d'}) + + # Draw rectangular cuboid with side lengths [1, 1, 5] + r = [0, 1] + scale = np.array([1, 1, 5]) + pts = combinations(np.array(list(product(r, r, r))), 2) + for start, end in pts: + if np.sum(np.abs(start - end)) == r[1] - r[0]: + for ax in axs.values(): + ax.plot3D(*zip(start*scale, end*scale), color='C0') + + # Set the aspect ratios + for aspect, ax in axs.items(): + ax.set_box_aspect((3, 4, 5)) + ax.set_aspect(aspect) + ax.set_title(f'set_aspect({aspect!r})') + +Interactive tool improvements +============================= + +Rotation, aspect ratio correction and add/remove state +------------------------------------------------------ + +The `.RectangleSelector` and `.EllipseSelector` can now be rotated +interactively between -45° and 45°. The range limits are currently dictated by +the implementation. The rotation is enabled or disabled by striking the *r* key +('r' is the default key mapped to 'rotate' in *state_modifier_keys*) or by +calling ``selector.add_state('rotate')``. + +The aspect ratio of the axes can now be taken into account when using the +"square" state. This is enabled by specifying ``use_data_coordinates='True'`` +when the selector is initialized. + +In addition to changing selector state interactively using the modifier keys +defined in *state_modifier_keys*, the selector state can now be changed +programmatically using the *add_state* and *remove_state* methods. + +.. code-block:: python + + from matplotlib.widgets import RectangleSelector + + values = np.arange(0, 100) + + fig = plt.figure() + ax = fig.add_subplot() + ax.plot(values, values) + + selector = RectangleSelector(ax, print, interactive=True, + drag_from_anywhere=True, + use_data_coordinates=True) + selector.add_state('rotate') # alternatively press 'r' key + # rotate the selector interactively + + selector.remove_state('rotate') # alternatively press 'r' key + + selector.add_state('square') + +``MultiCursor`` now supports Axes split over multiple figures +------------------------------------------------------------- + +Previously, `.MultiCursor` only worked if all target Axes belonged to the same +figure. + +As a consequence of this change, the first argument to the `.MultiCursor` +constructor has become unused (it was previously the joint canvas of all Axes, +but the canvases are now directly inferred from the list of Axes). + +``PolygonSelector`` bounding boxes +---------------------------------- + +`.PolygonSelector` now has a *draw_bounding_box* argument, which when set to +`True` will draw a bounding box around the polygon once it is complete. The +bounding box can be resized and moved, allowing the points of the polygon to be +easily resized. + +Setting ``PolygonSelector`` vertices +------------------------------------ + +The vertices of `.PolygonSelector` can now be set programmatically by using the +`.PolygonSelector.verts` property. Setting the vertices this way will reset the +selector, and create a new complete selector with the supplied vertices. + +``SpanSelector`` widget can now be snapped to specified values +-------------------------------------------------------------- + +The `.SpanSelector` widget can now be snapped to values specified by the +*snap_values* argument. + +More toolbar icons are styled for dark themes +--------------------------------------------- + +On the macOS and Tk backends, toolbar icons will now be inverted when using a +dark theme. + +Platform-specific changes +========================= + +Wx backend uses standard toolbar +-------------------------------- + +Instead of a custom sizer, the toolbar is set on Wx windows as a standard +toolbar. + +Improvements to macosx backend +------------------------------ + +Modifier keys handled more consistently +....................................... + +The macosx backend now handles modifier keys in a manner more consistent with +other backends. See the table in :ref:`event-connections` for further +information. + +``savefig.directory`` rcParam support +..................................... + +The macosx backend will now obey the :rc:`savefig.directory` setting. If set to +a non-empty string, then the save dialog will default to this directory, and +preserve subsequent save directories as they are changed. + +``figure.raise_window`` rcParam support +....................................... + +The macosx backend will now obey the :rc:`figure.raise_window` setting. If set +to False, figure windows will not be raised to the top on update. + +Full-screen toggle support +.......................... + +As supported on other backends, the macosx backend now supports toggling +fullscreen view. By default, this view can be toggled by pressing the :kbd:`f` +key. + +Improved animation and blitting support +....................................... + +The macosx backend has been improved to fix blitting, animation frames with new +artists, and to reduce unnecessary draw calls. + +macOS application icon applied on Qt backend +-------------------------------------------- + +When using the Qt-based backends on macOS, the application icon will now be +set, as is done on other backends/platforms. + +New minimum macOS version +------------------------- + +The macosx backend now requires macOS >= 10.12. + +Windows on ARM support +---------------------- + +Preliminary support for Windows on arm64 target has been added. This support +requires FreeType 2.11 or above. + +No binary wheels are available yet but it may be built from source. diff --git a/doc/release/prev_whats_new/whats_new_3.7.0.rst b/doc/release/prev_whats_new/whats_new_3.7.0.rst new file mode 100644 index 000000000000..d2451bfa50bc --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.7.0.rst @@ -0,0 +1,453 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.7.0 + +============================================= +What's new in Matplotlib 3.7.0 (Feb 13, 2023) +============================================= + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Plotting and Annotation improvements +==================================== + + +``hatch`` parameter for pie +--------------------------- + +`~matplotlib.axes.Axes.pie` now accepts a *hatch* keyword that takes as input +a hatch or list of hatches: + +.. plot:: + :include-source: true + :alt: Two pie charts, identified as ax1 and ax2, both have a small blue slice, a medium orange slice, and a large green slice. ax1 has a dot hatching on the small slice, a small open circle hatching on the medium slice, and a large open circle hatching on the large slice. ax2 has the same large open circle with a dot hatch on every slice. + + fig, (ax1, ax2) = plt.subplots(ncols=2) + x = [10, 30, 60] + + ax1.pie(x, hatch=['.', 'o', 'O']) + ax2.pie(x, hatch='.O') + + ax1.set_title("hatch=['.', 'o', 'O']") + ax2.set_title("hatch='.O'") + + +Polar plot errors drawn in polar coordinates +-------------------------------------------- +Caps and error lines are now drawn with respect to polar coordinates, +when plotting errorbars on polar plots. + +.. figure:: /gallery/pie_and_polar_charts/images/sphx_glr_polar_error_caps_001.png + :target: ../../gallery/pie_and_polar_charts/polar_error_caps.html + + + +Additional format string options in `~matplotlib.axes.Axes.bar_label` +--------------------------------------------------------------------- + +The ``fmt`` argument of `~matplotlib.axes.Axes.bar_label` now accepts +{}-style format strings: + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + + fruit_names = ['Coffee', 'Salted Caramel', 'Pistachio'] + fruit_counts = [4000, 2000, 7000] + + fig, ax = plt.subplots() + bar_container = ax.bar(fruit_names, fruit_counts) + ax.set(ylabel='pints sold', title='Gelato sales by flavor', ylim=(0, 8000)) + ax.bar_label(bar_container, fmt='{:,.0f}') + +It also accepts callables: + +.. plot:: + :include-source: true + + animal_names = ['Lion', 'Gazelle', 'Cheetah'] + mph_speed = [50, 60, 75] + + fig, ax = plt.subplots() + bar_container = ax.bar(animal_names, mph_speed) + ax.set(ylabel='speed in MPH', title='Running speeds', ylim=(0, 80)) + ax.bar_label( + bar_container, fmt=lambda x: '{:.1f} km/h'.format(x * 1.61) + ) + + + +``ellipse`` boxstyle option for annotations +------------------------------------------- + +The ``'ellipse'`` option for boxstyle can now be used to create annotations +with an elliptical outline. It can be used as a closed curve shape for +longer texts instead of the ``'circle'`` boxstyle which can get quite big. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + fig, ax = plt.subplots(figsize=(5, 5)) + t = ax.text(0.5, 0.5, "elliptical box", + ha="center", size=15, + bbox=dict(boxstyle="ellipse,pad=0.3")) + + +The *extent* of ``imshow`` can now be expressed with units +---------------------------------------------------------- +The *extent* parameter of `~.axes.Axes.imshow` and `~.AxesImage.set_extent` +can now be expressed with units. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(layout='constrained') + date_first = np.datetime64('2020-01-01', 'D') + date_last = np.datetime64('2020-01-11', 'D') + + arr = [[i+j for i in range(10)] for j in range(10)] + + ax.imshow(arr, origin='lower', extent=[0, 10, date_first, date_last]) + + plt.show() + +Reversed order of legend entries +-------------------------------- +The order of legend entries can now be reversed by passing ``reverse=True`` to +`~.Axes.legend`. + + +``pcolormesh`` accepts RGB(A) colors +------------------------------------ + +The `~.Axes.pcolormesh` method can now handle explicit colors +specified with RGB(A) values. To specify colors, the array must be 3D +with a shape of ``(M, N, [3, 4])``. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import numpy as np + + colors = np.linspace(0, 1, 90).reshape((5, 6, 3)) + plt.pcolormesh(colors) + plt.show() + + + + +View current appearance settings for ticks, tick labels, and gridlines +---------------------------------------------------------------------- + +The new `~matplotlib.axis.Axis.get_tick_params` method can be used to +retrieve the appearance settings that will be applied to any +additional ticks, tick labels, and gridlines added to the plot: + +.. code-block:: pycon + + >>> import matplotlib.pyplot as plt + + >>> fig, ax = plt.subplots() + >>> ax.yaxis.set_tick_params(labelsize=30, labelcolor='red', + ... direction='out', which='major') + >>> ax.yaxis.get_tick_params(which='major') + {'direction': 'out', + 'left': True, + 'right': False, + 'labelleft': True, + 'labelright': False, + 'gridOn': False, + 'labelsize': 30, + 'labelcolor': 'red'} + >>> ax.yaxis.get_tick_params(which='minor') + {'left': True, + 'right': False, + 'labelleft': True, + 'labelright': False, + 'gridOn': False} + + + +Style files can be imported from third-party packages +----------------------------------------------------- + +Third-party packages can now distribute style files that are globally available +as follows. Assume that a package is importable as ``import mypackage``, with +a ``mypackage/__init__.py`` module. Then a ``mypackage/presentation.mplstyle`` +style sheet can be used as ``plt.style.use("mypackage.presentation")``. + +The implementation does not actually import ``mypackage``, making this process +safe against possible import-time side effects. Subpackages (e.g. +``dotted.package.name``) are also supported. + + +Improvements to 3D Plotting +=========================== + + +3D plot pan and zoom buttons +---------------------------- + +The pan and zoom buttons in the toolbar of 3D plots are now enabled. +Unselect both to rotate the plot. When the zoom button is pressed, +zoom in by using the left mouse button to draw a bounding box, and +out by using the right mouse button to draw the box. When zooming a +3D plot, the current view aspect ratios are kept fixed. + + +*adjustable* keyword argument for setting equal aspect ratios in 3D +------------------------------------------------------------------- + +While setting equal aspect ratios for 3D plots, users can choose to modify +either the data limits or the bounding box in parity with 2D Axes. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import numpy as np + from itertools import combinations, product + + aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz') + fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'}, + figsize=(12, 6)) + + # Draw rectangular cuboid with side lengths [4, 3, 5] + r = [0, 1] + scale = np.array([4, 3, 5]) + pts = combinations(np.array(list(product(r, r, r))), 2) + for start, end in pts: + if np.sum(np.abs(start - end)) == r[1] - r[0]: + for ax in axs: + ax.plot3D(*zip(start*scale, end*scale), color='C0') + + # Set the aspect ratios + for i, ax in enumerate(axs): + ax.set_aspect(aspects[i], adjustable='datalim') + # Alternatively: ax.set_aspect(aspects[i], adjustable='box') + # which will change the box aspect ratio instead of axis data limits. + ax.set_title(f"set_aspect('{aspects[i]}')") + + plt.show() + + +``Poly3DCollection`` supports shading +------------------------------------- + +It is now possible to shade a `.Poly3DCollection`. This is useful if the +polygons are obtained from e.g. a 3D model. + +.. plot:: + :include-source: true + + import numpy as np + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d.art3d import Poly3DCollection + + # Define 3D shape + block = np.array([ + [[1, 1, 0], + [1, 0, 0], + [0, 1, 0]], + [[1, 1, 0], + [1, 1, 1], + [1, 0, 0]], + [[1, 1, 0], + [1, 1, 1], + [0, 1, 0]], + [[1, 0, 0], + [1, 1, 1], + [0, 1, 0]] + ]) + + ax = plt.subplot(projection='3d') + pc = Poly3DCollection(block, facecolors='b', shade=True) + ax.add_collection(pc) + plt.show() + + + +rcParam for 3D pane color +------------------------- + +The rcParams :rc:`axes3d.xaxis.panecolor`, :rc:`axes3d.yaxis.panecolor`, +:rc:`axes3d.zaxis.panecolor` can be used to change the color of the background +panes in 3D plots. Note that it is often beneficial to give them slightly +different shades to obtain a "3D effect" and to make them slightly transparent +(alpha < 1). + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + with plt.rc_context({'axes3d.xaxis.panecolor': (0.9, 0.0, 0.0, 0.5), + 'axes3d.yaxis.panecolor': (0.7, 0.0, 0.0, 0.5), + 'axes3d.zaxis.panecolor': (0.8, 0.0, 0.0, 0.5)}): + fig = plt.figure() + fig.add_subplot(projection='3d') + + + + +Figure and Axes Layout +====================== + +``colorbar`` now has a *location* keyword argument +-------------------------------------------------- + +The ``colorbar`` method now supports a *location* keyword argument to more +easily position the color bar. This is useful when providing your own inset +axes using the *cax* keyword argument and behaves similar to the case where +axes are not provided (where the *location* keyword is passed through). +*orientation* and *ticklocation* are no longer required as they are +determined by *location*. *ticklocation* can still be provided if the +automatic setting is not preferred. (*orientation* can also be provided but +must be compatible with the *location*.) + +An example is: + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import numpy as np + rng = np.random.default_rng(19680801) + imdata = rng.random((10, 10)) + fig, ax = plt.subplots(layout='constrained') + im = ax.imshow(imdata) + fig.colorbar(im, cax=ax.inset_axes([0, 1.05, 1, 0.05]), + location='top') + + + +Figure legends can be placed outside figures using constrained_layout +--------------------------------------------------------------------- +Constrained layout will make space for Figure legends if they are specified +by a *loc* keyword argument that starts with the string "outside". The +codes are unique from axes codes, in that "outside upper right" will +make room at the top of the figure for the legend, whereas +"outside right upper" will make room on the right-hand side of the figure. +See :ref:`legend_guide` for details. + + +Per-subplot keyword arguments in ``subplot_mosaic`` +---------------------------------------------------- + +It is now possible to pass keyword arguments through to Axes creation in each +specific call to ``add_subplot`` in `.Figure.subplot_mosaic` and +`.pyplot.subplot_mosaic` : + +.. plot:: + :include-source: true + + fig, axd = plt.subplot_mosaic( + "AB;CD", + per_subplot_kw={ + "A": {"projection": "polar"}, + ("C", "D"): {"xscale": "log"}, + "B": {"projection": "3d"}, + }, + ) + + +This is particularly useful for creating mosaics with mixed projections, but +any keyword arguments can be passed through. + + +``subplot_mosaic`` no longer provisional +---------------------------------------- + +The API on `.Figure.subplot_mosaic` and `.pyplot.subplot_mosaic` are now +considered stable and will change under Matplotlib's normal deprecation +process. + + +Widget Improvements +=================== + + +Custom styling of button widgets +-------------------------------- + +Additional custom styling of button widgets may be achieved via the +*label_props* and *radio_props* arguments to `.RadioButtons`; and the +*label_props*, *frame_props*, and *check_props* arguments to `.CheckButtons`. + +.. plot:: + :include-source: true + + from matplotlib.widgets import CheckButtons, RadioButtons + + fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(5, 2), width_ratios=[1, 2]) + default_rb = RadioButtons(ax[0, 0], ['Apples', 'Oranges']) + styled_rb = RadioButtons(ax[0, 1], ['Apples', 'Oranges'], + label_props={'color': ['red', 'orange'], + 'fontsize': [16, 20]}, + radio_props={'edgecolor': ['red', 'orange'], + 'facecolor': ['mistyrose', 'peachpuff']}) + + default_cb = CheckButtons(ax[1, 0], ['Apples', 'Oranges'], + actives=[True, True]) + styled_cb = CheckButtons(ax[1, 1], ['Apples', 'Oranges'], + actives=[True, True], + label_props={'color': ['red', 'orange'], + 'fontsize': [16, 20]}, + frame_props={'edgecolor': ['red', 'orange'], + 'facecolor': ['mistyrose', 'peachpuff']}, + check_props={'color': ['darkred', 'darkorange']}) + + ax[0, 0].set_title('Default') + ax[0, 1].set_title('Stylized') + + +Blitting in Button widgets +-------------------------- + +The `.Button`, `.CheckButtons`, and `.RadioButtons` widgets now support +blitting for faster rendering, on backends that support it, by passing +``useblit=True`` to the constructor. Blitting is enabled by default on +supported backends. + + +Other Improvements +================== + + +Source links can be shown or hidden for each Sphinx plot directive +------------------------------------------------------------------ +The :doc:`Sphinx plot directive ` +(``.. plot::``) now supports a ``:show-source-link:`` option to show or hide +the link to the source code for each plot. The default is set using the +``plot_html_show_source_link`` variable in :file:`conf.py` (which +defaults to True). + + + +Figure hooks +------------ + +The new :rc:`figure.hooks` provides a mechanism to register +arbitrary customizations on pyplot figures; it is a list of +"dotted.module.name:dotted.callable.name" strings specifying functions +that are called on each figure created by `.pyplot.figure`; these +functions can e.g. attach callbacks or modify the toolbar. See +:doc:`/gallery/user_interfaces/mplcvd` for an example of toolbar customization. + + +New & Improved Narrative Documentation +====================================== +* Brand new :ref:`Animations ` tutorial. +* New grouped and stacked `bar chart <../../gallery/index.html#lines_bars_and_markers>`_ examples. +* New section for new contributors and reorganized git instructions in the :ref:`contributing guide`. +* Restructured :ref:`annotations` tutorial. diff --git a/doc/release/prev_whats_new/whats_new_3.8.0.rst b/doc/release/prev_whats_new/whats_new_3.8.0.rst new file mode 100644 index 000000000000..2d5ffe3ad3e7 --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.8.0.rst @@ -0,0 +1,531 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.8.0 + +============================================== +What's new in Matplotlib 3.8.0 (Sept 13, 2023) +============================================== + +For a list of all of the issues and pull requests since the last revision, see +the :ref:`github-stats`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Type Hints +========== + +Matplotlib now provides first-party PEP484 style type hints files for most public APIs. + +While still considered provisional and subject to change (and sometimes we are not +quite able to fully specify what we would like to), they should provide a reasonable +basis to type check many common usage patterns, as well as integrating with many +editors/IDEs. + +Plotting and Annotation improvements +==================================== + +Support customizing antialiasing for text and annotation +-------------------------------------------------------- +``matplotlib.pyplot.annotate()`` and ``matplotlib.pyplot.text()`` now support parameter *antialiased*. +When *antialiased* is set to ``True``, antialiasing will be applied to the text. +When *antialiased* is set to ``False``, antialiasing will not be applied to the text. +When *antialiased* is not specified, antialiasing will be set by :rc:`text.antialiased` at the creation time of ``Text`` and ``Annotation`` object. +Examples: + +.. code-block:: python + + mpl.text.Text(.5, .5, "foo\nbar", antialiased=True) + plt.text(0.5, 0.5, '6 inches x 2 inches', antialiased=True) + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), antialiased=False) + +If the text contains math expression, *antialiased* applies to the whole text. +Examples: + +.. code-block:: python + + # no part will be antialiased for the text below + plt.text(0.5, 0.25, r"$I'm \sqrt{x}$", antialiased=False) + +Also note that antialiasing for tick labels will be set with :rc:`text.antialiased` when they are created (usually when a ``Figure`` is created) and cannot be changed afterwards. + +Furthermore, with this new feature, you may want to make sure that you are creating and saving/showing the figure under the same context:: + + # previously this was a no-op, now it is what works + with rccontext(text.antialiased=False): + fig, ax = plt.subplots() + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5)) + fig.savefig('/tmp/test.png') + + + # previously this had an effect, now this is a no-op + fig, ax = plt.subplots() + ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5)) + with rccontext(text.antialiased=False): + fig.savefig('/tmp/test.png') + +rcParams for ``AutoMinorLocator`` divisions +------------------------------------------- +The rcParams :rc:`xtick.minor.ndivs` and :rc:`ytick.minor.ndivs` have been +added to enable setting the default number of divisions; if set to ``auto``, +the number of divisions will be chosen by the distance between major ticks. + +Axline setters and getters +-------------------------- + +The returned object from `.axes.Axes.axline` now supports getter and setter +methods for its *xy1*, *xy2* and *slope* attributes: + +.. code-block:: python + + line1.get_xy1() + line1.get_slope() + line2.get_xy2() + +.. code-block:: python + + line1.set_xy1(.2, .3) + line1.set_slope(2.4) + line2.set_xy2(.1, .6) + +Clipping for contour plots +-------------------------- + +`~.Axes.contour` and `~.Axes.contourf` now accept the *clip_path* parameter. + +.. plot:: + :include-source: true + + import numpy as np + import matplotlib.pyplot as plt + import matplotlib.patches as mpatches + + x = y = np.arange(-3.0, 3.01, 0.025) + X, Y = np.meshgrid(x, y) + Z1 = np.exp(-X**2 - Y**2) + Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) + Z = (Z1 - Z2) * 2 + + fig, ax = plt.subplots() + patch = mpatches.RegularPolygon((0, 0), 5, radius=2, + transform=ax.transData) + ax.contourf(X, Y, Z, clip_path=patch) + + plt.show() + +``Axes.ecdf`` +------------- +A new Axes method, `~.Axes.ecdf`, allows plotting empirical cumulative +distribution functions without any binning. + +.. plot:: + :include-source: + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots() + ax.ecdf(np.random.randn(100)) + +``Figure.get_suptitle()``, ``Figure.get_supxlabel()``, ``Figure.get_supylabel()`` +--------------------------------------------------------------------------------- +These methods return the strings set by ``Figure.suptitle()``, ``Figure.supxlabel()`` +and ``Figure.supylabel()`` respectively. + +``Ellipse.get_vertices()``, ``Ellipse.get_co_vertices()`` +--------------------------------------------------------------------------------- +These methods return the coordinates of ellipse vertices of +major and minor axis. Additionally, an example gallery demo is added which +shows how to add an arrow to an ellipse showing a clockwise or counter-clockwise +rotation of the ellipse. To place the arrow exactly on the ellipse, +the coordinates of the vertices are used. + +Remove inner ticks in ``label_outer()`` +--------------------------------------- +Up to now, ``label_outer()`` has only removed the ticklabels. The ticks lines +were left visible. This is now configurable through a new parameter +``label_outer(remove_inner_ticks=True)``. + + +.. plot:: + :include-source: true + + import numpy as np + import matplotlib.pyplot as plt + + x = np.linspace(0, 2 * np.pi, 100) + + fig, axs = plt.subplots(2, 2, sharex=True, sharey=True, + gridspec_kw=dict(hspace=0, wspace=0)) + + axs[0, 0].plot(x, np.sin(x)) + axs[0, 1].plot(x, np.cos(x)) + axs[1, 0].plot(x, -np.cos(x)) + axs[1, 1].plot(x, -np.sin(x)) + + for ax in axs.flat: + ax.grid(color='0.9') + ax.label_outer(remove_inner_ticks=True) + +Configurable legend shadows +--------------------------- +The *shadow* parameter of legends now accepts dicts in addition to booleans. +Dictionaries can contain any keywords for `.patches.Patch`. +For example, this allows one to set the color and/or the transparency of a legend shadow: + +.. code-block:: python + + ax.legend(loc='center left', shadow={'color': 'red', 'alpha': 0.5}) + +and to control the shadow location: + +.. code-block:: python + + ax.legend(loc='center left', shadow={"ox":20, "oy":-20}) + +Configuration is currently not supported via :rc:`legend.shadow`. + + +``offset`` parameter for MultipleLocator +---------------------------------------- + +An *offset* may now be specified to shift all the ticks by the given value. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import matplotlib.ticker as mticker + + _, ax = plt.subplots() + ax.plot(range(10)) + locator = mticker.MultipleLocator(base=3, offset=0.3) + ax.xaxis.set_major_locator(locator) + + plt.show() + +Add a new valid color format ``(matplotlib_color, alpha)`` +---------------------------------------------------------- + + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + from matplotlib.patches import Rectangle + + fig, ax = plt.subplots() + + rectangle = Rectangle((.2, .2), .6, .6, + facecolor=('blue', 0.2), + edgecolor=('green', 0.5)) + ax.add_patch(rectangle) + + +Users can define a color using the new color specification, *(matplotlib_color, alpha)*. +Note that an explicit alpha keyword argument will override an alpha value from +*(matplotlib_color, alpha)*. + +The pie chart shadow can be controlled +-------------------------------------- + +The *shadow* argument to `~.Axes.pie` can now be a dict, allowing more control +of the `.Shadow`-patch used. + + +``PolyQuadMesh`` is a new class for drawing quadrilateral meshes +---------------------------------------------------------------- + +`~.Axes.pcolor` previously returned a flattened `.PolyCollection` with only +the valid polygons (unmasked) contained within it. Now, we return a `.PolyQuadMesh`, +which is a mixin incorporating the usefulness of 2D array and mesh coordinates +handling, but still inheriting the draw methods of `.PolyCollection`, which enables +more control over the rendering properties than a normal `.QuadMesh` that is +returned from `~.Axes.pcolormesh`. The new class subclasses `.PolyCollection` and thus +should still behave the same as before. This new class keeps track of the mask for +the user and updates the Polygons that are sent to the renderer appropriately. + +.. plot:: + + arr = np.arange(12).reshape((3, 4)) + + fig, ax = plt.subplots() + pc = ax.pcolor(arr) + + # Mask one element and show that the hatch is also not drawn + # over that region + pc.set_array(np.ma.masked_equal(arr, 5)) + pc.set_hatch('//') + + plt.show() + +Shadow shade can be controlled +------------------------------ + +The `.Shadow` patch now has a *shade* argument to control the shadow darkness. +If 1, the shadow is black, if 0, the shadow has the same color as the patch that +is shadowed. The default value, which earlier was fixed, is 0.7. + +``SpinesProxy`` now supports calling the ``set()`` method +--------------------------------------------------------- +One can now call e.g. ``ax.spines[:].set(visible=False)``. + +Allow setting the tick label fonts with a keyword argument +---------------------------------------------------------- +``Axes.tick_params`` now accepts a *labelfontfamily* keyword that changes the tick +label font separately from the rest of the text objects: + +.. code-block:: python + + Axis.tick_params(labelfontfamily='monospace') + + +Figure, Axes, and Legend Layout +=============================== + +pad_inches="layout" for savefig +------------------------------- + +When using constrained or compressed layout, + +.. code-block:: python + + savefig(filename, bbox_inches="tight", pad_inches="layout") + +will now use the padding sizes defined on the layout engine. + +Add a public method to modify the location of ``Legend`` +-------------------------------------------------------- + +`~matplotlib.legend.Legend` locations now can be tweaked after they've been defined. + +.. plot:: + :include-source: true + + from matplotlib import pyplot as plt + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + + x = list(range(-100, 101)) + y = [i**2 for i in x] + + ax.plot(x, y, label="f(x)") + ax.legend() + ax.get_legend().set_loc("right") + # Or + # ax.get_legend().set(loc="right") + + plt.show() + + +``rcParams['legend.loc']`` now accepts float-tuple inputs +--------------------------------------------------------- + +The :rc:`legend.loc` rcParams now accepts float-tuple inputs, same as the *loc* keyword argument to `.Legend`. +This allows users to set the location of the legend in a more flexible and consistent way. + +Mathtext improvements +===================== + +Improvements are to Mathtext, Matplotlib's native TeX-like mathematics parser +(see :ref:`mathtext`, not to be confused with Matplotlib using LaTeX directly: +:ref:`usetex`). + +Boldsymbol mathtext command ``\boldsymbol`` +------------------------------------------- + +Supports using the ``\boldsymbol{}`` command in mathtext: + +To change symbols to bold enclose the text in a font command as +shown: + +.. code-block:: none + + r'$\boldsymbol{a+2+\alpha}$' + +.. math:: + \boldsymbol{a+2+\alpha} + +``mathtext`` has more sizable delimiters +---------------------------------------- + +The ``\lgroup`` and ``\rgroup`` sizable delimiters have been added. + +The following delimiter names have been supported earlier, but can now be sized with +``\left`` and ``\right``: + +* ``\lbrace``, ``\rbrace``, ``\leftbrace``, and ``\rightbrace`` +* ``\lbrack`` and ``\rbrack`` +* ``\leftparen`` and ``\rightparen`` + +There are really no obvious advantages in using these. +Instead, they are added for completeness. + +``mathtext`` documentation improvements +--------------------------------------- + +The documentation is updated to take information directly from the parser. This +means that (almost) all supported symbols, operators etc are shown at :ref:`mathtext`. + +``mathtext`` now supports ``\substack`` +--------------------------------------- + +``\substack`` can be used to create multi-line subscripts or superscripts within an equation. + +To use it to enclose the math in a substack command as shown: + +.. code-block:: none + + r'$\sum_{\substack{1\leq i\leq 3\\ 1\leq j\leq 5}}$' + +.. mathmpl:: + + \sum_{\substack{1\leq i\leq 3\\ 1\leq j\leq 5}} + + + +``mathtext`` now supports ``\middle`` delimiter +----------------------------------------------- + +The ``\middle`` delimiter has been added, and can now be used with the +``\left`` and ``\right`` delimiters: + +To use the middle command enclose it in between the ``\left`` and +``\right`` delimiter command as shown: + +.. code-block:: none + + r'$\left( \frac{a}{b} \middle| q \right)$' + +.. mathmpl:: + + \left( \frac{a}{b} \middle| q \right) + +``mathtext`` operators +---------------------- + +There has been a number of operators added and corrected when a Unicode font is used. +In addition, correct spacing has been added to a number of the previous operators. +Especially, the characters used for ``\gnapprox``, ``\lnapprox``, ``\leftangle``, and +``\rightangle`` have been corrected. + +``mathtext`` spacing corrections +-------------------------------- + +As consequence of the updated documentation, the spacing on a number of relational and +operator symbols were classified like that and therefore will be spaced properly. + +``mathtext`` now supports ``\text`` +----------------------------------- + +``\text`` can be used to obtain upright text within an equation and to get a plain dash +(-). + +.. plot:: + :include-source: true + :alt: Illustration of the newly added \text command, showing that it renders as normal text, including spaces, despite being part of an equation. Also show that a dash is not rendered as a minus when part of a \text command. + + import matplotlib.pyplot as plt + plt.text(0.1, 0.5, r"$a = \sin(\phi) \text{ such that } \phi = \frac{x}{y}$") + plt.text(0.1, 0.3, r"$\text{dashes (-) are retained}$") + + +Bold-italic mathtext command ``\mathbfit`` +------------------------------------------ + +Supports use of bold-italic font style in mathtext using the ``\mathbfit{}`` command: + +To change font to bold and italic enclose the text in a font command as +shown: + +.. code-block:: none + + r'$\mathbfit{\eta \leq C(\delta(\eta))}$ + +.. math:: + \mathbfit{\eta \leq C(\delta(\eta))} + + +3D plotting improvements +======================== + +Specify ticks and axis label positions for 3D plots +--------------------------------------------------- + +You can now specify the positions of ticks and axis labels for 3D plots. + +.. plot:: + :include-source: + + import matplotlib.pyplot as plt + + positions = ['lower', 'upper', 'default', 'both', 'none'] + fig, axs = plt.subplots(2, 3, figsize=(12, 8), + subplot_kw={'projection': '3d'}) + for ax, pos in zip(axs.flatten(), positions): + for axis in ax.xaxis, ax.yaxis, ax.zaxis: + axis.set_label_position(pos) + axis.set_ticks_position(pos) + title = f'position="{pos}"' + ax.set(xlabel='x', ylabel='y', zlabel='z', title=title) + axs[1, 2].axis('off') + +3D hover coordinates +-------------------- + +The x, y, z coordinates displayed in 3D plots were previously showing +nonsensical values. This has been fixed to report the coordinate on the view +pane directly beneath the mouse cursor. This is likely to be most useful when +viewing 3D plots along a primary axis direction when using an orthographic +projection, or when a 2D plot has been projected onto one of the 3D axis panes. +Note that there is still no way to directly display the coordinates of plotted +data points. + +3D plots can share view angles +------------------------------ + +3D plots can now share the same view angles, so that when you rotate one plot +the other plots also rotate. This can be done with the *shareview* keyword +argument when adding an axes, or by using the *ax1.shareview(ax2)* method of +existing 3D axes. + + +Other improvements +================== + +macosx: New figures can be opened in either windows or tabs +----------------------------------------------------------- + +There is a new :rc:`macosx.window_mode` rcParam to control how +new figures are opened with the macosx backend. The default is +**system** which uses the system settings, or one can specify either +**tab** or **window** to explicitly choose the mode used to open new figures. + +``matplotlib.mpl_toolkits`` is now an implicit namespace package +---------------------------------------------------------------- + +Following the deprecation of ``pkg_resources.declare_namespace`` in ``setuptools`` 67.3.0, +``matplotlib.mpl_toolkits`` is now implemented as an implicit namespace, following +`PEP 420 `_. + +Plot Directive now can make responsive images with "srcset" +----------------------------------------------------------- + +The plot sphinx directive (``matplotlib.sphinxext.plot_directive``, invoked in +rst as ``.. plot::``) can be configured to automatically make higher res +figures and add these to the built html docs. In ``conf.py``:: + + extensions = [ + ... + 'matplotlib.sphinxext.plot_directive', + 'matplotlib.sphinxext.figmpl_directive', + ...] + + plot_srcset = ['2x'] + +will make png files with double the resolution for hiDPI displays. Resulting +html files will have image entries like:: + + diff --git a/doc/release/prev_whats_new/whats_new_3.9.0.rst b/doc/release/prev_whats_new/whats_new_3.9.0.rst new file mode 100644 index 000000000000..259bd2f35ee4 --- /dev/null +++ b/doc/release/prev_whats_new/whats_new_3.9.0.rst @@ -0,0 +1,411 @@ +.. redirect-from:: /users/prev_whats_new/whats_new_3.9.0 + +============================================= +What's new in Matplotlib 3.9.0 (May 15, 2024) +============================================= + +For a list of all of the issues and pull requests since the last revision, see the +:ref:`github-stats-3-9-0`. + +.. contents:: Table of Contents + :depth: 4 + +.. toctree:: + :maxdepth: 4 + +Plotting and Annotation improvements +==================================== + +``Axes.inset_axes`` is no longer experimental +--------------------------------------------- + +`.Axes.inset_axes` is considered stable for use. + +Legend support for Boxplot +-------------------------- + +Boxplots now support a *label* parameter to create legend entries. Legend labels can be +passed as a list of strings to label multiple boxes in a single `.Axes.boxplot` call: + +.. plot:: + :include-source: + :alt: Example of creating 3 boxplots and assigning legend labels as a sequence. + + np.random.seed(19680801) + fruit_weights = [ + np.random.normal(130, 10, size=100), + np.random.normal(125, 20, size=100), + np.random.normal(120, 30, size=100), + ] + labels = ['peaches', 'oranges', 'tomatoes'] + colors = ['peachpuff', 'orange', 'tomato'] + + fig, ax = plt.subplots() + ax.set_ylabel('fruit weight (g)') + + bplot = ax.boxplot(fruit_weights, + patch_artist=True, # fill with color + label=labels) + + # fill with colors + for patch, color in zip(bplot['boxes'], colors): + patch.set_facecolor(color) + + ax.set_xticks([]) + ax.legend() + + +Or as a single string to each individual `.Axes.boxplot`: + +.. plot:: + :include-source: + :alt: Example of creating 2 boxplots and assigning each legend label as a string. + + fig, ax = plt.subplots() + + data_A = np.random.random((100, 3)) + data_B = np.random.random((100, 3)) + 0.2 + pos = np.arange(3) + + ax.boxplot(data_A, positions=pos - 0.2, patch_artist=True, label='Box A', + boxprops={'facecolor': 'steelblue'}) + ax.boxplot(data_B, positions=pos + 0.2, patch_artist=True, label='Box B', + boxprops={'facecolor': 'lightblue'}) + + ax.legend() + +Percent sign in pie labels auto-escaped with ``usetex=True`` +------------------------------------------------------------ + +It is common, with `.Axes.pie`, to specify labels that include a percent sign (``%``), +which denotes a comment for LaTeX. When enabling LaTeX with :rc:`text.usetex` or passing +``textprops={"usetex": True}``, this used to cause the percent sign to disappear. + +Now, the percent sign is automatically escaped (by adding a preceding backslash) so that +it appears regardless of the ``usetex`` setting. If you have pre-escaped the percent +sign, this will be detected, and remain as is. + +``hatch`` parameter for stackplot +--------------------------------- + +The `~.Axes.stackplot` *hatch* parameter now accepts a list of strings describing +hatching styles that will be applied sequentially to the layers in the stack: + +.. plot:: + :include-source: + :alt: Two charts, identified as ax1 and ax2, showing "stackplots", i.e. one-dimensional distributions of data stacked on top of one another. The first plot, ax1 has cross-hatching on all slices, having been given a single string as the "hatch" argument. The second plot, ax2 has different styles of hatching on each slice - diagonal hatching in opposite directions on the first two slices, cross-hatching on the third slice, and open circles on the fourth. + + fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,5)) + + cols = 10 + rows = 4 + data = ( + np.reshape(np.arange(0, cols, 1), (1, -1)) ** 2 + + np.reshape(np.arange(0, rows), (-1, 1)) + + np.random.random((rows, cols))*5 + ) + x = range(data.shape[1]) + ax1.stackplot(x, data, hatch="x") + ax2.stackplot(x, data, hatch=["//","\\","x","o"]) + + ax1.set_title("hatch='x'") + ax2.set_title("hatch=['//','\\\\','x','o']") + + plt.show() + +Add option to plot only one half of violin plot +----------------------------------------------- + +Setting the parameter *side* to 'low' or 'high' allows to only plot one half of the +`.Axes.violinplot`. + +.. plot:: + :include-source: + :alt: Three copies of a vertical violin plot; first in blue showing the default of both sides, followed by an orange copy that only shows the "low" (or left, in this case) side, and finally a green copy that only shows the "high" (or right) side. + + # Fake data with reproducible random state. + np.random.seed(19680801) + data = np.random.normal(0, 8, size=100) + + fig, ax = plt.subplots() + + ax.violinplot(data, [0], showmeans=True, showextrema=True) + ax.violinplot(data, [1], showmeans=True, showextrema=True, side='low') + ax.violinplot(data, [2], showmeans=True, showextrema=True, side='high') + + ax.set_title('Violin Sides Example') + ax.set_xticks([0, 1, 2], ['Default', 'side="low"', 'side="high"']) + ax.set_yticklabels([]) + +``axhline`` and ``axhspan`` on polar axes +----------------------------------------- + +... now draw circles and circular arcs (`~.Axes.axhline`) or annuli and wedges +(`~.Axes.axhspan`). + +.. plot:: + :include-source: + :alt: A sample polar plot, that contains an axhline at radius 1, an axhspan annulus between radius 0.8 and 0.9, and an axhspan wedge between radius 0.6 and 0.7 and 288° and 324°. + + fig = plt.figure() + ax = fig.add_subplot(projection="polar") + ax.set_rlim(0, 1.2) + + ax.axhline(1, c="C0", alpha=.5) + ax.axhspan(.8, .9, fc="C1", alpha=.5) + ax.axhspan(.6, .7, .8, .9, fc="C2", alpha=.5) + +Subplot titles can now be automatically aligned +----------------------------------------------- + +Subplot axes titles can be misaligned vertically if tick labels or xlabels are placed at +the top of one subplot. The new `~.Figure.align_titles` method on the `.Figure` class +will now align the titles vertically. + +.. plot:: + :include-source: + :alt: A figure with two Axes side-by-side, the second of which with ticks on top. The Axes titles and x-labels appear unaligned with each other due to these ticks. + + fig, axs = plt.subplots(1, 2, layout='constrained') + + axs[0].plot(np.arange(0, 1e6, 1000)) + axs[0].set_title('Title 0') + axs[0].set_xlabel('XLabel 0') + + axs[1].plot(np.arange(1, 0, -0.1) * 2000, np.arange(1, 0, -0.1)) + axs[1].set_title('Title 1') + axs[1].set_xlabel('XLabel 1') + axs[1].xaxis.tick_top() + axs[1].tick_params(axis='x', rotation=55) + +.. plot:: + :include-source: + :alt: A figure with two Axes side-by-side, the second of which with ticks on top. Unlike the previous figure, the Axes titles and x-labels appear aligned. + + fig, axs = plt.subplots(1, 2, layout='constrained') + + axs[0].plot(np.arange(0, 1e6, 1000)) + axs[0].set_title('Title 0') + axs[0].set_xlabel('XLabel 0') + + axs[1].plot(np.arange(1, 0, -0.1) * 2000, np.arange(1, 0, -0.1)) + axs[1].set_title('Title 1') + axs[1].set_xlabel('XLabel 1') + axs[1].xaxis.tick_top() + axs[1].tick_params(axis='x', rotation=55) + + fig.align_labels() + fig.align_titles() + +``axisartist`` can now be used together with standard ``Formatters`` +-------------------------------------------------------------------- + +... instead of being limited to axisartist-specific ones. + +Toggle minorticks on Axis +------------------------- + +Minor ticks on an `~matplotlib.axis.Axis` can be displayed or removed using +`~matplotlib.axis.Axis.minorticks_on` and `~matplotlib.axis.Axis.minorticks_off`; e.g., +``ax.xaxis.minorticks_on()``. See also `~matplotlib.axes.Axes.minorticks_on`. + +``StrMethodFormatter`` now respects ``axes.unicode_minus`` +---------------------------------------------------------- + +When formatting negative values, `.StrMethodFormatter` will now use unicode minus signs +if :rc:`axes.unicode_minus` is set. + + >>> from matplotlib.ticker import StrMethodFormatter + >>> with plt.rc_context({'axes.unicode_minus': False}): + ... formatter = StrMethodFormatter('{x}') + ... print(formatter.format_data(-10)) + -10 + + >>> with plt.rc_context({'axes.unicode_minus': True}): + ... formatter = StrMethodFormatter('{x}') + ... print(formatter.format_data(-10)) + −10 + +Figure, Axes, and Legend Layout +=============================== + +Subfigures now have controllable zorders +---------------------------------------- + +Previously, setting the zorder of a subfigure had no effect, and those were plotted on +top of any figure-level artists (i.e for example on top of fig-level legends). Now, +subfigures behave like any other artists, and their zorder can be controlled, with +default a zorder of 0. + +.. plot:: + :include-source: + :alt: Example on controlling the zorder of a subfigure + + x = np.linspace(1, 10, 10) + y1, y2 = x, -x + fig = plt.figure(constrained_layout=True) + subfigs = fig.subfigures(nrows=1, ncols=2) + for subfig in subfigs: + axarr = subfig.subplots(2, 1) + for ax in axarr.flatten(): + (l1,) = ax.plot(x, y1, label="line1") + (l2,) = ax.plot(x, y2, label="line2") + subfigs[0].set_zorder(6) + l = fig.legend(handles=[l1, l2], loc="upper center", ncol=2) + +Getters for xmargin, ymargin and zmargin +---------------------------------------- + +`.Axes.get_xmargin`, `.Axes.get_ymargin` and `.Axes3D.get_zmargin` methods have been +added to return the margin values set by `.Axes.set_xmargin`, `.Axes.set_ymargin` and +`.Axes3D.set_zmargin`, respectively. + +Mathtext improvements +===================== + +``mathtext`` documentation improvements +--------------------------------------- + +The documentation is updated to take information directly from the parser. This means +that (almost) all supported symbols, operators, etc. are shown at :ref:`mathtext`. + +``mathtext`` spacing corrections +-------------------------------- + +As consequence of the updated documentation, the spacing on a number of relational and +operator symbols were correctly classified and therefore will be spaced properly. + +Widget Improvements +=================== + +Check and Radio Button widgets support clearing +----------------------------------------------- + +The `.CheckButtons` and `.RadioButtons` widgets now support clearing their state by +calling their ``.clear`` method. Note that it is not possible to have no selected radio +buttons, so the selected option at construction time is selected. + +3D plotting improvements +======================== + +Setting 3D axis limits now set the limits exactly +------------------------------------------------- + +Previously, setting the limits of a 3D axis would always add a small margin to the +limits. Limits are now set exactly by default. The newly introduced rcparam +``axes3d.automargin`` can be used to revert to the old behavior where margin is +automatically added. + +.. plot:: + :include-source: + :alt: Example of the new behavior of 3D axis limits, and how setting the rcParam reverts to the old behavior. + + fig, axs = plt.subplots(1, 2, subplot_kw={'projection': '3d'}) + + plt.rcParams['axes3d.automargin'] = True + axs[0].set(xlim=(0, 1), ylim=(0, 1), zlim=(0, 1), title='Old Behavior') + + plt.rcParams['axes3d.automargin'] = False # the default in 3.9.0 + axs[1].set(xlim=(0, 1), ylim=(0, 1), zlim=(0, 1), title='New Behavior') + +Other improvements +================== + +BackendRegistry +--------------- + +New :class:`~matplotlib.backends.registry.BackendRegistry` class is the single source of +truth for available backends. The singleton instance is +``matplotlib.backends.backend_registry``. It is used internally by Matplotlib, and also +IPython (and therefore Jupyter) starting with IPython 8.24.0. + +There are three sources of backends: built-in (source code is within the Matplotlib +repository), explicit ``module://some.backend`` syntax (backend is obtained by loading +the module), or via an entry point (self-registering backend in an external package). + +To obtain a list of all registered backends use: + + >>> from matplotlib.backends import backend_registry + >>> backend_registry.list_all() + +Add ``widths``, ``heights`` and ``angles`` setter to ``EllipseCollection`` +-------------------------------------------------------------------------- + +The ``widths``, ``heights`` and ``angles`` values of the +`~matplotlib.collections.EllipseCollection` can now be changed after the collection has +been created. + +.. plot:: + :include-source: + + from matplotlib.collections import EllipseCollection + + rng = np.random.default_rng(0) + + widths = (2, ) + heights = (3, ) + angles = (45, ) + offsets = rng.random((10, 2)) * 10 + + fig, ax = plt.subplots() + + ec = EllipseCollection( + widths=widths, + heights=heights, + angles=angles, + offsets=offsets, + units='x', + offset_transform=ax.transData, + ) + + ax.add_collection(ec) + ax.set_xlim(-2, 12) + ax.set_ylim(-2, 12) + + new_widths = rng.random((10, 2)) * 2 + new_heights = rng.random((10, 2)) * 3 + new_angles = rng.random((10, 2)) * 180 + + ec.set(widths=new_widths, heights=new_heights, angles=new_angles) + +``image.interpolation_stage`` rcParam +------------------------------------- + +This new rcParam controls whether image interpolation occurs in "data" space or in +"rgba" space. + +Arrow patch position is now modifiable +-------------------------------------- + +A setter method has been added that allows updating the position of the `.patches.Arrow` +object without requiring a full re-draw. + +.. plot:: + :include-source: + :alt: Example of changing the position of the arrow with the new ``set_data`` method. + + from matplotlib import animation + from matplotlib.patches import Arrow + + fig, ax = plt.subplots() + ax.set_xlim(0, 10) + ax.set_ylim(0, 10) + + a = Arrow(2, 0, 0, 10) + ax.add_patch(a) + + + # code for modifying the arrow + def update(i): + a.set_data(x=.5, dx=i, dy=6, width=2) + + + ani = animation.FuncAnimation(fig, update, frames=15, interval=90, blit=False) + + plt.show() + +NonUniformImage now has mouseover support +----------------------------------------- + +When mousing over a `~matplotlib.image.NonUniformImage`, the data values are now +displayed. diff --git a/doc/release/release_notes.rst b/doc/release/release_notes.rst new file mode 100644 index 000000000000..8ea4ad3d4f57 --- /dev/null +++ b/doc/release/release_notes.rst @@ -0,0 +1,282 @@ +.. redirect-from:: /api/api_changes_old +.. redirect-from:: /users/whats_new_old +.. redirect-from:: /users/release_notes + +.. _release-notes: + +============= +Release notes +============= + +.. include from another document so that it's easy to exclude this for releases +.. ifconfig:: releaselevel == 'dev' + + .. include:: release_notes_next.rst + +Version 3.10 +^^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.10.0.rst + ../api/prev_api_changes/api_changes_3.10.7.rst + ../api/prev_api_changes/api_changes_3.10.1.rst + ../api/prev_api_changes/api_changes_3.10.0.rst + github_stats.rst + prev_whats_new/github_stats_3.10.7.rst + prev_whats_new/github_stats_3.10.6.rst + prev_whats_new/github_stats_3.10.5.rst + prev_whats_new/github_stats_3.10.3.rst + prev_whats_new/github_stats_3.10.1.rst + prev_whats_new/github_stats_3.10.0.rst + +Version 3.9 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.9.0.rst + ../api/prev_api_changes/api_changes_3.9.2.rst + ../api/prev_api_changes/api_changes_3.9.1.rst + ../api/prev_api_changes/api_changes_3.9.0.rst + prev_whats_new/github_stats_3.9.4.rst + prev_whats_new/github_stats_3.9.3.rst + prev_whats_new/github_stats_3.9.2.rst + prev_whats_new/github_stats_3.9.1.rst + prev_whats_new/github_stats_3.9.0.rst + +Version 3.8 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.8.0.rst + ../api/prev_api_changes/api_changes_3.8.1.rst + ../api/prev_api_changes/api_changes_3.8.0.rst + prev_whats_new/github_stats_3.8.3.rst + prev_whats_new/github_stats_3.8.2.rst + prev_whats_new/github_stats_3.8.1.rst + prev_whats_new/github_stats_3.8.0.rst + +Version 3.7 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.7.0.rst + ../api/prev_api_changes/api_changes_3.7.0.rst + prev_whats_new/github_stats_3.7.3.rst + prev_whats_new/github_stats_3.7.2.rst + prev_whats_new/github_stats_3.7.1.rst + prev_whats_new/github_stats_3.7.0.rst + +Version 3.6 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.6.0.rst + ../api/prev_api_changes/api_changes_3.6.1.rst + ../api/prev_api_changes/api_changes_3.6.0.rst + prev_whats_new/github_stats_3.6.3.rst + prev_whats_new/github_stats_3.6.2.rst + prev_whats_new/github_stats_3.6.1.rst + prev_whats_new/github_stats_3.6.0.rst + +Version 3.5 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.5.2.rst + prev_whats_new/whats_new_3.5.0.rst + ../api/prev_api_changes/api_changes_3.5.3.rst + ../api/prev_api_changes/api_changes_3.5.2.rst + ../api/prev_api_changes/api_changes_3.5.0.rst + prev_whats_new/github_stats_3.5.3.rst + prev_whats_new/github_stats_3.5.2.rst + prev_whats_new/github_stats_3.5.1.rst + prev_whats_new/github_stats_3.5.0.rst + +Version 3.4 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.4.0.rst + ../api/prev_api_changes/api_changes_3.4.2.rst + ../api/prev_api_changes/api_changes_3.4.0.rst + prev_whats_new/github_stats_3.4.1.rst + prev_whats_new/github_stats_3.4.0.rst + +Version 3.3 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.3.0.rst + ../api/prev_api_changes/api_changes_3.3.1.rst + ../api/prev_api_changes/api_changes_3.3.0.rst + prev_whats_new/github_stats_3.3.4.rst + prev_whats_new/github_stats_3.3.3.rst + prev_whats_new/github_stats_3.3.2.rst + prev_whats_new/github_stats_3.3.1.rst + prev_whats_new/github_stats_3.3.0.rst + +Version 3.2 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.2.0.rst + ../api/prev_api_changes/api_changes_3.2.0.rst + prev_whats_new/github_stats_3.2.2.rst + prev_whats_new/github_stats_3.2.1.rst + prev_whats_new/github_stats_3.2.0.rst + +Version 3.1 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.1.0.rst + ../api/prev_api_changes/api_changes_3.1.1.rst + ../api/prev_api_changes/api_changes_3.1.0.rst + prev_whats_new/github_stats_3.1.3.rst + prev_whats_new/github_stats_3.1.2.rst + prev_whats_new/github_stats_3.1.1.rst + prev_whats_new/github_stats_3.1.0.rst + +Version 3.0 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_3.0.rst + ../api/prev_api_changes/api_changes_3.0.1.rst + ../api/prev_api_changes/api_changes_3.0.0.rst + prev_whats_new/github_stats_3.0.3.rst + prev_whats_new/github_stats_3.0.2.rst + prev_whats_new/github_stats_3.0.1.rst + prev_whats_new/github_stats_3.0.0.rst + +Version 2.2 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.2.rst + ../api/prev_api_changes/api_changes_2.2.0.rst + +Version 2.1 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.1.0.rst + ../api/prev_api_changes/api_changes_2.1.2.rst + ../api/prev_api_changes/api_changes_2.1.1.rst + ../api/prev_api_changes/api_changes_2.1.0.rst + +Version 2.0 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_2.0.0.rst + ../api/prev_api_changes/api_changes_2.0.1.rst + ../api/prev_api_changes/api_changes_2.0.0.rst + +Version 1.5 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.5.rst + ../api/prev_api_changes/api_changes_1.5.3.rst + ../api/prev_api_changes/api_changes_1.5.2.rst + ../api/prev_api_changes/api_changes_1.5.0.rst + +Version 1.4 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.4.rst + ../api/prev_api_changes/api_changes_1.4.x.rst + +Version 1.3 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.3.rst + ../api/prev_api_changes/api_changes_1.3.x.rst + +Version 1.2 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.2.2.rst + prev_whats_new/whats_new_1.2.rst + ../api/prev_api_changes/api_changes_1.2.x.rst + +Version 1.1 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.1.rst + ../api/prev_api_changes/api_changes_1.1.x.rst + +Version 1.0 +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/whats_new_1.0.rst + +Version 0.x +^^^^^^^^^^^ +.. toctree:: + :maxdepth: 1 + + prev_whats_new/changelog.rst + prev_whats_new/whats_new_0.99.rst + ../api/prev_api_changes/api_changes_0.99.x.rst + ../api/prev_api_changes/api_changes_0.99.rst + prev_whats_new/whats_new_0.98.4.rst + ../api/prev_api_changes/api_changes_0.98.x.rst + ../api/prev_api_changes/api_changes_0.98.1.rst + ../api/prev_api_changes/api_changes_0.98.0.rst + ../api/prev_api_changes/api_changes_0.91.2.rst + ../api/prev_api_changes/api_changes_0.91.0.rst + ../api/prev_api_changes/api_changes_0.90.1.rst + ../api/prev_api_changes/api_changes_0.90.0.rst + + ../api/prev_api_changes/api_changes_0.87.7.rst + ../api/prev_api_changes/api_changes_0.86.rst + ../api/prev_api_changes/api_changes_0.85.rst + ../api/prev_api_changes/api_changes_0.84.rst + ../api/prev_api_changes/api_changes_0.83.rst + ../api/prev_api_changes/api_changes_0.82.rst + ../api/prev_api_changes/api_changes_0.81.rst + ../api/prev_api_changes/api_changes_0.80.rst + + ../api/prev_api_changes/api_changes_0.73.rst + ../api/prev_api_changes/api_changes_0.72.rst + ../api/prev_api_changes/api_changes_0.71.rst + ../api/prev_api_changes/api_changes_0.70.rst + + ../api/prev_api_changes/api_changes_0.65.1.rst + ../api/prev_api_changes/api_changes_0.65.rst + ../api/prev_api_changes/api_changes_0.63.rst + ../api/prev_api_changes/api_changes_0.61.rst + ../api/prev_api_changes/api_changes_0.60.rst + + ../api/prev_api_changes/api_changes_0.54.3.rst + ../api/prev_api_changes/api_changes_0.54.rst + ../api/prev_api_changes/api_changes_0.50.rst + ../api/prev_api_changes/api_changes_0.42.rst + ../api/prev_api_changes/api_changes_0.40.rst diff --git a/doc/release/release_notes_next.rst b/doc/release/release_notes_next.rst new file mode 100644 index 000000000000..de10d5e8dc27 --- /dev/null +++ b/doc/release/release_notes_next.rst @@ -0,0 +1,12 @@ +.. redirect-from:: /users/release_notes_next + +:orphan: + +Next version +============ +.. toctree:: + :maxdepth: 1 + + next_whats_new + ../api/next_api_changes + github_stats diff --git a/doc/resources/index.rst b/doc/resources/index.rst deleted file mode 100644 index b58df6f54f23..000000000000 --- a/doc/resources/index.rst +++ /dev/null @@ -1,67 +0,0 @@ -.. _resources-index: - -******************* - External Resources -******************* - - -============================= - Books, Chapters and Articles -============================= - -* `Mastering matplotlib - `_ - by Duncan M. McGreggor - -* `Interactive Applications Using Matplotlib - `_ - by Benjamin Root - -* `Matplotlib for Python Developers - `_ - by Sandro Tosi - -* `Matplotlib chapter `_ - by John Hunter and Michael Droettboom in The Architecture of Open Source - Applications - -* `Graphics with Matplotlib - `_ - by David J. Raymond - -* `Ten Simple Rules for Better Figures - `_ - by Nicolas P. Rougier, Michael Droettboom and Philip E. Bourne - -======= - Videos -======= - -* `Getting started with Matplotlib - `_ - by `unpingco `_ - -* `Plotting with matplotlib `_ - by Mike Müller - -* `Introduction to NumPy and Matplotlib - `_ by Eric Jones - -* `Anatomy of Matplotlib - `_ - by Benjamin Root - -* `Data Visualization Basics with Python (O'Reilly) - `_ - by Randal S. Olson - -========== - Tutorials -========== - -* `Matplotlib tutorial `_ - by Nicolas P. Rougier - -* `Anatomy of Matplotlib - IPython Notebooks - `_ - by Benjamin Root diff --git a/doc/sphinxext/gallery_order.py b/doc/sphinxext/gallery_order.py new file mode 100644 index 000000000000..99b90062a42a --- /dev/null +++ b/doc/sphinxext/gallery_order.py @@ -0,0 +1,127 @@ +""" +Configuration for the order of gallery sections and examples. +Paths are relative to the conf.py file. +""" + +from sphinx_gallery.sorting import ExplicitOrder + +# Gallery sections shall be displayed in the following order. +# Non-matching sections are inserted at the unsorted position + +UNSORTED = "unsorted" + +examples_order = [ + '../galleries/examples/lines_bars_and_markers', + '../galleries/examples/images_contours_and_fields', + '../galleries/examples/subplots_axes_and_figures', + '../galleries/examples/statistics', + '../galleries/examples/pie_and_polar_charts', + '../galleries/examples/text_labels_and_annotations', + '../galleries/examples/color', + '../galleries/examples/shapes_and_collections', + '../galleries/examples/style_sheets', + '../galleries/examples/pyplots', + '../galleries/examples/axes_grid1', + '../galleries/examples/axisartist', + '../galleries/examples/showcase', + UNSORTED, + '../galleries/examples/userdemo', +] + +tutorials_order = [ + '../galleries/tutorials/introductory', + '../galleries/tutorials/intermediate', + '../galleries/tutorials/advanced', + UNSORTED, + '../galleries/tutorials/provisional' +] + +plot_types_order = [ + '../galleries/plot_types/basic', + '../galleries/plot_types/stats', + '../galleries/plot_types/arrays', + '../galleries/plot_types/unstructured', + '../galleries/plot_types/3D', + UNSORTED +] + +folder_lists = [examples_order, tutorials_order, plot_types_order] + +explicit_order_folders = [fd for folders in folder_lists + for fd in folders[:folders.index(UNSORTED)]] +explicit_order_folders.append(UNSORTED) +explicit_order_folders.extend([fd for folders in folder_lists + for fd in folders[folders.index(UNSORTED):]]) + + +class MplExplicitOrder(ExplicitOrder): + """For use within the 'subsection_order' key.""" + def __call__(self, item): + """Return a string determining the sort order.""" + if item in self.ordered_list: + return f"{self.ordered_list.index(item):04d}" + else: + return f"{self.ordered_list.index(UNSORTED):04d}{item}" + +# Subsection order: +# Subsections are ordered by filename, unless they appear in the following +# lists in which case the list order determines the order within the section. +# Examples/tutorials that do not appear in a list will be appended. + +list_all = [ + # **Tutorials** + # introductory + "quick_start", "pyplot", "images", "lifecycle", "customizing", + # intermediate + "artists", "legend_guide", "color_cycle", + "constrainedlayout_guide", "tight_layout_guide", + # advanced + # text + "text_intro", "text_props", + # colors + "colors", + + # **Examples** + # color + "color_demo", + # pies + "pie_features", "pie_demo2", + # scales + "scales", # Scales overview + + # **Plot Types + # Basic + "plot", "scatter_plot", "bar", "stem", "step", "fill_between", + # Arrays + "imshow", "pcolormesh", "contour", "contourf", + "barbs", "quiver", "streamplot", + # Stats + "hist_plot", "boxplot_plot", "errorbar_plot", "violin", + "eventplot", "hist2d", "hexbin", "pie", + # Unstructured + "tricontour", "tricontourf", "tripcolor", "triplot", + # Spines + "spines", "spine_placement_demo", "spines_dropped", + "multiple_yaxis_with_spines", "centered_spines_with_arrows", + ] +explicit_subsection_order = [item + ".py" for item in list_all] + + +class MplExplicitSubOrder(ExplicitOrder): + """For use within the 'within_subsection_order' key.""" + def __init__(self, src_dir): + self.src_dir = src_dir # src_dir is unused here + self.ordered_list = explicit_subsection_order + + def __call__(self, item): + """Return a string determining the sort order.""" + if item in self.ordered_list: + return f"{self.ordered_list.index(item):04d}" + else: + # ensure not explicitly listed items come last. + return "zzz" + item + + +# Provide the above classes for use in conf.py +sectionorder = MplExplicitOrder(explicit_order_folders) +subsectionorder = MplExplicitSubOrder diff --git a/doc/sphinxext/gen_gallery.py b/doc/sphinxext/gen_gallery.py deleted file mode 100644 index 9992e788f2f3..000000000000 --- a/doc/sphinxext/gen_gallery.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: UTF-8 -*- -import os -import re -import glob -import warnings - -import sphinx.errors - -import matplotlib.image as image - - -exclude_example_sections = ['units'] -multiimage = re.compile('(.*?)(_\d\d){1,2}') - -# generate a thumbnail gallery of examples -gallery_template = """\ -{{% extends "layout.html" %}} -{{% set title = "Thumbnail gallery" %}} - - -{{% block body %}} - -

Click on any image to see full size image and source code

-
- -
  • Gallery -
      - {toc} -
    -
  • - -{gallery} - -{{% endblock %}} -""" - -header_template = """\ -
    -

    - {title} -

    """ - -link_template = """\ -
    - {basename}
    -
    {title}
    -
    -""" - -toc_template = """\ -
  • {title}
  • """ - - -def make_thumbnail(args): - image.thumbnail(args[0], args[1], 0.3) - - -def out_of_date(original, derived): - return (not os.path.exists(derived) or - os.stat(derived).st_mtime < os.stat(original).st_mtime) - - -def gen_gallery(app, doctree): - if app.builder.name not in ('html', 'htmlhelp'): - return - - outdir = app.builder.outdir - rootdir = 'plot_directive/mpl_examples' - - example_sections = list(app.builder.config.mpl_example_sections) - for i, (subdir, title) in enumerate(example_sections): - if subdir in exclude_example_sections: - example_sections.pop(i) - - # images we want to skip for the gallery because they are an unusual - # size that doesn't layout well in a table, or because they may be - # redundant with other images or uninteresting - skips = set([ - 'mathtext_examples', - 'matshow_02', - 'matshow_03', - 'matplotlib_icon', - ]) - - thumbnails = {} - rows = [] - toc_rows = [] - - for subdir, title in example_sections: - rows.append(header_template.format(title=title, section=subdir)) - toc_rows.append(toc_template.format(title=title, section=subdir)) - - origdir = os.path.join('build', rootdir, subdir) - thumbdir = os.path.join(outdir, rootdir, subdir, 'thumbnails') - if not os.path.exists(thumbdir): - os.makedirs(thumbdir) - - data = [] - - for filename in sorted(glob.glob(os.path.join(origdir, '*.png'))): - if filename.endswith("hires.png"): - continue - - path, filename = os.path.split(filename) - basename, ext = os.path.splitext(filename) - if basename in skips: - continue - - # Create thumbnails based on images in tmpdir, and place - # them within the build tree - orig_path = str(os.path.join(origdir, filename)) - thumb_path = str(os.path.join(thumbdir, filename)) - if out_of_date(orig_path, thumb_path) or True: - thumbnails[orig_path] = thumb_path - - m = multiimage.match(basename) - if m is not None: - basename = m.group(1) - - data.append((subdir, basename, - os.path.join(rootdir, subdir, 'thumbnails', filename))) - - for (subdir, basename, thumbfile) in data: - if thumbfile is not None: - link = 'examples/%s/%s.html'%(subdir, basename) - rows.append(link_template.format(link=link, - thumb=thumbfile, - basename=basename, - title=basename)) - - if len(data) == 0: - warnings.warn("No thumbnails were found in %s" % subdir) - - # Close out the
    opened up at the top of this loop - rows.append("
    ") - - content = gallery_template.format(toc='\n'.join(toc_rows), - gallery='\n'.join(rows)) - - # Only write out the file if the contents have actually changed. - # Otherwise, this triggers a full rebuild of the docs - - gallery_path = os.path.join(app.builder.srcdir, - '_templates', 'gallery.html') - if os.path.exists(gallery_path): - fh = open(gallery_path, 'r') - regenerate = fh.read() != content - fh.close() - else: - regenerate = True - - if regenerate: - fh = open(gallery_path, 'w') - fh.write(content) - fh.close() - - for key in app.builder.status_iterator( - iter(thumbnails.keys()), "generating thumbnails... ", - length=len(thumbnails)): - if out_of_date(key, thumbnails[key]): - image.thumbnail(key, thumbnails[key], 0.3) - - -def setup(app): - app.connect('env-updated', gen_gallery) - - try: # multiple plugins may use mpl_example_sections - app.add_config_value('mpl_example_sections', [], True) - except sphinx.errors.ExtensionError: - pass # mpl_example_sections already defined - - metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} - return metadata diff --git a/doc/sphinxext/gen_rst.py b/doc/sphinxext/gen_rst.py deleted file mode 100644 index eff0709f1b6f..000000000000 --- a/doc/sphinxext/gen_rst.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -generate the rst files for the examples by iterating over the pylab examples -""" -from __future__ import print_function -import io -import os -import re -import sys - -import sphinx.errors - - -exclude_example_sections = ['widgets'] -noplot_regex = re.compile(r"#\s*-\*-\s*noplot\s*-\*-") - - -def out_of_date(original, derived): - """ - Returns True if derivative is out-of-date wrt original, - both of which are full file paths. - - TODO: this check isn't adequate in some cases. e.g., if we discover - a bug when building the examples, the original and derived will be - unchanged but we still want to force a rebuild. - """ - return (not os.path.exists(derived) or - os.stat(derived).st_mtime < os.stat(original).st_mtime) - -def generate_example_rst(app): - rootdir = os.path.join(app.builder.srcdir, 'mpl_examples') - exampledir = os.path.join(app.builder.srcdir, 'examples') - if not os.path.exists(exampledir): - os.makedirs(exampledir) - - example_sections = list(app.builder.config.mpl_example_sections) - for i, (subdir, title) in enumerate(example_sections): - if subdir in exclude_example_sections: - example_sections.pop(i) - example_subdirs, titles = zip(*example_sections) - - datad = {} - for root, subFolders, files in os.walk(rootdir): - for fname in files: - if ( fname.startswith('.') or fname.startswith('#') - or fname.startswith('_') or not fname.endswith('.py') ): - continue - - fullpath = os.path.join(root,fname) - contents = io.open(fullpath, encoding='utf8').read() - # indent - relpath = os.path.split(root)[-1] - datad.setdefault(relpath, []).append((fullpath, fname, contents)) - - subdirs = list(datad.keys()) - subdirs.sort() - - fhindex = open(os.path.join(exampledir, 'index.rst'), 'w') - fhindex.write("""\ -.. _examples-index: - -#################### -Matplotlib Examples -#################### - -.. htmlonly:: - - :Release: |version| - :Date: |today| - -.. toctree:: - :maxdepth: 2 - -""") - - for subdir in subdirs: - rstdir = os.path.join(exampledir, subdir) - if not os.path.exists(rstdir): - os.makedirs(rstdir) - - outputdir = os.path.join(app.builder.outdir, 'examples') - if not os.path.exists(outputdir): - os.makedirs(outputdir) - - outputdir = os.path.join(outputdir, subdir) - if not os.path.exists(outputdir): - os.makedirs(outputdir) - - subdirIndexFile = os.path.join(rstdir, 'index.rst') - fhsubdirIndex = open(subdirIndexFile, 'w') - fhindex.write(' %s/index.rst\n\n'%subdir) - - fhsubdirIndex.write("""\ -.. _%s-examples-index: - -############################################## -%s Examples -############################################## - -.. htmlonly:: - - :Release: |version| - :Date: |today| - -.. toctree:: - :maxdepth: 1 - -"""%(subdir, subdir)) - - sys.stdout.write(subdir + ", ") - sys.stdout.flush() - - data = datad[subdir] - data.sort() - - for fullpath, fname, contents in data: - basename, ext = os.path.splitext(fname) - outputfile = os.path.join(outputdir, fname) - #thumbfile = os.path.join(thumb_dir, '%s.png'%basename) - #print ' static_dir=%s, basename=%s, fullpath=%s, fname=%s, thumb_dir=%s, thumbfile=%s'%(static_dir, basename, fullpath, fname, thumb_dir, thumbfile) - - rstfile = '%s.rst'%basename - outrstfile = os.path.join(rstdir, rstfile) - - # XXX: We might consider putting extra metadata in the example - # files to include a title. If so, this line is where we would add - # this information. - fhsubdirIndex.write(' %s <%s>\n'%(os.path.basename(basename),rstfile)) - - do_plot = (subdir in example_subdirs - and not noplot_regex.search(contents)) - if not do_plot: - fhstatic = io.open(outputfile, 'w', encoding='utf-8') - fhstatic.write(contents) - fhstatic.close() - - if not out_of_date(fullpath, outrstfile): - continue - - fh = io.open(outrstfile, 'w', encoding='utf-8') - fh.write(u'.. _%s-%s:\n\n' % (subdir, basename)) - title = '%s example code: %s'%(subdir, fname) - #title = ' %s example code: %s'%(thumbfile, subdir, fname) - - fh.write(title + u'\n') - fh.write(u'=' * len(title) + u'\n\n') - - if do_plot: - fh.write(u"\n\n.. plot:: %s\n\n::\n\n" % fullpath) - else: - fh.write(u"[`source code <%s>`_]\n\n::\n\n" % fname) - - # indent the contents - contents = u'\n'.join([u' %s'%row.rstrip() for row in contents.split(u'\n')]) - fh.write(contents) - - fh.write(u'\n\nKeywords: python, matplotlib, pylab, example, codex (see :ref:`how-to-search-examples`)') - fh.close() - - fhsubdirIndex.close() - - fhindex.close() - - print() - -def setup(app): - app.connect('builder-inited', generate_example_rst) - - try: # multiple plugins may use mpl_example_sections - app.add_config_value('mpl_example_sections', [], True) - except sphinx.errors.ExtensionError: - pass # mpl_example_sections already defined - - metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} - return metadata diff --git a/doc/sphinxext/github.py b/doc/sphinxext/github.py index 8f0ffc0d9782..0a96ac185f86 100644 --- a/doc/sphinxext/github.py +++ b/doc/sphinxext/github.py @@ -1,4 +1,5 @@ -"""Define text roles for GitHub +""" +Define text roles for GitHub. * ghissue - Issue * ghpull - Pull Request @@ -20,8 +21,10 @@ from docutils import nodes, utils from docutils.parsers.rst.roles import set_classes + def make_link_node(rawtext, app, type, slug, options): - """Create a link to a github resource. + """ + Create a link to a github resource. :param rawtext: Text being replaced with link node. :param app: Sphinx application context @@ -37,7 +40,9 @@ def make_link_node(rawtext, app, type, slug, options): if not base.endswith('/'): base += '/' except AttributeError as err: - raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) + raise ValueError( + f'github_project_url configuration value is not set ' + f'({err})') from err ref = base + type + '/' + slug + '/' set_classes(options) @@ -48,8 +53,10 @@ def make_link_node(rawtext, app, type, slug, options): **options) return node + def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - """Link to a GitHub issue. + """ + Link to a GitHub issue. Returns 2 part tuple containing list of nodes to insert into the document and a list of system messages. Both are allowed to be @@ -75,7 +82,6 @@ def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app - #app.info('issue %r' % text) if 'pull' in name.lower(): category = 'pull' elif 'issue' in name.lower(): @@ -89,8 +95,10 @@ def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): node = make_link_node(rawtext, app, category, str(issue_num), options) return [node], [] + def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - """Link to a GitHub user. + """ + Link to a GitHub user. Returns 2 part tuple containing list of nodes to insert into the document and a list of system messages. Both are allowed to be @@ -104,14 +112,15 @@ def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param options: Directive options for customization. :param content: The directive content for customization. """ - app = inliner.document.settings.env.app - #app.info('user link %r' % text) ref = 'https://www.github.com/' + text node = nodes.reference(rawtext, text, refuri=ref, **options) return [node], [] -def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): - """Link to a GitHub commit. + +def ghcommit_role( + name, rawtext, text, lineno, inliner, options={}, content=[]): + """ + Link to a GitHub commit. Returns 2 part tuple containing list of nodes to insert into the document and a list of system messages. Both are allowed to be @@ -126,7 +135,6 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param content: The directive content for customization. """ app = inliner.document.settings.env.app - #app.info('user link %r' % text) try: base = app.config.github_project_url if not base: @@ -134,7 +142,9 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): if not base.endswith('/'): base += '/' except AttributeError as err: - raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) + raise ValueError( + f'github_project_url configuration value is not set ' + f'({err})') from err ref = base + text node = nodes.reference(rawtext, text[:6], refuri=ref, **options) @@ -142,11 +152,11 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): def setup(app): - """Install the plugin. + """ + Install the plugin. :param app: Sphinx application context. """ - app.info('Initializing GitHub plugin') app.add_role('ghissue', ghissue_role) app.add_role('ghpull', ghissue_role) app.add_role('ghuser', ghuser_role) diff --git a/doc/sphinxext/math_symbol_table.py b/doc/sphinxext/math_symbol_table.py index d0edb7c4b1ae..a143326ab75b 100644 --- a/doc/sphinxext/math_symbol_table.py +++ b/doc/sphinxext/math_symbol_table.py @@ -1,163 +1,152 @@ -from __future__ import print_function +import re +from docutils.parsers.rst import Directive + +from matplotlib import _mathtext, _mathtext_data + +bb_pattern = re.compile("Bbb[A-Z]") +scr_pattern = re.compile("scr[a-zA-Z]") +frak_pattern = re.compile("frak[A-Z]") + symbols = [ ["Lower-case Greek", - 5, - r"""\alpha \beta \gamma \chi \delta \epsilon \eta \iota \kappa - \lambda \mu \nu \omega \phi \pi \psi \rho \sigma \tau \theta - \upsilon \xi \zeta \digamma \varepsilon \varkappa \varphi - \varpi \varrho \varsigma \vartheta"""], + 4, + (r"\alpha", r"\beta", r"\gamma", r"\chi", r"\delta", r"\epsilon", + r"\eta", r"\iota", r"\kappa", r"\lambda", r"\mu", r"\nu", r"\omega", + r"\phi", r"\pi", r"\psi", r"\rho", r"\sigma", r"\tau", r"\theta", + r"\upsilon", r"\xi", r"\zeta", r"\digamma", r"\varepsilon", r"\varkappa", + r"\varphi", r"\varpi", r"\varrho", r"\varsigma", r"\vartheta")], ["Upper-case Greek", - 6, - r"""\Delta \Gamma \Lambda \Omega \Phi \Pi \Psi \Sigma \Theta - \Upsilon \Xi \mho \nabla"""], - ["Hebrew", 4, - r"""\aleph \beth \daleth \gimel"""], - ["Delimiters", + (r"\Delta", r"\Gamma", r"\Lambda", r"\Omega", r"\Phi", r"\Pi", r"\Psi", + r"\Sigma", r"\Theta", r"\Upsilon", r"\Xi")], + ["Hebrew", 6, - r"""| \{ \lfloor / \Uparrow \llcorner \vert \} \rfloor \backslash - \uparrow \lrcorner \| \langle \lceil [ \Downarrow \ulcorner - \Vert \rangle \rceil ] \downarrow \urcorner"""], + (r"\aleph", r"\beth", r"\gimel", r"\daleth")], + ["Latin named characters", + 6, + r"""\aa \AA \ae \AE \oe \OE \O \o \thorn \Thorn \ss \eth \dh \DH""".split()], + ["Delimiters", + 5, + _mathtext.Parser._delims], ["Big symbols", 5, - r"""\bigcap \bigcup \bigodot \bigoplus \bigotimes \biguplus - \bigvee \bigwedge \coprod \oint \prod \sum \int"""], + _mathtext.Parser._overunder_symbols | _mathtext.Parser._dropsub_symbols], ["Standard function names", + 5, + {fr"\{fn}" for fn in _mathtext.Parser._function_names}], + ["Binary operation symbols", + 4, + _mathtext.Parser._binary_operators], + ["Relation symbols", 4, - r"""\arccos \csc \ker \min \arcsin \deg \lg \Pr \arctan \det \lim - \gcd \ln \sup \cot \hom \log \tan \coth \inf \max \tanh - \sec \arg \dim \liminf \sin \cos \exp \limsup \sinh \cosh"""], - ["Binary operation and relation symbols", - 3, - r"""\ast \pm \slash \cap \star \mp \cup \cdot \uplus - \triangleleft \circ \odot \sqcap \triangleright \bullet \ominus - \sqcup \bigcirc \oplus \wedge \diamond \oslash \vee - \bigtriangledown \times \otimes \dag \bigtriangleup \div \wr - \ddag \barwedge \veebar \boxplus \curlywedge \curlyvee \boxminus - \Cap \Cup \boxtimes \bot \top \dotplus \boxdot \intercal - \rightthreetimes \divideontimes \leftthreetimes \equiv \leq \geq - \perp \cong \prec \succ \mid \neq \preceq \succeq \parallel \sim - \ll \gg \bowtie \simeq \subset \supset \Join \approx \subseteq - \supseteq \ltimes \asymp \sqsubset \sqsupset \rtimes \doteq - \sqsubseteq \sqsupseteq \smile \propto \dashv \vdash \frown - \models \in \ni \notin \approxeq \leqq \geqq \lessgtr \leqslant - \geqslant \lesseqgtr \backsim \lessapprox \gtrapprox \lesseqqgtr - \backsimeq \lll \ggg \gtreqqless \triangleq \lessdot \gtrdot - \gtreqless \circeq \lesssim \gtrsim \gtrless \bumpeq \eqslantless - \eqslantgtr \backepsilon \Bumpeq \precsim \succsim \between - \doteqdot \precapprox \succapprox \pitchfork \Subset \Supset - \fallingdotseq \subseteqq \supseteqq \risingdotseq \sqsubset - \sqsupset \varpropto \preccurlyeq \succcurlyeq \Vdash \therefore - \curlyeqprec \curlyeqsucc \vDash \because \blacktriangleleft - \blacktriangleright \Vvdash \eqcirc \trianglelefteq - \trianglerighteq \neq \vartriangleleft \vartriangleright \ncong - \nleq \ngeq \nsubseteq \nmid \nsupseteq \nparallel \nless \ngtr - \nprec \nsucc \subsetneq \nsim \supsetneq \nVDash \precnapprox - \succnapprox \subsetneqq \nvDash \precnsim \succnsim \supsetneqq - \nvdash \lnapprox \gnapprox \ntriangleleft \ntrianglelefteq - \lneqq \gneqq \ntriangleright \lnsim \gnsim \ntrianglerighteq - \coloneq \eqsim \nequiv \napprox \nsupset \doublebarwedge \nVdash - \Doteq \nsubset \eqcolon \ne - """], + _mathtext.Parser._relation_symbols], ["Arrow symbols", - 2, - r"""\leftarrow \longleftarrow \uparrow \Leftarrow \Longleftarrow - \Uparrow \rightarrow \longrightarrow \downarrow \Rightarrow - \Longrightarrow \Downarrow \leftrightarrow \updownarrow - \longleftrightarrow \updownarrow \Leftrightarrow - \Longleftrightarrow \Updownarrow \mapsto \longmapsto \nearrow - \hookleftarrow \hookrightarrow \searrow \leftharpoonup - \rightharpoonup \swarrow \leftharpoondown \rightharpoondown - \nwarrow \rightleftharpoons \leadsto \dashrightarrow - \dashleftarrow \leftleftarrows \leftrightarrows \Lleftarrow - \Rrightarrow \twoheadleftarrow \leftarrowtail \looparrowleft - \leftrightharpoons \curvearrowleft \circlearrowleft \Lsh - \upuparrows \upharpoonleft \downharpoonleft \multimap - \leftrightsquigarrow \rightrightarrows \rightleftarrows - \rightrightarrows \rightleftarrows \twoheadrightarrow - \rightarrowtail \looparrowright \rightleftharpoons - \curvearrowright \circlearrowright \Rsh \downdownarrows - \upharpoonright \downharpoonright \rightsquigarrow \nleftarrow - \nrightarrow \nLeftarrow \nRightarrow \nleftrightarrow - \nLeftrightarrow \to \Swarrow \Searrow \Nwarrow \Nearrow - \leftsquigarrow - """], + 4, + _mathtext.Parser._arrow_symbols], + ["Dot symbols", + 4, + r"""\cdots \vdots \ldots \ddots \adots \Colon \therefore \because""".split()], + ["Black-board characters", + 6, + [fr"\{symbol}" for symbol in _mathtext_data.tex2uni + if re.match(bb_pattern, symbol)]], + ["Script characters", + 6, + [fr"\{symbol}" for symbol in _mathtext_data.tex2uni + if re.match(scr_pattern, symbol)]], + ["Fraktur characters", + 6, + [fr"\{symbol}" for symbol in _mathtext_data.tex2uni + if re.match(frak_pattern, symbol)]], ["Miscellaneous symbols", - 3, + 4, r"""\neg \infty \forall \wp \exists \bigstar \angle \partial - \nexists \measuredangle \eth \emptyset \sphericalangle \clubsuit + \nexists \measuredangle \emptyset \sphericalangle \clubsuit \varnothing \complement \diamondsuit \imath \Finv \triangledown - \heartsuit \jmath \Game \spadesuit \ell \hbar \vartriangle \cdots - \hslash \vdots \blacksquare \ldots \blacktriangle \ddots \sharp + \heartsuit \jmath \Game \spadesuit \ell \hbar \vartriangle + \hslash \blacksquare \blacktriangle \sharp \increment \prime \blacktriangledown \Im \flat \backprime \Re \natural - \circledS \P \copyright \ss \circledR \S \yen \AA \checkmark \$ - \iiint \iint \iint \oiiint"""] + \circledS \P \copyright \circledR \S \yen \checkmark \$ + \cent \triangle \QED \sinewave \dag \ddag \perthousand \ac + \lambdabar \L \l \degree \danger \maltese \clubsuitopen + \i \hermitmatrix \sterling \nabla \mho""".split()], ] + def run(state_machine): - def get_n(n, l): - part = [] - for x in l: - part.append(x) - if len(part) == n: - yield part - part = [] - yield part + + def render_symbol(sym, ignore_variant=False): + if ignore_variant and sym not in (r"\varnothing", r"\varlrtriangle"): + sym = sym.replace(r"\var", "\\") + if sym.startswith("\\"): + sym = sym.lstrip("\\") + if sym not in (_mathtext.Parser._overunder_functions | + _mathtext.Parser._function_names): + sym = chr(_mathtext_data.tex2uni[sym]) + return f'\\{sym}' if sym in ('\\', '|', '+', '-', '*') else sym lines = [] for category, columns, syms in symbols: - syms = syms.split() - syms.sort() + syms = sorted(syms, + # Sort by Unicode and place variants immediately + # after standard versions. + key=lambda sym: (render_symbol(sym, ignore_variant=True), + sym.startswith(r"\var")), + reverse=(category == "Hebrew")) # Hebrew is rtl + rendered_syms = [f"{render_symbol(sym)} ``{sym}``" for sym in syms] + columns = min(columns, len(syms)) lines.append("**%s**" % category) lines.append('') - max_width = 0 - for sym in syms: - max_width = max(max_width, len(sym)) - max_width = max_width * 2 + 16 - header = " " + (('=' * max_width) + ' ') * columns - format = '%%%ds' % max_width - for chunk in get_n(20, get_n(columns, syms)): - lines.append(header) - for part in chunk: - line = [] - for sym in part: - line.append(format % (":math:`%s` ``%s``" % (sym, sym))) - lines.append(" " + " ".join(line)) - lines.append(header) - lines.append('') + max_width = max(map(len, rendered_syms)) + header = (('=' * max_width) + ' ') * columns + lines.append(header.rstrip()) + for part in range(0, len(rendered_syms), columns): + row = " ".join( + sym.rjust(max_width) for sym in rendered_syms[part:part + columns]) + lines.append(row) + lines.append(header.rstrip()) + lines.append('') state_machine.insert_input(lines, "Symbol table") return [] -def math_symbol_table_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - return run(state_machine) + +class MathSymbolTableDirective(Directive): + has_content = False + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + return run(self.state_machine) + def setup(app): - app.add_directive( - 'math_symbol_table', math_symbol_table_directive, - False, (0, 1, 0)) + app.add_directive("math_symbol_table", MathSymbolTableDirective) metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} return metadata + if __name__ == "__main__": # Do some verification of the tables - from matplotlib import _mathtext_data print("SYMBOLS NOT IN STIX:") all_symbols = {} for category, columns, syms in symbols: if category == "Standard Function Names": continue - syms = syms.split() for sym in syms: if len(sym) > 1: all_symbols[sym[1:]] = None if sym[1:] not in _mathtext_data.tex2uni: print(sym) + # Add accents + all_symbols.update({v[1:]: k for k, v in _mathtext.Parser._accent_map.items()}) + all_symbols.update({v: v for v in _mathtext.Parser._wide_accents}) print("SYMBOLS NOT IN TABLE:") - for sym in _mathtext_data.tex2uni: + for sym, val in _mathtext_data.tex2uni.items(): if sym not in all_symbols: - print(sym) + print(f"{sym} = {chr(val)}") diff --git a/doc/sphinxext/missing_references.py b/doc/sphinxext/missing_references.py new file mode 100644 index 000000000000..87432bc524b4 --- /dev/null +++ b/doc/sphinxext/missing_references.py @@ -0,0 +1,232 @@ +""" +This is a sphinx extension to freeze your broken reference problems +when using ``nitpicky = True``. + +The basic operation is: + +1. Add this extension to your ``conf.py`` extensions. +2. Add ``missing_references_write_json = True`` to your ``conf.py`` +3. Run sphinx-build. It will generate ``missing-references.json`` + next to your ``conf.py``. +4. Remove ``missing_references_write_json = True`` from your + ``conf.py`` (or set it to ``False``) +5. Run sphinx-build again, and ``nitpick_ignore`` will + contain all of the previously failed references. + +""" + +from collections import defaultdict +import json +from pathlib import Path + +from docutils.utils import get_source_line +from sphinx.util import logging as sphinx_logging + +import matplotlib + +logger = sphinx_logging.getLogger(__name__) + + +def get_location(node, app): + """ + Given a docutils node and a sphinx application, return a string + representation of the source location of this node. + + Usually, this will be of the form "path/to/file:linenumber". Two + special values can be emitted, "" for paths which are + not contained in this source tree (e.g. docstrings included from + other modules) or "", indicating that the sphinx application + cannot locate the original source file (usually because an extension + has injected text into the sphinx parsing engine). + """ + source, line = get_source_line(node) + + if source: + # 'source' can have the form '/some/path:docstring of some.api' but the + # colons are forbidden on windows, but on posix just passes through. + if ':docstring of' in source: + path, *post = source.rpartition(':docstring of') + post = ''.join(post) + else: + path = source + post = '' + # We locate references relative to the parent of the doc + # directory, which for matplotlib, will be the root of the + # matplotlib repo. When matplotlib is not an editable install + # weird things will happen, but we can't totally recover from + # that. + basepath = Path(app.srcdir).parent.resolve() + + fullpath = Path(path).resolve() + + try: + path = fullpath.relative_to(basepath) + except ValueError: + # Sometimes docs directly contain e.g. docstrings + # from installed modules, and we record those as + # so as to be independent of where the + # module was installed + path = Path("") / fullpath.name + + # Ensure that all reported paths are POSIX so that docs + # on windows result in the same warnings in the JSON file. + path = path.as_posix() + + else: + path = "" + post = '' + if not line: + line = "" + + return f"{path}{post}:{line}" + + +def _truncate_location(location): + """ + Cuts off anything after the first colon in location strings. + + This allows for easy comparison even when line numbers change + (as they do regularly). + """ + return location.split(":", 1)[0] + + +def handle_missing_reference(app, domain, node): + """ + Handle the warn-missing-reference Sphinx event. + + This function will: + + #. record missing references for saving/comparing with ignored list. + #. prevent Sphinx from raising a warning on ignored references. + """ + refdomain = node["refdomain"] + reftype = node["reftype"] + target = node["reftarget"] + location = get_location(node, app) + domain_type = f"{refdomain}:{reftype}" + + app.env.missing_references_events[(domain_type, target)].add(location) + + # If we're ignoring this event, return True so that Sphinx thinks we handled it, + # even though we didn't print or warn. If we aren't ignoring it, Sphinx will print a + # warning about the missing reference. + if location in app.env.missing_references_ignored_references.get( + (domain_type, target), []): + return True + + +def warn_unused_missing_references(app, exc): + """ + Check that all lines of the existing JSON file are still necessary. + """ + # We can only warn if we are building from a source install + # otherwise, we just have to skip this step. + basepath = Path(matplotlib.__file__).parent.parent.parent.resolve() + srcpath = Path(app.srcdir).parent.resolve() + + if basepath != srcpath: + return + + # This is a dictionary of {(domain_type, target): locations} + references_ignored = app.env.missing_references_ignored_references + references_events = app.env.missing_references_events + + # Warn about any reference which is no longer missing. + for (domain_type, target), locations in references_ignored.items(): + missing_reference_locations = [ + _truncate_location(location) + for location in references_events.get((domain_type, target), [])] + + # For each ignored reference location, ensure a missing reference + # was observed. If it wasn't observed, issue a warning. + for ignored_reference_location in locations: + short_location = _truncate_location(ignored_reference_location) + if short_location not in missing_reference_locations: + msg = (f"Reference {domain_type} {target} for " + f"{ignored_reference_location} can be removed" + f" from {app.config.missing_references_filename}." + " It is no longer a missing reference in the docs.") + logger.warning(msg, + location=ignored_reference_location, + type='ref', + subtype=domain_type) + + +def save_missing_references(app, exc): + """ + Write a new JSON file containing missing references. + """ + json_path = Path(app.confdir) / app.config.missing_references_filename + references_warnings = app.env.missing_references_events + _write_missing_references_json(references_warnings, json_path) + + +def _write_missing_references_json(records, json_path): + """ + Convert ignored references to a format which we can write as JSON + + Convert from ``{(domain_type, target): locations}`` to + ``{domain_type: {target: locations}}`` since JSON can't serialize tuples. + """ + # Sorting records and keys avoids needlessly big diffs when + # missing_references.json is regenerated. + transformed_records = defaultdict(dict) + for (domain_type, target), paths in records.items(): + transformed_records[domain_type][target] = sorted(paths) + with json_path.open("w") as stream: + json.dump(transformed_records, stream, sort_keys=True, indent=2) + stream.write("\n") # Silence pre-commit no-newline-at-end-of-file warning. + + +def _read_missing_references_json(json_path): + """ + Convert from the JSON file to the form used internally by this + extension. + + The JSON file is stored as ``{domain_type: {target: [locations,]}}`` + since JSON can't store dictionary keys which are tuples. We convert + this back to ``{(domain_type, target):[locations]}`` for internal use. + + """ + with json_path.open("r") as stream: + data = json.load(stream) + + ignored_references = {} + for domain_type, targets in data.items(): + for target, locations in targets.items(): + ignored_references[(domain_type, target)] = locations + return ignored_references + + +def prepare_missing_references_setup(app): + """ + Initialize this extension once the configuration is ready. + """ + if not app.config.missing_references_enabled: + # no-op when we are disabled. + return + + app.connect("warn-missing-reference", handle_missing_reference) + if app.config.missing_references_warn_unused_ignores: + app.connect("build-finished", warn_unused_missing_references) + if app.config.missing_references_write_json: + app.connect("build-finished", save_missing_references) + + json_path = Path(app.confdir) / app.config.missing_references_filename + app.env.missing_references_ignored_references = ( + _read_missing_references_json(json_path) if json_path.exists() else {} + ) + app.env.missing_references_events = defaultdict(set) + + +def setup(app): + app.add_config_value("missing_references_enabled", True, "env") + app.add_config_value("missing_references_write_json", False, "env") + app.add_config_value("missing_references_warn_unused_ignores", True, "env") + app.add_config_value("missing_references_filename", + "missing-references.json", "env") + + app.connect("builder-inited", prepare_missing_references_setup) + + return {'parallel_read_safe': True} diff --git a/doc/sphinxext/mock_gui_toolkits.py b/doc/sphinxext/mock_gui_toolkits.py new file mode 100644 index 000000000000..a3eee4dea61a --- /dev/null +++ b/doc/sphinxext/mock_gui_toolkits.py @@ -0,0 +1,13 @@ +import sys +from unittest.mock import MagicMock + + +class MyCairoCffi(MagicMock): + __name__ = "cairocffi" + + +def setup(app): + sys.modules.update( + cairocffi=MyCairoCffi(), + ) + return {'parallel_read_safe': True, 'parallel_write_safe': True} diff --git a/doc/sphinxext/redirect_from.py b/doc/sphinxext/redirect_from.py new file mode 100644 index 000000000000..329352b3a3c8 --- /dev/null +++ b/doc/sphinxext/redirect_from.py @@ -0,0 +1,132 @@ +""" +Redirecting old docs to new location +==================================== + +If an rst file is moved or its content subsumed in a different file, it +is desirable to redirect the old file to the new or existing file. This +extension enables this with a simple html refresh. + +For example suppose ``doc/topic/old-page.rst`` is removed and its content +included in ``doc/topic/new-page.rst``. We use the ``redirect-from`` +directive in ``doc/topic/new-page.rst``:: + + .. redirect-from:: /topic/old-page + +This creates in the build directory a file ``build/html/topic/old-page.html`` +that contains a relative refresh:: + + + + + + + + + +If you need to redirect across subdirectory trees, that works as well. For +instance if ``doc/topic/subdir1/old-page.rst`` is now found at +``doc/topic/subdir2/new-page.rst`` then ``new-page.rst`` just lists the +full path:: + + .. redirect-from:: /topic/subdir1/old-page.rst + +""" + +from pathlib import Path +from sphinx.util.docutils import SphinxDirective +from sphinx.domains import Domain +from sphinx.util import logging + +logger = logging.getLogger(__name__) + + +HTML_TEMPLATE = """ + + + + + + +""" + + +def setup(app): + app.add_directive("redirect-from", RedirectFrom) + app.add_domain(RedirectFromDomain) + app.connect("builder-inited", _clear_redirects) + app.connect("build-finished", _generate_redirects) + + metadata = {'parallel_read_safe': True} + return metadata + + +class RedirectFromDomain(Domain): + """ + The sole purpose of this domain is a parallel_read_safe data store for the + redirects mapping. + """ + name = 'redirect_from' + label = 'redirect_from' + + @property + def redirects(self): + """The mapping of the redirects.""" + return self.data.setdefault('redirects', {}) + + def clear_doc(self, docname): + self.redirects.pop(docname, None) + + def merge_domaindata(self, docnames, otherdata): + for src, dst in otherdata['redirects'].items(): + if src not in self.redirects: + self.redirects[src] = dst + elif self.redirects[src] != dst: + raise ValueError( + f"Inconsistent redirections from {src} to " + f"{self.redirects[src]} and {otherdata['redirects'][src]}") + + +class RedirectFrom(SphinxDirective): + required_arguments = 1 + + def run(self): + redirected_doc, = self.arguments + domain = self.env.get_domain('redirect_from') + current_doc = self.env.path2doc(self.state.document.current_source) + redirected_reldoc, _ = self.env.relfn2path(redirected_doc, current_doc) + if ( + redirected_reldoc in domain.redirects + and domain.redirects[redirected_reldoc] != current_doc + ): + raise ValueError( + f"{redirected_reldoc} is already noted as redirecting to " + f"{domain.redirects[redirected_reldoc]}\n" + f"Cannot also redirect it to {current_doc}" + ) + domain.redirects[redirected_reldoc] = current_doc + return [] + + +def _generate_redirects(app, exception): + builder = app.builder + if builder.name != "html" or exception: + return + for k, v in app.env.get_domain('redirect_from').redirects.items(): + p = Path(app.outdir, k + builder.out_suffix) + html = HTML_TEMPLATE.format(v=builder.get_relative_uri(k, v)) + if p.is_file(): + if p.read_text() != html: + logger.warning('A redirect-from directive is trying to ' + 'create %s, but that file already exists ' + '(perhaps you need to run "make clean")', p) + else: + logger.info('making refresh html file: %s redirect to %s', k, v) + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(html, encoding='utf-8') + + +def _clear_redirects(app): + domain = app.env.get_domain('redirect_from') + if domain.redirects: + logger.info('clearing cached redirects') + domain.redirects.clear() diff --git a/doc/sphinxext/skip_deprecated.py b/doc/sphinxext/skip_deprecated.py new file mode 100644 index 000000000000..d4ef795e9ab1 --- /dev/null +++ b/doc/sphinxext/skip_deprecated.py @@ -0,0 +1,17 @@ +# Skip deprecated members + + +def skip_deprecated(app, what, name, obj, skip, options): + if skip: + return skip + skipped = {"matplotlib.colors": ["ColorConverter", "hex2color", "rgb2hex"]} + skip_list = skipped.get(getattr(obj, "__module__", None)) + if skip_list is not None: + return getattr(obj, "__name__", None) in skip_list + + +def setup(app): + app.connect('autodoc-skip-member', skip_deprecated) + + metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} + return metadata diff --git a/doc/sphinxext/util.py b/doc/sphinxext/util.py new file mode 100644 index 000000000000..c0f336eaea18 --- /dev/null +++ b/doc/sphinxext/util.py @@ -0,0 +1,53 @@ +import sys + +from sphinx_gallery import gen_rst + + +def matplotlib_reduced_latex_scraper(block, block_vars, gallery_conf, + **kwargs): + """ + Reduce srcset when creating a PDF. + + Because sphinx-gallery runs *very* early, we cannot modify this even in the + earliest builder-inited signal. Thus we do it at scraping time. + """ + from sphinx_gallery.scrapers import matplotlib_scraper + + if gallery_conf['builder_name'] == 'latex': + gallery_conf['image_srcset'] = [] + return matplotlib_scraper(block, block_vars, gallery_conf, **kwargs) + + +# Clear basic_units module to re-register with unit registry on import. +def clear_basic_units(gallery_conf, fname): + return sys.modules.pop('basic_units', None) + + +# Monkey-patching gallery header to include search keywords +EXAMPLE_HEADER = """ +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "{0}" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. meta:: + :keywords: codex + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code{2} + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_{1}: + +""" + + +def patch_header(gallery_conf, fname): + gen_rst.EXAMPLE_HEADER = EXAMPLE_HEADER diff --git a/doc/thirdpartypackages/index.rst b/doc/thirdpartypackages/index.rst index 0c81916ca5e9..81dc4d710a52 100644 --- a/doc/thirdpartypackages/index.rst +++ b/doc/thirdpartypackages/index.rst @@ -1,173 +1,5 @@ -.. _thirdparty-index: +:orphan: -********************* - Third party packages -********************* +.. raw:: html -Several external packages that extend or build on Matplotlib functionality -exist. Below we list a number of these. Note that they are each -maintained and distributed separately from Matplotlib, and will need -to be installed individually. - -Please submit an issue or pull request -on Github if you have created a package that you would like to have included. -We are also happy to host third party packages within the `Matplotlib Github -Organization `_. - -.. _hl_plotting: - -High-Level Plotting -******************* - -Several projects provide higher-level interfaces for creating -matplotlib plots. - -.. _toolkit_seaborn: - -seaborn -======= - -`seaborn `_ is a high level interface for drawing -statistical graphics with matplotlib. It aims to make visualization a central -part of exploring and understanding complex datasets. - -.. image:: /_static/seaborn.png - :height: 157px - -.. _toolkit_ggplot: - -ggplot -====== - -`ggplot `_ is a port of the R ggplot2 package -to python based on matplotlib. - -.. image:: /_static/ggplot.png - :height: 195px - -.. _toolkit_holoviews: - -holoviews -========= - -`holoviews `_ makes it easier to visualize data -interactively, especially in a `Jupyter notebook -`_, by providing a set of declarative -plotting objects that store your data and associated metadata. Your -data is then immediately visualizable alongside or overlaid with other -data, either statically or with automatically provided widgets for -parameter exploration. - -.. image:: /_static/holoviews.png - :height: 354px - - -.. _toolkits-mapping: - -Mapping Toolkits -**************** - -Two independent mapping toolkits are available. - -.. _toolkit_basemap: - -Basemap -======= - -Plots data on map projections, with continental and political -boundaries. See `basemap `_ -docs. - -.. image:: /_static/basemap_contour1.png - :height: 400px - - - -Cartopy -======= -`Cartopy `_ builds on top of -matplotlib to provide object oriented map projection definitions and close -integration with Shapely for powerful yet easy-to-use vector data processing -tools. An example plot from the -`Cartopy gallery `_: - -.. image:: /_static/cartopy_hurricane_katrina_01_00.png - :height: 400px - - -.. _toolkits-misc: -.. _toolkits-general: - -Miscellaneous Toolkits -********************** - -.. _toolkit_probscale: - -mpl-probscale -============= -`mpl-probscale `_ -is a small extension that allows matplotlib users to specify probabilty -scales. Simply importing the ``probscale`` module registers the scale -with matplotlib, making it accessible via e.g., -``ax.set_xscale('prob')`` or ``plt.yscale('prob')``. - -.. image:: /_static/probscale_demo.png - -iTerm2 terminal backend -======================= - -`matplotlib_iterm2 `_ is an -external matplotlib backend using iTerm2 nightly build inline image display -feature. - -.. image:: /_static/matplotlib_iterm2_demo.png - - -.. _toolkit_mpldatacursor: - -MplDataCursor -============= - -`MplDataCursor `_ is a -toolkit written by Joe Kington to provide interactive "data cursors" -(clickable annotation boxes) for matplotlib. - - -.. _toolkit_mplcursors: - -mplcursors -========== - -`mplcursors `_ provides interactive -data cursors for matplotlib. - - -.. _toolkit_natgrid: - -Natgrid -======= - -mpl_toolkits.natgrid is an interface to natgrid C library for gridding -irregularly spaced data. This requires a separate installation of the -`natgrid toolkit `__. - - -.. _toolkit_matplotlibvenn: - -Matplotlib-Venn -=============== - -`Matplotlib-Venn `_ provides a set of functions for plotting 2- and 3-set area-weighted (or unweighted) Venn diagrams. - -mplstereonet -=============== - -`mplstereonet `_ provides stereonets for plotting and analyzing orientation data in Matplotlib. - -pyupset -=============== -`pyUpSet `_ is a static Python implementation of the `UpSet suite by Lex et al. `_ to explore complex intersections of sets and data frames. - -Windrose -=============== -`Windrose `_ is a Python Matplotlib, Numpy library to manage wind data, draw windrose (also known as a polar rose plot), draw probability density function and fit Weibull distribution + diff --git a/doc/users/_rcparams_generated.rst b/doc/users/_rcparams_generated.rst new file mode 100644 index 000000000000..19f972771ea9 --- /dev/null +++ b/doc/users/_rcparams_generated.rst @@ -0,0 +1,1674 @@ +.. + autogenerated rcParams documentation. Update via + > python -c "from matplotlib import rcsetup; rcsetup._write_rcParam_rst()" + + +.. _rcparam_webagg_port: + +webagg.port: ``8988`` + The port to use for the web server in the WebAgg backend. + +.. _rcparam_webagg_address: + +webagg.address: ``'127.0.0.1'`` + The address on which the WebAgg web server should be reachable. + +.. _rcparam_webagg_port_retries: + +webagg.port_retries: ``50`` + If webagg.port is unavailable, a number of other random ports will be tried until one that is available is found. + +.. _rcparam_webagg_open_in_browser: + +webagg.open_in_browser: ``True`` + When True, open the web browser to the plot that is shown + +.. _rcparam_backend_fallback: + +backend_fallback: ``True`` + If you are running pyplot inside a GUI and your backend choice conflicts, we will automatically try to find a compatible one for you if backend_fallback is True + +.. _rcparam_interactive: + +interactive: ``False`` + *no description* + +.. _rcparam_figure_hooks: + +figure.hooks: ``[]`` + list of dotted.module.name:dotted.callable.name + +.. _rcparam_toolbar: + +toolbar: ``'toolbar2'`` + {None, toolbar2, toolmanager} + +.. _rcparam_timezone: + +timezone: ``'UTC'`` + a pytz timezone string, e.g., US/Central or Europe/Paris + +.. _rcparam_lines_linewidth: + +lines.linewidth: ``1.5`` + line width in points + +.. _rcparam_lines_linestyle: + +lines.linestyle: ``'-'`` + solid line + +.. _rcparam_lines_color: + +lines.color: ``'C0'`` + has no affect on plot(); see axes.prop_cycle + +.. _rcparam_lines_marker: + +lines.marker: ``'None'`` + the default marker + +.. _rcparam_lines_markerfacecolor: + +lines.markerfacecolor: ``'auto'`` + the default marker face color + +.. _rcparam_lines_markeredgecolor: + +lines.markeredgecolor: ``'auto'`` + the default marker edge color + +.. _rcparam_lines_markeredgewidth: + +lines.markeredgewidth: ``1.0`` + the line width around the marker symbol + +.. _rcparam_lines_markersize: + +lines.markersize: ``6.0`` + marker size, in points + +.. _rcparam_lines_dash_joinstyle: + +lines.dash_joinstyle: ``'round'`` + {miter, round, bevel} + +.. _rcparam_lines_dash_capstyle: + +lines.dash_capstyle: ``'butt'`` + {butt, round, projecting} + +.. _rcparam_lines_solid_joinstyle: + +lines.solid_joinstyle: ``'round'`` + {miter, round, bevel} + +.. _rcparam_lines_solid_capstyle: + +lines.solid_capstyle: ``'projecting'`` + {butt, round, projecting} + +.. _rcparam_lines_antialiased: + +lines.antialiased: ``True`` + render lines in antialiased (no jaggies) + +.. _rcparam_lines_dashed_pattern: + +lines.dashed_pattern: ``[3.7, 1.6]`` + The dash pattern for linestyle 'dashed' + +.. _rcparam_lines_dashdot_pattern: + +lines.dashdot_pattern: ``[6.4, 1.6, 1.0, 1.6]`` + The dash pattern for linestyle 'dashdot' + +.. _rcparam_lines_dotted_pattern: + +lines.dotted_pattern: ``[1.0, 1.65]`` + The dash pattern for linestyle 'dotted' + +.. _rcparam_lines_scale_dashes: + +lines.scale_dashes: ``True`` + *no description* + +.. _rcparam_markers_fillstyle: + +markers.fillstyle: ``'full'`` + {full, left, right, bottom, top, none} + +.. _rcparam_pcolor_shading: + +pcolor.shading: ``'auto'`` + *no description* + +.. _rcparam_pcolormesh_snap: + +pcolormesh.snap: ``True`` + Whether to snap the mesh to pixel boundaries. This is provided solely to allow old test images to remain unchanged. Set to False to obtain the previous behavior. + +.. _rcparam_patch_linewidth: + +patch.linewidth: ``1.0`` + edge width in points. + +.. _rcparam_patch_facecolor: + +patch.facecolor: ``'C0'`` + *no description* + +.. _rcparam_patch_edgecolor: + +patch.edgecolor: ``'black'`` + By default, Patches and Collections do not draw edges. This value is only used if facecolor is "none" (an Artist without facecolor and edgecolor would be invisible) or if patch.force_edgecolor is True. + +.. _rcparam_patch_force_edgecolor: + +patch.force_edgecolor: ``False`` + By default, Patches and Collections do not draw edges. Set this to True to draw edges with patch.edgedcolor as the default edgecolor. This is mainly relevant for styles. + +.. _rcparam_patch_antialiased: + +patch.antialiased: ``True`` + render patches in antialiased (no jaggies) + +.. _rcparam_hatch_color: + +hatch.color: ``'edge'`` + *no description* + +.. _rcparam_hatch_linewidth: + +hatch.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_notch: + +boxplot.notch: ``False`` + *no description* + +.. _rcparam_boxplot_vertical: + +boxplot.vertical: ``True`` + *no description* + +.. _rcparam_boxplot_whiskers: + +boxplot.whiskers: ``1.5`` + *no description* + +.. _rcparam_boxplot_bootstrap: + +boxplot.bootstrap: ``None`` + *no description* + +.. _rcparam_boxplot_patchartist: + +boxplot.patchartist: ``False`` + *no description* + +.. _rcparam_boxplot_showmeans: + +boxplot.showmeans: ``False`` + *no description* + +.. _rcparam_boxplot_showcaps: + +boxplot.showcaps: ``True`` + *no description* + +.. _rcparam_boxplot_showbox: + +boxplot.showbox: ``True`` + *no description* + +.. _rcparam_boxplot_showfliers: + +boxplot.showfliers: ``True`` + *no description* + +.. _rcparam_boxplot_meanline: + +boxplot.meanline: ``False`` + *no description* + +.. _rcparam_boxplot_flierprops_color: + +boxplot.flierprops.color: ``'black'`` + *no description* + +.. _rcparam_boxplot_flierprops_marker: + +boxplot.flierprops.marker: ``'o'`` + *no description* + +.. _rcparam_boxplot_flierprops_markerfacecolor: + +boxplot.flierprops.markerfacecolor: ``'none'`` + *no description* + +.. _rcparam_boxplot_flierprops_markeredgecolor: + +boxplot.flierprops.markeredgecolor: ``'black'`` + *no description* + +.. _rcparam_boxplot_flierprops_markeredgewidth: + +boxplot.flierprops.markeredgewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_flierprops_markersize: + +boxplot.flierprops.markersize: ``6.0`` + *no description* + +.. _rcparam_boxplot_flierprops_linestyle: + +boxplot.flierprops.linestyle: ``'none'`` + *no description* + +.. _rcparam_boxplot_flierprops_linewidth: + +boxplot.flierprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_boxprops_color: + +boxplot.boxprops.color: ``'black'`` + *no description* + +.. _rcparam_boxplot_boxprops_linewidth: + +boxplot.boxprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_boxprops_linestyle: + +boxplot.boxprops.linestyle: ``'-'`` + *no description* + +.. _rcparam_boxplot_whiskerprops_color: + +boxplot.whiskerprops.color: ``'black'`` + *no description* + +.. _rcparam_boxplot_whiskerprops_linewidth: + +boxplot.whiskerprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_whiskerprops_linestyle: + +boxplot.whiskerprops.linestyle: ``'-'`` + *no description* + +.. _rcparam_boxplot_capprops_color: + +boxplot.capprops.color: ``'black'`` + *no description* + +.. _rcparam_boxplot_capprops_linewidth: + +boxplot.capprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_capprops_linestyle: + +boxplot.capprops.linestyle: ``'-'`` + *no description* + +.. _rcparam_boxplot_medianprops_color: + +boxplot.medianprops.color: ``'C1'`` + *no description* + +.. _rcparam_boxplot_medianprops_linewidth: + +boxplot.medianprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_boxplot_medianprops_linestyle: + +boxplot.medianprops.linestyle: ``'-'`` + *no description* + +.. _rcparam_boxplot_meanprops_color: + +boxplot.meanprops.color: ``'C2'`` + *no description* + +.. _rcparam_boxplot_meanprops_marker: + +boxplot.meanprops.marker: ``'^'`` + *no description* + +.. _rcparam_boxplot_meanprops_markerfacecolor: + +boxplot.meanprops.markerfacecolor: ``'C2'`` + *no description* + +.. _rcparam_boxplot_meanprops_markeredgecolor: + +boxplot.meanprops.markeredgecolor: ``'C2'`` + *no description* + +.. _rcparam_boxplot_meanprops_markersize: + +boxplot.meanprops.markersize: ``6.0`` + *no description* + +.. _rcparam_boxplot_meanprops_linestyle: + +boxplot.meanprops.linestyle: ``'--'`` + *no description* + +.. _rcparam_boxplot_meanprops_linewidth: + +boxplot.meanprops.linewidth: ``1.0`` + *no description* + +.. _rcparam_font_family: + +font.family: ``['sans-serif']`` + *no description* + +.. _rcparam_font_style: + +font.style: ``'normal'`` + *no description* + +.. _rcparam_font_variant: + +font.variant: ``'normal'`` + *no description* + +.. _rcparam_font_weight: + +font.weight: ``'normal'`` + *no description* + +.. _rcparam_font_stretch: + +font.stretch: ``'normal'`` + *no description* + +.. _rcparam_font_size: + +font.size: ``10.0`` + *no description* + +.. _rcparam_font_serif: + +font.serif: ``['DejaVu Serif', 'Bitstream Vera Serif', 'Computer Modern Roman', 'New Century Schoolbook', 'Century Schoolbook L', 'Utopia', 'ITC Bookman', 'Bookman', 'Nimbus Roman No9 L', 'Times New Roman', 'Times', 'Palatino', 'Charter', 'serif']`` + *no description* + +.. _rcparam_font_sans-serif: + +font.sans-serif: ``['DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'sans-serif']`` + *no description* + +.. _rcparam_font_cursive: + +font.cursive: ``['Apple Chancery', 'Textile', 'Zapf Chancery', 'Sand', 'Script MT', 'Felipa', 'Comic Neue', 'Comic Sans MS', 'cursive']`` + *no description* + +.. _rcparam_font_fantasy: + +font.fantasy: ``['Chicago', 'Charcoal', 'Impact', 'Western', 'xkcd script', 'fantasy']`` + *no description* + +.. _rcparam_font_monospace: + +font.monospace: ``['DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Computer Modern Typewriter', 'Andale Mono', 'Nimbus Mono L', 'Courier New', 'Courier', 'Fixed', 'Terminal', 'monospace']`` + *no description* + +.. _rcparam_font_enable_last_resort: + +font.enable_last_resort: ``True`` + If True, then Unicode Consortium's Last Resort font will be appended to all font selections. This ensures that there will always be a glyph displayed. + +.. _rcparam_text_color: + +text.color: ``'black'`` + *no description* + +.. _rcparam_text_hinting: + +text.hinting: ``'force_autohint'`` + FreeType hinting flag ("foo" corresponds to FT_LOAD_FOO); may be one of the following (Proprietary Matplotlib-specific synonyms are given in parentheses, but their use is discouraged): - default: Use the font's native hinter if possible, else FreeType's auto-hinter. ("either" is a synonym).- no_autohint: Use the font's native hinter if possible, else don't hint. ("native" is a synonym.)- force_autohint: Use FreeType's auto-hinter. ("auto" is a synonym.)- no_hinting: Disable hinting. ("none" is a synonym.) + +.. _rcparam_text_hinting_factor: + +text.hinting_factor: ``8`` + Specifies the amount of softness for hinting in the horizontal direction. A value of 1 will hint to full pixels. A value of 2 will hint to half pixels etc. + +.. _rcparam_text_kerning_factor: + +text.kerning_factor: ``0`` + Specifies the scaling factor for kerning values. This is provided solely to allow old test images to remain unchanged. Set to 6 to obtain previous behavior. Values other than 0 or 6 have no defined meaning. + +.. _rcparam_text_antialiased: + +text.antialiased: ``True`` + If True (default), the text will be antialiased. This only affects raster outputs. + +.. _rcparam_text_parse_math: + +text.parse_math: ``True`` + Use mathtext if there is an even number of unescaped dollar signs. + +.. _rcparam_text_usetex: + +text.usetex: ``False`` + use latex for all text handling. The following fonts are supported through the usual rc parameter settings: new century schoolbook, bookman, times, palatino, zapf chancery, charter, serif, sans-serif, helvetica, avant garde, courier, monospace, computer modern roman, computer modern sans serif, computer modern typewriter + +.. _rcparam_text_latex_preamble: + +text.latex.preamble: ``''`` + IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO. text.latex.preamble is a single line of LaTeX code that will be passed on to the LaTeX system. It may contain any code that is valid for the LaTeX "preamble", i.e. between the "\documentclass" and "\begin{document}" statements. Note that it has to be put on a single line, which may become quite long. The following packages are always loaded with usetex, so beware of package collisions: color, fix-cm, geometry, graphicx, textcomp. PostScript (PSNFSS) font packages may also be loaded, depending on your font settings. + +.. _rcparam_mathtext_fontset: + +mathtext.fontset: ``'dejavusans'`` + Should be 'dejavusans' (default), 'dejavuserif', 'cm' (Computer Modern), 'stix', 'stixsans' or 'custom' + +.. _rcparam_mathtext_bf: + +mathtext.bf: ``'sans:bold'`` + *no description* + +.. _rcparam_mathtext_bfit: + +mathtext.bfit: ``'sans:italic:bold'`` + *no description* + +.. _rcparam_mathtext_cal: + +mathtext.cal: ``'cursive'`` + *no description* + +.. _rcparam_mathtext_it: + +mathtext.it: ``'sans:italic'`` + *no description* + +.. _rcparam_mathtext_rm: + +mathtext.rm: ``'sans'`` + *no description* + +.. _rcparam_mathtext_sf: + +mathtext.sf: ``'sans'`` + *no description* + +.. _rcparam_mathtext_tt: + +mathtext.tt: ``'monospace'`` + *no description* + +.. _rcparam_mathtext_fallback: + +mathtext.fallback: ``'cm'`` + Select fallback font from ['cm' (Computer Modern), 'stix', 'stixsans'] when a symbol cannot be found in one of the custom math fonts. Select 'None' to not perform fallback and replace the missing character by a dummy symbol. + +.. _rcparam_mathtext_default: + +mathtext.default: ``'it'`` + The default font to use for math. Can be any of the LaTeX font names, including the special name "regular" for the same font used in regular text. + +.. _rcparam_axes_facecolor: + +axes.facecolor: ``'white'`` + axes background color + +.. _rcparam_axes_edgecolor: + +axes.edgecolor: ``'black'`` + axes edge color + +.. _rcparam_axes_linewidth: + +axes.linewidth: ``0.8`` + edge line width + +.. _rcparam_axes_grid: + +axes.grid: ``False`` + display grid or not + +.. _rcparam_axes_grid_axis: + +axes.grid.axis: ``'both'`` + which axis the grid should apply to + +.. _rcparam_axes_grid_which: + +axes.grid.which: ``'major'`` + grid lines at {major, minor, both} ticks + +.. _rcparam_axes_titlelocation: + +axes.titlelocation: ``'center'`` + alignment of the title: {left, right, center} + +.. _rcparam_axes_titlesize: + +axes.titlesize: ``'large'`` + font size of the axes title + +.. _rcparam_axes_titleweight: + +axes.titleweight: ``'normal'`` + font weight of title + +.. _rcparam_axes_titlecolor: + +axes.titlecolor: ``'auto'`` + color of the axes title, auto falls back to text.color as default value + +.. _rcparam_axes_titley: + +axes.titley: ``None`` + position title (axes relative units). None implies auto + +.. _rcparam_axes_titlepad: + +axes.titlepad: ``6.0`` + pad between axes and title in points + +.. _rcparam_axes_labelsize: + +axes.labelsize: ``'medium'`` + font size of the x and y labels + +.. _rcparam_axes_labelpad: + +axes.labelpad: ``4.0`` + space between label and axis + +.. _rcparam_axes_labelweight: + +axes.labelweight: ``'normal'`` + weight of the x and y labels + +.. _rcparam_axes_labelcolor: + +axes.labelcolor: ``'black'`` + *no description* + +.. _rcparam_axes_axisbelow: + +axes.axisbelow: ``'line'`` + draw axis gridlines and ticks: - below patches (True) - above patches but below lines ('line') - above all (False) + +.. _rcparam_axes_formatter_limits: + +axes.formatter.limits: ``[-5, 6]`` + use scientific notation if log10 of the axis range is smaller than the first or larger than the second + +.. _rcparam_axes_formatter_use_locale: + +axes.formatter.use_locale: ``False`` + When True, format tick labels according to the user's locale. For example, use ',' as a decimal separator in the fr_FR locale. + +.. _rcparam_axes_formatter_use_mathtext: + +axes.formatter.use_mathtext: ``False`` + When True, use mathtext for scientific notation. + +.. _rcparam_axes_formatter_min_exponent: + +axes.formatter.min_exponent: ``0`` + minimum exponent to format in scientific notation + +.. _rcparam_axes_formatter_useoffset: + +axes.formatter.useoffset: ``True`` + If True, the tick label formatter will default to labeling ticks relative to an offset when the data range is small compared to the minimum absolute value of the data. + +.. _rcparam_axes_formatter_offset_threshold: + +axes.formatter.offset_threshold: ``4`` + When useoffset is True, the offset will be used when it can remove at least this number of significant digits from tick labels. + +.. _rcparam_axes_spines_left: + +axes.spines.left: ``True`` + display axis spines + +.. _rcparam_axes_spines_bottom: + +axes.spines.bottom: ``True`` + *no description* + +.. _rcparam_axes_spines_top: + +axes.spines.top: ``True`` + *no description* + +.. _rcparam_axes_spines_right: + +axes.spines.right: ``True`` + *no description* + +.. _rcparam_axes_unicode_minus: + +axes.unicode_minus: ``True`` + use Unicode for the minus symbol rather than hyphen. See https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes + +.. _rcparam_axes_prop_cycle: + +axes.prop_cycle: ``cycler('color', [(0.12156862745098039, 0.4666666666666667, 0.7058823529411765), (1.0, 0.4980392156862745, 0.054901960784313725), (0.17254901960784313, 0.6274509803921569, 0.17254901960784313), (0.8392156862745098, 0.15294117647058825, 0.1568627450980392), (0.5803921568627451, 0.403921568627451, 0.7411764705882353), (0.5490196078431373, 0.33725490196078434, 0.29411764705882354), (0.8901960784313725, 0.4666666666666667, 0.7607843137254902), (0.4980392156862745, 0.4980392156862745, 0.4980392156862745), (0.7372549019607844, 0.7411764705882353, 0.13333333333333333), (0.09019607843137255, 0.7450980392156863, 0.8117647058823529)])`` + *no description* + +.. _rcparam_axes_xmargin: + +axes.xmargin: ``0.05`` + x margin. See `~.axes.Axes.margins` + +.. _rcparam_axes_ymargin: + +axes.ymargin: ``0.05`` + y margin. See `~.axes.Axes.margins` + +.. _rcparam_axes_zmargin: + +axes.zmargin: ``0.05`` + z margin. See `~.axes.Axes.margins` + +.. _rcparam_axes_autolimit_mode: + +axes.autolimit_mode: ``'data'`` + If "data", use axes.xmargin and axes.ymargin as is. If "round_numbers", after application of margins, axis limits are further expanded to the nearest "round" number. + +.. _rcparam_polaraxes_grid: + +polaraxes.grid: ``True`` + display grid on polar axes + +.. _rcparam_axes3d_grid: + +axes3d.grid: ``True`` + display grid on 3D axes + +.. _rcparam_axes3d_automargin: + +axes3d.automargin: ``False`` + automatically add margin when manually setting 3D axis limits + +.. _rcparam_axes3d_xaxis_panecolor: + +axes3d.xaxis.panecolor: ``(0.95, 0.95, 0.95, 0.5)`` + background pane on 3D axes + +.. _rcparam_axes3d_yaxis_panecolor: + +axes3d.yaxis.panecolor: ``(0.9, 0.9, 0.9, 0.5)`` + background pane on 3D axes + +.. _rcparam_axes3d_zaxis_panecolor: + +axes3d.zaxis.panecolor: ``(0.925, 0.925, 0.925, 0.5)`` + background pane on 3D axes + +.. _rcparam_axes3d_depthshade: + +axes3d.depthshade: ``True`` + depth shade for 3D scatter plots + +.. _rcparam_axes3d_depthshade_minalpha: + +axes3d.depthshade_minalpha: ``0.3`` + minimum alpha value for depth shading + +.. _rcparam_axes3d_mouserotationstyle: + +axes3d.mouserotationstyle: ``'arcball'`` + {azel, trackball, sphere, arcball} See also https://matplotlib.org/stable/api/toolkits/mplot3d/view_angles.html#rotation-with-mouse + +.. _rcparam_axes3d_trackballsize: + +axes3d.trackballsize: ``0.667`` + trackball diameter, in units of the Axes bbox + +.. _rcparam_axes3d_trackballborder: + +axes3d.trackballborder: ``0.2`` + trackball border width, in units of the Axes bbox (only for 'sphere' and 'arcball' style) + +.. _rcparam_xaxis_labellocation: + +xaxis.labellocation: ``'center'`` + alignment of the xaxis label: {left, right, center} + +.. _rcparam_yaxis_labellocation: + +yaxis.labellocation: ``'center'`` + alignment of the yaxis label: {bottom, top, center} + +.. _rcparam_date_autoformatter_year: + +date.autoformatter.year: ``'%Y'`` + *no description* + +.. _rcparam_date_autoformatter_month: + +date.autoformatter.month: ``'%Y-%m'`` + *no description* + +.. _rcparam_date_autoformatter_day: + +date.autoformatter.day: ``'%Y-%m-%d'`` + *no description* + +.. _rcparam_date_autoformatter_hour: + +date.autoformatter.hour: ``'%m-%d %H'`` + *no description* + +.. _rcparam_date_autoformatter_minute: + +date.autoformatter.minute: ``'%d %H:%M'`` + *no description* + +.. _rcparam_date_autoformatter_second: + +date.autoformatter.second: ``'%H:%M:%S'`` + *no description* + +.. _rcparam_date_autoformatter_microsecond: + +date.autoformatter.microsecond: ``'%M:%S.%f'`` + *no description* + +.. _rcparam_date_epoch: + +date.epoch: ``'1970-01-01T00:00:00'`` + The reference date for Matplotlib's internal date representation. See https://matplotlib.org/stable/gallery/ticks/date_precision_and_epochs.html + +.. _rcparam_date_converter: + +date.converter: ``'auto'`` + 'auto', 'concise' + +.. _rcparam_date_interval_multiples: + +date.interval_multiples: ``True`` + For auto converter whether to use interval_multiples + +.. _rcparam_xtick_top: + +xtick.top: ``False`` + draw ticks on the top side + +.. _rcparam_xtick_bottom: + +xtick.bottom: ``True`` + draw ticks on the bottom side + +.. _rcparam_xtick_labeltop: + +xtick.labeltop: ``False`` + draw label on the top + +.. _rcparam_xtick_labelbottom: + +xtick.labelbottom: ``True`` + draw label on the bottom + +.. _rcparam_xtick_major_size: + +xtick.major.size: ``3.5`` + major tick size in points + +.. _rcparam_xtick_minor_size: + +xtick.minor.size: ``2.0`` + minor tick size in points + +.. _rcparam_xtick_major_width: + +xtick.major.width: ``0.8`` + major tick width in points + +.. _rcparam_xtick_minor_width: + +xtick.minor.width: ``0.6`` + minor tick width in points + +.. _rcparam_xtick_major_pad: + +xtick.major.pad: ``3.5`` + distance to major tick label in points + +.. _rcparam_xtick_minor_pad: + +xtick.minor.pad: ``3.4`` + distance to the minor tick label in points + +.. _rcparam_xtick_color: + +xtick.color: ``'black'`` + color of the ticks + +.. _rcparam_xtick_labelcolor: + +xtick.labelcolor: ``'inherit'`` + color of the tick labels or inherit from xtick.color + +.. _rcparam_xtick_labelsize: + +xtick.labelsize: ``'medium'`` + font size of the tick labels + +.. _rcparam_xtick_direction: + +xtick.direction: ``'out'`` + direction: {in, out, inout} + +.. _rcparam_xtick_minor_visible: + +xtick.minor.visible: ``False`` + visibility of minor ticks on x-axis + +.. _rcparam_xtick_major_top: + +xtick.major.top: ``True`` + draw x axis top major ticks + +.. _rcparam_xtick_major_bottom: + +xtick.major.bottom: ``True`` + draw x axis bottom major ticks + +.. _rcparam_xtick_minor_top: + +xtick.minor.top: ``True`` + draw x axis top minor ticks + +.. _rcparam_xtick_minor_bottom: + +xtick.minor.bottom: ``True`` + draw x axis bottom minor ticks + +.. _rcparam_xtick_minor_ndivs: + +xtick.minor.ndivs: ``'auto'`` + number of minor ticks between the major ticks on x-axis + +.. _rcparam_xtick_alignment: + +xtick.alignment: ``'center'`` + alignment of xticks + +.. _rcparam_ytick_left: + +ytick.left: ``True`` + draw ticks on the left side + +.. _rcparam_ytick_right: + +ytick.right: ``False`` + draw ticks on the right side + +.. _rcparam_ytick_labelleft: + +ytick.labelleft: ``True`` + draw tick labels on the left side + +.. _rcparam_ytick_labelright: + +ytick.labelright: ``False`` + draw tick labels on the right side + +.. _rcparam_ytick_major_size: + +ytick.major.size: ``3.5`` + major tick size in points + +.. _rcparam_ytick_minor_size: + +ytick.minor.size: ``2.0`` + minor tick size in points + +.. _rcparam_ytick_major_width: + +ytick.major.width: ``0.8`` + major tick width in points + +.. _rcparam_ytick_minor_width: + +ytick.minor.width: ``0.6`` + minor tick width in points + +.. _rcparam_ytick_major_pad: + +ytick.major.pad: ``3.5`` + distance to major tick label in points + +.. _rcparam_ytick_minor_pad: + +ytick.minor.pad: ``3.4`` + distance to the minor tick label in points + +.. _rcparam_ytick_color: + +ytick.color: ``'black'`` + color of the ticks + +.. _rcparam_ytick_labelcolor: + +ytick.labelcolor: ``'inherit'`` + color of the tick labels or inherit from ytick.color + +.. _rcparam_ytick_labelsize: + +ytick.labelsize: ``'medium'`` + font size of the tick labels + +.. _rcparam_ytick_direction: + +ytick.direction: ``'out'`` + direction: {in, out, inout} + +.. _rcparam_ytick_minor_visible: + +ytick.minor.visible: ``False`` + visibility of minor ticks on y-axis + +.. _rcparam_ytick_major_left: + +ytick.major.left: ``True`` + draw y axis left major ticks + +.. _rcparam_ytick_major_right: + +ytick.major.right: ``True`` + draw y axis right major ticks + +.. _rcparam_ytick_minor_left: + +ytick.minor.left: ``True`` + draw y axis left minor ticks + +.. _rcparam_ytick_minor_right: + +ytick.minor.right: ``True`` + draw y axis right minor ticks + +.. _rcparam_ytick_minor_ndivs: + +ytick.minor.ndivs: ``'auto'`` + number of minor ticks between the major ticks on y-axis + +.. _rcparam_ytick_alignment: + +ytick.alignment: ``'center_baseline'`` + alignment of yticks + +.. _rcparam_grid_color: + +grid.color: ``'#b0b0b0'`` + b0b0b0" # grid color + +.. _rcparam_grid_linestyle: + +grid.linestyle: ``'-'`` + solid + +.. _rcparam_grid_linewidth: + +grid.linewidth: ``0.8`` + in points + +.. _rcparam_grid_alpha: + +grid.alpha: ``1.0`` + transparency, between 0.0 and 1.0 + +.. _rcparam_grid_major_color: + +grid.major.color: ``None`` + If None defaults to grid.color + +.. _rcparam_grid_major_linestyle: + +grid.major.linestyle: ``None`` + If None defaults to grid.linestyle + +.. _rcparam_grid_major_linewidth: + +grid.major.linewidth: ``None`` + If None defaults to grid.linewidth + +.. _rcparam_grid_major_alpha: + +grid.major.alpha: ``None`` + If None defaults to grid.alpha + +.. _rcparam_grid_minor_color: + +grid.minor.color: ``None`` + If None defaults to grid.color + +.. _rcparam_grid_minor_linestyle: + +grid.minor.linestyle: ``None`` + If None defaults to grid.linestyle + +.. _rcparam_grid_minor_linewidth: + +grid.minor.linewidth: ``None`` + If None defaults to grid.linewidth + +.. _rcparam_grid_minor_alpha: + +grid.minor.alpha: ``None`` + If None defaults to grid.alpha + +.. _rcparam_legend_loc: + +legend.loc: ``'best'`` + *no description* + +.. _rcparam_legend_frameon: + +legend.frameon: ``True`` + if True, draw the legend on a background patch + +.. _rcparam_legend_framealpha: + +legend.framealpha: ``0.8`` + legend patch transparency + +.. _rcparam_legend_facecolor: + +legend.facecolor: ``'inherit'`` + inherit from axes.facecolor; or color spec + +.. _rcparam_legend_edgecolor: + +legend.edgecolor: ``'0.8'`` + background patch boundary color + +.. _rcparam_legend_linewidth: + +legend.linewidth: ``None`` + line width of the legend frame, None means inherit from patch.linewidth + +.. _rcparam_legend_fancybox: + +legend.fancybox: ``True`` + if True, use a rounded box for the legend background, else a rectangle + +.. _rcparam_legend_shadow: + +legend.shadow: ``False`` + if True, give background a shadow effect + +.. _rcparam_legend_numpoints: + +legend.numpoints: ``1`` + the number of marker points in the legend line + +.. _rcparam_legend_scatterpoints: + +legend.scatterpoints: ``1`` + number of scatter points + +.. _rcparam_legend_markerscale: + +legend.markerscale: ``1.0`` + the relative size of legend markers vs. original + +.. _rcparam_legend_fontsize: + +legend.fontsize: ``'medium'`` + *no description* + +.. _rcparam_legend_labelcolor: + +legend.labelcolor: ``'None'`` + *no description* + +.. _rcparam_legend_title_fontsize: + +legend.title_fontsize: ``None`` + None sets to the same as the default axes. + +.. _rcparam_legend_borderpad: + +legend.borderpad: ``0.4`` + border whitespace + +.. _rcparam_legend_labelspacing: + +legend.labelspacing: ``0.5`` + the vertical space between the legend entries + +.. _rcparam_legend_handlelength: + +legend.handlelength: ``2.0`` + the length of the legend lines + +.. _rcparam_legend_handleheight: + +legend.handleheight: ``0.7`` + the height of the legend handle + +.. _rcparam_legend_handletextpad: + +legend.handletextpad: ``0.8`` + the space between the legend line and legend text + +.. _rcparam_legend_borderaxespad: + +legend.borderaxespad: ``0.5`` + the border between the axes and legend edge + +.. _rcparam_legend_columnspacing: + +legend.columnspacing: ``2.0`` + column separation + +.. _rcparam_figure_titlesize: + +figure.titlesize: ``'large'`` + size of the figure title (``Figure.suptitle()``) + +.. _rcparam_figure_titleweight: + +figure.titleweight: ``'normal'`` + weight of the figure title + +.. _rcparam_figure_labelsize: + +figure.labelsize: ``'large'`` + size of the figure label (``Figure.sup[x|y]label()``) + +.. _rcparam_figure_labelweight: + +figure.labelweight: ``'normal'`` + weight of the figure label + +.. _rcparam_figure_figsize: + +figure.figsize: ``[6.4, 4.8]`` + figure size in inches + +.. _rcparam_figure_dpi: + +figure.dpi: ``100.0`` + figure dots per inch + +.. _rcparam_figure_facecolor: + +figure.facecolor: ``'white'`` + figure face color + +.. _rcparam_figure_edgecolor: + +figure.edgecolor: ``'white'`` + figure edge color + +.. _rcparam_figure_frameon: + +figure.frameon: ``True`` + enable figure frame + +.. _rcparam_figure_max_open_warning: + +figure.max_open_warning: ``20`` + The maximum number of figures to open through the pyplot interface before emitting a warning. If less than one this feature is disabled. + +.. _rcparam_figure_raise_window: + +figure.raise_window: ``True`` + Raise the GUI window to front when show() is called. If set to False, we currently do not take any further actions and whether the window appears on the front may depend on the GUI framework and window manager. + +.. _rcparam_figure_subplot_left: + +figure.subplot.left: ``0.125`` + the left side of the subplots of the figure + +.. _rcparam_figure_subplot_right: + +figure.subplot.right: ``0.9`` + the right side of the subplots of the figure + +.. _rcparam_figure_subplot_bottom: + +figure.subplot.bottom: ``0.11`` + the bottom of the subplots of the figure + +.. _rcparam_figure_subplot_top: + +figure.subplot.top: ``0.88`` + the top of the subplots of the figure + +.. _rcparam_figure_subplot_wspace: + +figure.subplot.wspace: ``0.2`` + the amount of width reserved for space between subplots, expressed as a fraction of the average axis width + +.. _rcparam_figure_subplot_hspace: + +figure.subplot.hspace: ``0.2`` + the amount of height reserved for space between subplots, expressed as a fraction of the average axis height + +.. _rcparam_figure_autolayout: + +figure.autolayout: ``False`` + When True, automatically adjust subplot parameters to make the plot fit the figure using `~.Figure.tight_layout` + +.. _rcparam_figure_constrained_layout_use: + +figure.constrained_layout.use: ``False`` + When True, automatically make plot elements fit on the figure. (Not compatible with "figure.autolayout", above). + +.. _rcparam_figure_constrained_layout_h_pad: + +figure.constrained_layout.h_pad: ``0.04167`` + Padding (in inches) around axes; defaults to 3/72 inches, i.e. 3 points + +.. _rcparam_figure_constrained_layout_w_pad: + +figure.constrained_layout.w_pad: ``0.04167`` + Padding (in inches) around axes; defaults to 3/72 inches, i.e. 3 points + +.. _rcparam_figure_constrained_layout_hspace: + +figure.constrained_layout.hspace: ``0.02`` + Spacing between subplots, relative to the subplot sizes. Much smaller than for tight_layout (figure.subplot.hspace, figure.subplot.wspace) as constrained_layout already takes surrounding texts (titles, labels, # ticklabels) into account. + +.. _rcparam_figure_constrained_layout_wspace: + +figure.constrained_layout.wspace: ``0.02`` + Spacing between subplots, relative to the subplot sizes. Much smaller than for tight_layout (figure.subplot.hspace, figure.subplot.wspace) as constrained_layout already takes surrounding texts (titles, labels, # ticklabels) into account. + +.. _rcparam_image_aspect: + +image.aspect: ``'equal'`` + {equal, auto} or a number + +.. _rcparam_image_interpolation: + +image.interpolation: ``'auto'`` + see help(imshow) for options + +.. _rcparam_image_interpolation_stage: + +image.interpolation_stage: ``'auto'`` + see help(imshow) for options + +.. _rcparam_image_cmap: + +image.cmap: ``'viridis'`` + A colormap name (plasma, magma, etc.) + +.. _rcparam_image_lut: + +image.lut: ``256`` + the size of the colormap lookup table + +.. _rcparam_image_origin: + +image.origin: ``'upper'`` + {lower, upper} + +.. _rcparam_image_resample: + +image.resample: ``True`` + *no description* + +.. _rcparam_image_composite_image: + +image.composite_image: ``True`` + When True, all the images on a set of axes are combined into a single composite image before saving a figure as a vector graphics file, such as a PDF. + +.. _rcparam_contour_negative_linestyle: + +contour.negative_linestyle: ``'dashed'`` + string or on-off ink sequence + +.. _rcparam_contour_corner_mask: + +contour.corner_mask: ``True`` + {True, False} + +.. _rcparam_contour_linewidth: + +contour.linewidth: ``None`` + {float, None} Size of the contour line widths. If set to None, it falls back to "line.linewidth". + +.. _rcparam_contour_algorithm: + +contour.algorithm: ``'mpl2014'`` + {mpl2005, mpl2014, serial, threaded} + +.. _rcparam_errorbar_capsize: + +errorbar.capsize: ``0.0`` + length of end cap on error bars in pixels + +.. _rcparam_hist_bins: + +hist.bins: ``10`` + The default number of histogram bins or 'auto'. + +.. _rcparam_scatter_marker: + +scatter.marker: ``'o'`` + The default marker type for scatter plots. + +.. _rcparam_scatter_edgecolors: + +scatter.edgecolors: ``'face'`` + The default edge colors for scatter plots. + +.. _rcparam_agg_path_chunksize: + +agg.path.chunksize: ``0`` + 0 to disable; values in the range 10000 to 100000 can improve speed slightly and prevent an Agg rendering failure when plotting very large data sets, especially if they are very gappy. It may cause minor artifacts, though. A value of 20000 is probably a good starting point. + +.. _rcparam_path_simplify: + +path.simplify: ``True`` + When True, simplify paths by removing "invisible" points to reduce file size and increase rendering speed + +.. _rcparam_path_simplify_threshold: + +path.simplify_threshold: ``0.111111111111`` + The threshold of similarity below which vertices will be removed in the simplification process. + +.. _rcparam_path_snap: + +path.snap: ``True`` + When True, rectilinear axis-aligned paths will be snapped to the nearest pixel when certain criteria are met. When False, paths will never be snapped. + +.. _rcparam_path_sketch: + +path.sketch: ``None`` + May be None, or a tuple of the form:path.sketch: (scale, length, randomness)- *scale* is the amplitude of the wiggle perpendicular to the line (in pixels).- *length* is the length of the wiggle along the line (in pixels).- *randomness* is the factor by which the length is randomly scaled. + +.. _rcparam_path_effects: + +path.effects: ``[]`` + *no description* + +.. _rcparam_savefig_dpi: + +savefig.dpi: ``'figure'`` + figure dots per inch or 'figure' + +.. _rcparam_savefig_facecolor: + +savefig.facecolor: ``'auto'`` + figure face color when saving + +.. _rcparam_savefig_edgecolor: + +savefig.edgecolor: ``'auto'`` + figure edge color when saving + +.. _rcparam_savefig_format: + +savefig.format: ``'png'`` + {png, ps, pdf, svg} + +.. _rcparam_savefig_bbox: + +savefig.bbox: ``None`` + {tight, standard} 'tight' is incompatible with generating frames for animation + +.. _rcparam_savefig_pad_inches: + +savefig.pad_inches: ``0.1`` + padding to be used, when bbox is set to 'tight' + +.. _rcparam_savefig_directory: + +savefig.directory: ``'~'`` + default directory in savefig dialog, gets updated after interactive saves, unless set to the empty string (i.e. the current directory); use '.' to start at the current directory but update after interactive saves + +.. _rcparam_savefig_transparent: + +savefig.transparent: ``False`` + whether figures are saved with a transparent background by default + +.. _rcparam_savefig_orientation: + +savefig.orientation: ``'portrait'`` + orientation of saved figure, for PostScript output only + +.. _rcparam_macosx_window_mode: + +macosx.window_mode: ``'system'`` + How to open new figures (system, tab, window) system uses the MacOS system preferences + +.. _rcparam_tk_window_focus: + +tk.window_focus: ``False`` + Maintain shell focus for TkAgg + +.. _rcparam_ps_papersize: + +ps.papersize: ``'letter'`` + {figure, letter, legal, ledger, A0-A10, B0-B10} + +.. _rcparam_ps_useafm: + +ps.useafm: ``False`` + use AFM fonts, results in small files + +.. _rcparam_ps_usedistiller: + +ps.usedistiller: ``None`` + {ghostscript, xpdf, None} Experimental: may produce smaller files. xpdf intended for production of publication quality files, but requires ghostscript, xpdf and ps2eps + +.. _rcparam_ps_distiller_res: + +ps.distiller.res: ``6000`` + dpi + +.. _rcparam_ps_fonttype: + +ps.fonttype: ``3`` + Output Type 3 (Type3) or Type 42 (TrueType) + +.. _rcparam_pdf_compression: + +pdf.compression: ``6`` + integer from 0 to 9 0 disables compression (good for debugging) + +.. _rcparam_pdf_fonttype: + +pdf.fonttype: ``3`` + Output Type 3 (Type3) or Type 42 (TrueType) + +.. _rcparam_pdf_use14corefonts: + +pdf.use14corefonts: ``False`` + *no description* + +.. _rcparam_pdf_inheritcolor: + +pdf.inheritcolor: ``False`` + *no description* + +.. _rcparam_svg_image_inline: + +svg.image_inline: ``True`` + Write raster image data directly into the SVG file + +.. _rcparam_svg_fonttype: + +svg.fonttype: ``'path'`` + How to handle SVG fonts: path: Embed characters as paths -- supported by most SVG renderersnone: Assume fonts are installed on the machine where the SVG will be viewed. + +.. _rcparam_svg_hashsalt: + +svg.hashsalt: ``None`` + If not None, use this string as hash salt instead of uuid4 + +.. _rcparam_svg_id: + +svg.id: ``None`` + If not None, use this string as the value for the `id` attribute in the top tag + +.. _rcparam_pgf_rcfonts: + +pgf.rcfonts: ``True`` + *no description* + +.. _rcparam_pgf_preamble: + +pgf.preamble: ``''`` + See text.latex.preamble for documentation + +.. _rcparam_pgf_texsystem: + +pgf.texsystem: ``'xelatex'`` + *no description* + +.. _rcparam_docstring_hardcopy: + +docstring.hardcopy: ``False`` + set this when you want to generate hardcopy docstring + +.. _rcparam_keymap_fullscreen: + +keymap.fullscreen: ``['f', 'ctrl+f']`` + toggling + +.. _rcparam_keymap_home: + +keymap.home: ``['h', 'r', 'home']`` + home or reset mnemonic + +.. _rcparam_keymap_back: + +keymap.back: ``['left', 'c', 'backspace', 'MouseButton.BACK']`` + forward / backward keys + +.. _rcparam_keymap_forward: + +keymap.forward: ``['right', 'v', 'MouseButton.FORWARD']`` + for quick navigation + +.. _rcparam_keymap_pan: + +keymap.pan: ``['p']`` + pan mnemonic + +.. _rcparam_keymap_zoom: + +keymap.zoom: ``['o']`` + zoom mnemonic + +.. _rcparam_keymap_save: + +keymap.save: ``['s', 'ctrl+s']`` + saving current figure + +.. _rcparam_keymap_help: + +keymap.help: ``['f1']`` + display help about active tools + +.. _rcparam_keymap_quit: + +keymap.quit: ``['ctrl+w', 'cmd+w', 'q']`` + close the current figure + +.. _rcparam_keymap_quit_all: + +keymap.quit_all: ``[]`` + close all figures + +.. _rcparam_keymap_grid: + +keymap.grid: ``['g']`` + switching on/off major grids in current axes + +.. _rcparam_keymap_grid_minor: + +keymap.grid_minor: ``['G']`` + switching on/off minor grids in current axes + +.. _rcparam_keymap_yscale: + +keymap.yscale: ``['l']`` + toggle scaling of y-axes ('log'/'linear') + +.. _rcparam_keymap_xscale: + +keymap.xscale: ``['k', 'L']`` + toggle scaling of x-axes ('log'/'linear') + +.. _rcparam_keymap_copy: + +keymap.copy: ``['ctrl+c', 'cmd+c']`` + copy figure to clipboard + +.. _rcparam_animation_html: + +animation.html: ``'none'`` + How to display the animation as HTML in the IPython notebook: - 'html5' uses HTML5 video tag - 'jshtml' creates a JavaScript animation + +.. _rcparam_animation_writer: + +animation.writer: ``'ffmpeg'`` + MovieWriter 'backend' to use + +.. _rcparam_animation_codec: + +animation.codec: ``'h264'`` + Codec to use for writing movie + +.. _rcparam_animation_bitrate: + +animation.bitrate: ``-1`` + Controls size/quality trade-off for movie. -1 implies let utility auto-determine + +.. _rcparam_animation_frame_format: + +animation.frame_format: ``'png'`` + Controls frame format used by temp files + +.. _rcparam_animation_ffmpeg_path: + +animation.ffmpeg_path: ``'ffmpeg'`` + Path to ffmpeg binary. Unqualified paths are resolved by subprocess.Popen. + +.. _rcparam_animation_ffmpeg_args: + +animation.ffmpeg_args: ``[]`` + Additional arguments to pass to ffmpeg + +.. _rcparam_animation_convert_path: + +animation.convert_path: ``'convert'`` + Path to ImageMagick's convert binary. Unqualified paths are resolved by subprocess.Popen, except that on Windows, we look up an install of ImageMagick in the registry (as convert is also the name of a system tool). + +.. _rcparam_animation_convert_args: + +animation.convert_args: ``['-layers', 'OptimizePlus']`` + Additional arguments to pass to convert + +.. _rcparam_animation_embed_limit: + +animation.embed_limit: ``20.0`` + Limit, in MB, of size of base64 encoded animation in HTML (i.e. IPython notebook) + +.. _rcparam__internal_classic_mode: + +_internal.classic_mode: ``False`` + *no description* + +.. _rcparam_backend: + +backend: ``None`` + *no description* diff --git a/doc/users/annotations.rst b/doc/users/annotations.rst deleted file mode 100644 index 992ef68eee3e..000000000000 --- a/doc/users/annotations.rst +++ /dev/null @@ -1,503 +0,0 @@ -============ - Annotation -============ - -.. contents:: Table of Contents - :depth: 3 - -.. _annotations-tutorial: - -Basic annotation -================ - -The uses of the basic :func:`~matplotlib.pyplot.text` will place text -at an arbitrary position on the Axes. A common use case of text is to -annotate some feature of the plot, and the -:func:`~matplotlib.Axes.annotate` method provides helper functionality -to make annotations easy. In an annotation, there are two points to -consider: the location being annotated represented by the argument -``xy`` and the location of the text ``xytext``. Both of these -arguments are ``(x,y)`` tuples. - -.. plot:: pyplots/annotation_basic.py - :include-source: - - -In this example, both the ``xy`` (arrow tip) and ``xytext`` locations -(text location) are in data coordinates. There are a variety of other -coordinate systems one can choose -- you can specify the coordinate -system of ``xy`` and ``xytext`` with one of the following strings for -``xycoords`` and ``textcoords`` (default is 'data') - -==================== ==================================================== -argument coordinate system -==================== ==================================================== - 'figure points' points from the lower left corner of the figure - 'figure pixels' pixels from the lower left corner of the figure - 'figure fraction' 0,0 is lower left of figure and 1,1 is upper right - 'axes points' points from lower left corner of axes - 'axes pixels' pixels from lower left corner of axes - 'axes fraction' 0,0 is lower left of axes and 1,1 is upper right - 'data' use the axes data coordinate system -==================== ==================================================== - -For example to place the text coordinates in fractional axes -coordinates, one could do:: - - ax.annotate('local max', xy=(3, 1), xycoords='data', - xytext=(0.8, 0.95), textcoords='axes fraction', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='right', verticalalignment='top', - ) - -For physical coordinate systems (points or pixels) the origin is the -bottom-left of the figure or axes. - -Optionally, you can enable drawing of an arrow from the text to the annotated -point by giving a dictionary of arrow properties in the optional keyword -argument ``arrowprops``. - - -==================== ===================================================== -``arrowprops`` key description -==================== ===================================================== -width the width of the arrow in points -frac the fraction of the arrow length occupied by the head -headwidth the width of the base of the arrow head in points -shrink move the tip and base some percent away from - the annotated point and text - -\*\*kwargs any key for :class:`matplotlib.patches.Polygon`, - e.g., ``facecolor`` -==================== ===================================================== - - -In the example below, the ``xy`` point is in native coordinates -(``xycoords`` defaults to 'data'). For a polar axes, this is in -(theta, radius) space. The text in this example is placed in the -fractional figure coordinate system. :class:`matplotlib.text.Text` -keyword args like ``horizontalalignment``, ``verticalalignment`` and -``fontsize`` are passed from `~matplotlib.Axes.annotate` to the -``Text`` instance. - -.. plot:: pyplots/annotation_polar.py - :include-source: - -For more on all the wild and wonderful things you can do with -annotations, including fancy arrows, see :ref:`plotting-guide-annotation` -and :ref:`pylab_examples-annotation_demo`. - - -Do not proceed unless you have already read :ref:`annotations-tutorial`, -:func:`~matplotlib.pyplot.text` and :func:`~matplotlib.pyplot.annotate`! - - -.. _plotting-guide-annotation: - -Advanced Annotation -=================== - - -Annotating with Text with Box ------------------------------ - -Let's start with a simple example. - -.. plot:: users/plotting/examples/annotate_text_arrow.py - - -The :func:`~matplotlib.pyplot.text` function in the pyplot module (or -text method of the Axes class) takes bbox keyword argument, and when -given, a box around the text is drawn. :: - - bbox_props = dict(boxstyle="rarrow,pad=0.3", fc="cyan", ec="b", lw=2) - t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45, - size=15, - bbox=bbox_props) - - -The patch object associated with the text can be accessed by:: - - bb = t.get_bbox_patch() - -The return value is an instance of FancyBboxPatch and the patch -properties like facecolor, edgewidth, etc. can be accessed and -modified as usual. To change the shape of the box, use the *set_boxstyle* -method. :: - - bb.set_boxstyle("rarrow", pad=0.6) - -The arguments are the name of the box style with its attributes as -keyword arguments. Currently, following box styles are implemented. - - ========== ============== ========================== - Class Name Attrs - ========== ============== ========================== - Circle ``circle`` pad=0.3 - DArrow ``darrow`` pad=0.3 - LArrow ``larrow`` pad=0.3 - RArrow ``rarrow`` pad=0.3 - Round ``round`` pad=0.3,rounding_size=None - Round4 ``round4`` pad=0.3,rounding_size=None - Roundtooth ``roundtooth`` pad=0.3,tooth_size=None - Sawtooth ``sawtooth`` pad=0.3,tooth_size=None - Square ``square`` pad=0.3 - ========== ============== ========================== - -.. plot:: mpl_examples/pylab_examples/fancybox_demo2.py - - -Note that the attribute arguments can be specified within the style -name with separating comma (this form can be used as "boxstyle" value -of bbox argument when initializing the text instance) :: - - bb.set_boxstyle("rarrow,pad=0.6") - - - - -Annotating with Arrow ---------------------- - -The :func:`~matplotlib.pyplot.annotate` function in the pyplot module -(or annotate method of the Axes class) is used to draw an arrow -connecting two points on the plot. :: - - ax.annotate("Annotation", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='offset points', - ) - -This annotates a point at ``xy`` in the given coordinate (``xycoords``) -with the text at ``xytext`` given in ``textcoords``. Often, the -annotated point is specified in the *data* coordinate and the annotating -text in *offset points*. -See :func:`~matplotlib.pyplot.annotate` for available coordinate systems. - -An arrow connecting two points (xy & xytext) can be optionally drawn by -specifying the ``arrowprops`` argument. To draw only an arrow, use -empty string as the first argument. :: - - ax.annotate("", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3"), - ) - -.. plot:: users/plotting/examples/annotate_simple01.py - -The arrow drawing takes a few steps. - -1. a connecting path between two points are created. This is - controlled by ``connectionstyle`` key value. - -2. If patch object is given (*patchA* & *patchB*), the path is clipped to - avoid the patch. - -3. The path is further shrunk by given amount of pixels (*shrinkA* - & *shrinkB*) - -4. The path is transmuted to arrow patch, which is controlled by the - ``arrowstyle`` key value. - - -.. plot:: users/plotting/examples/annotate_explain.py - - -The creation of the connecting path between two points is controlled by -``connectionstyle`` key and the following styles are available. - - ========== ============================================= - Name Attrs - ========== ============================================= - ``angle`` angleA=90,angleB=0,rad=0.0 - ``angle3`` angleA=90,angleB=0 - ``arc`` angleA=0,angleB=0,armA=None,armB=None,rad=0.0 - ``arc3`` rad=0.0 - ``bar`` armA=0.0,armB=0.0,fraction=0.3,angle=None - ========== ============================================= - -Note that "3" in ``angle3`` and ``arc3`` is meant to indicate that the -resulting path is a quadratic spline segment (three control -points). As will be discussed below, some arrow style options can only -be used when the connecting path is a quadratic spline. - -The behavior of each connection style is (limitedly) demonstrated in the -example below. (Warning : The behavior of the ``bar`` style is currently not -well defined, it may be changed in the future). - -.. plot:: users/plotting/examples/connectionstyle_demo.py - - -The connecting path (after clipping and shrinking) is then mutated to -an arrow patch, according to the given ``arrowstyle``. - - ========== ============================================= - Name Attrs - ========== ============================================= - ``-`` None - ``->`` head_length=0.4,head_width=0.2 - ``-[`` widthB=1.0,lengthB=0.2,angleB=None - ``|-|`` widthA=1.0,widthB=1.0 - ``-|>`` head_length=0.4,head_width=0.2 - ``<-`` head_length=0.4,head_width=0.2 - ``<->`` head_length=0.4,head_width=0.2 - ``<|-`` head_length=0.4,head_width=0.2 - ``<|-|>`` head_length=0.4,head_width=0.2 - ``fancy`` head_length=0.4,head_width=0.4,tail_width=0.4 - ``simple`` head_length=0.5,head_width=0.5,tail_width=0.2 - ``wedge`` tail_width=0.3,shrink_factor=0.5 - ========== ============================================= - -.. plot:: mpl_examples/pylab_examples/fancyarrow_demo.py - -Some arrowstyles only work with connection styles that generate a -quadratic-spline segment. They are ``fancy``, ``simple``, and ``wedge``. -For these arrow styles, you must use the "angle3" or "arc3" connection -style. - -If the annotation string is given, the patchA is set to the bbox patch -of the text by default. - -.. plot:: users/plotting/examples/annotate_simple02.py - -As in the text command, a box around the text can be drawn using -the ``bbox`` argument. - -.. plot:: users/plotting/examples/annotate_simple03.py - -By default, the starting point is set to the center of the text -extent. This can be adjusted with ``relpos`` key value. The values -are normalized to the extent of the text. For example, (0,0) means -lower-left corner and (1,1) means top-right. - -.. plot:: users/plotting/examples/annotate_simple04.py - - -Placing Artist at the anchored location of the Axes ---------------------------------------------------- - -There are classes of artists that can be placed at an anchored location -in the Axes. A common example is the legend. This type of artist can -be created by using the OffsetBox class. A few predefined classes are -available in ``mpl_toolkits.axes_grid1.anchored_artists`` others in -``matplotlib.offsetbox`` :: - - from matplotlib.offsetbox import AnchoredText - at = AnchoredText("Figure 1a", - prop=dict(size=8), frameon=True, - loc=2, - ) - at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") - ax.add_artist(at) - - -.. plot:: users/plotting/examples/anchored_box01.py - - -The *loc* keyword has same meaning as in the legend command. - -A simple application is when the size of the artist (or collection of -artists) is known in pixel size during the time of creation. For -example, If you want to draw a circle with fixed size of 20 pixel x 20 -pixel (radius = 10 pixel), you can utilize -``AnchoredDrawingArea``. The instance is created with a size of the -drawing area (in pixels), and arbitrary artists can added to the -drawing area. Note that the extents of the artists that are added to -the drawing area are not related to the placement of the drawing -area itself. Only the initial size matters. :: - - from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea - - ada = AnchoredDrawingArea(20, 20, 0, 0, - loc=1, pad=0., frameon=False) - p1 = Circle((10, 10), 10) - ada.drawing_area.add_artist(p1) - p2 = Circle((30, 10), 5, fc="r") - ada.drawing_area.add_artist(p2) - -The artists that are added to the drawing area should not have a -transform set (it will be overridden) and the dimensions of those -artists are interpreted as a pixel coordinate, i.e., the radius of the -circles in above example are 10 pixels and 5 pixels, respectively. - -.. plot:: users/plotting/examples/anchored_box02.py - -Sometimes, you want your artists to scale with the data coordinate (or -coordinates other than canvas pixels). You can use -``AnchoredAuxTransformBox`` class. This is similar to -``AnchoredDrawingArea`` except that the extent of the artist is -determined during the drawing time respecting the specified transform. :: - - from mpl_toolkits.axes_grid1.anchored_artists import AnchoredAuxTransformBox - - box = AnchoredAuxTransformBox(ax.transData, loc=2) - el = Ellipse((0,0), width=0.1, height=0.4, angle=30) # in data coordinates! - box.drawing_area.add_artist(el) - -The ellipse in the above example will have width and height -corresponding to 0.1 and 0.4 in data coordinateing and will be -automatically scaled when the view limits of the axes change. - -.. plot:: users/plotting/examples/anchored_box03.py - -As in the legend, the bbox_to_anchor argument can be set. Using the -HPacker and VPacker, you can have an arrangement(?) of artist as in the -legend (as a matter of fact, this is how the legend is created). - -.. plot:: users/plotting/examples/anchored_box04.py - -Note that unlike the legend, the ``bbox_transform`` is set -to IdentityTransform by default. - -Using Complex Coordinates with Annotations ------------------------------------------- - -The Annotation in matplotlib supports several types of coordinates as -described in :ref:`annotations-tutorial`. For an advanced user who wants -more control, it supports a few other options. - - 1. :class:`~matplotlib.transforms.Transform` instance. For example, :: - - ax.annotate("Test", xy=(0.5, 0.5), xycoords=ax.transAxes) - - is identical to :: - - ax.annotate("Test", xy=(0.5, 0.5), xycoords="axes fraction") - - With this, you can annotate a point in other axes. :: - - ax1, ax2 = subplot(121), subplot(122) - ax2.annotate("Test", xy=(0.5, 0.5), xycoords=ax1.transData, - xytext=(0.5, 0.5), textcoords=ax2.transData, - arrowprops=dict(arrowstyle="->")) - - 2. :class:`~matplotlib.artist.Artist` instance. The xy value (or - xytext) is interpreted as a fractional coordinate of the bbox - (return value of *get_window_extent*) of the artist. :: - - an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data", - va="center", ha="center", - bbox=dict(boxstyle="round", fc="w")) - an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1, # (1,0.5) of the an1's bbox - xytext=(30,0), textcoords="offset points", - va="center", ha="left", - bbox=dict(boxstyle="round", fc="w"), - arrowprops=dict(arrowstyle="->")) - - .. plot:: users/plotting/examples/annotate_simple_coord01.py - - Note that it is your responsibility that the extent of the - coordinate artist (*an1* in above example) is determined before *an2* - gets drawn. In most cases, it means that *an2* needs to be drawn - later than *an1*. - - - 3. A callable object that returns an instance of either - :class:`~matplotlib.transforms.BboxBase` or - :class:`~matplotlib.transforms.Transform`. If a transform is - returned, it is the same as 1 and if a bbox is returned, it is the same - as 2. The callable object should take a single argument of the - renderer instance. For example, the following two commands give - identical results :: - - an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1, - xytext=(30,0), textcoords="offset points") - an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1.get_window_extent, - xytext=(30,0), textcoords="offset points") - - - 4. A tuple of two coordinate specifications. The first item is for the - x-coordinate and the second is for the y-coordinate. For example, :: - - annotate("Test", xy=(0.5, 1), xycoords=("data", "axes fraction")) - - 0.5 is in data coordinates, and 1 is in normalized axes coordinates. - You may use an artist or transform as with a tuple. For example, - - .. plot:: users/plotting/examples/annotate_simple_coord02.py - :include-source: - - - 5. Sometimes, you want your annotation with some "offset points", not from the - annotated point but from some other point. - :class:`~matplotlib.text.OffsetFrom` is a helper class for such cases. - - .. plot:: users/plotting/examples/annotate_simple_coord03.py - :include-source: - - You may take a look at this example :ref:`pylab_examples-annotation_demo3`. - -Using ConnectorPatch --------------------- - -The ConnectorPatch is like an annotation without text. While the annotate -function is recommended in most situations, the ConnectorPatch is useful when -you want to connect points in different axes. :: - - from matplotlib.patches import ConnectionPatch - xy = (0.2, 0.2) - con = ConnectionPatch(xyA=xy, xyB=xy, coordsA="data", coordsB="data", - axesA=ax1, axesB=ax2) - ax2.add_artist(con) - -The above code connects point xy in the data coordinates of ``ax1`` to -point xy in the data coordinates of ``ax2``. Here is a simple example. - -.. plot:: users/plotting/examples/connect_simple01.py - - -While the ConnectorPatch instance can be added to any axes, you may want to add -it to the axes that is latest in drawing order to prevent overlap by other -axes. - - -Advanced Topics -~~~~~~~~~~~~~~~ - -Zoom effect between Axes ------------------------- - -``mpl_toolkits.axes_grid1.inset_locator`` defines some patch classes useful -for interconnecting two axes. Understanding the code requires some -knowledge of how mpl's transform works. But, utilizing it will be -straight forward. - - -.. plot:: mpl_examples/pylab_examples/axes_zoom_effect.py - - -Define Custom BoxStyle ----------------------- - -You can use a custom box style. The value for the ``boxstyle`` can be a -callable object in the following forms.:: - - def __call__(self, x0, y0, width, height, mutation_size, - aspect_ratio=1.): - """ - Given the location and size of the box, return the path of - the box around it. - - - *x0*, *y0*, *width*, *height* : location and size of the box - - *mutation_size* : a reference scale for the mutation. - - *aspect_ratio* : aspect-ratio for the mutation. - """ - path = ... - return path - -Here is a complete example. - -.. plot:: users/plotting/examples/custom_boxstyle01.py - -However, it is recommended that you derive from the -matplotlib.patches.BoxStyle._Base as demonstrated below. - -.. plot:: users/plotting/examples/custom_boxstyle02.py - :include-source: - - -Similarly, you can define a custom ConnectionStyle and a custom ArrowStyle. -See the source code of ``lib/matplotlib/patches.py`` and check -how each style class is defined. diff --git a/doc/users/artists.rst b/doc/users/artists.rst deleted file mode 100644 index 17f75fcac29f..000000000000 --- a/doc/users/artists.rst +++ /dev/null @@ -1,639 +0,0 @@ -.. _artist-tutorial: - -*************** -Artist tutorial -*************** - -There are three layers to the matplotlib API. The -:class:`matplotlib.backend_bases.FigureCanvas` is the area onto which -the figure is drawn, the :class:`matplotlib.backend_bases.Renderer` is -the object which knows how to draw on the -:class:`~matplotlib.backend_bases.FigureCanvas`, and the -:class:`matplotlib.artist.Artist` is the object that knows how to use -a renderer to paint onto the canvas. The -:class:`~matplotlib.backend_bases.FigureCanvas` and -:class:`~matplotlib.backend_bases.Renderer` handle all the details of -talking to user interface toolkits like `wxPython -`_ or drawing languages like PostScript®, and -the ``Artist`` handles all the high level constructs like representing -and laying out the figure, text, and lines. The typical user will -spend 95% of his time working with the ``Artists``. - -There are two types of ``Artists``: primitives and containers. The primitives -represent the standard graphical objects we want to paint onto our canvas: -:class:`~matplotlib.lines.Line2D`, :class:`~matplotlib.patches.Rectangle`, -:class:`~matplotlib.text.Text`, :class:`~matplotlib.image.AxesImage`, etc., and -the containers are places to put them (:class:`~matplotlib.axis.Axis`, -:class:`~matplotlib.axes.Axes` and :class:`~matplotlib.figure.Figure`). The -standard use is to create a :class:`~matplotlib.figure.Figure` instance, use -the ``Figure`` to create one or more :class:`~matplotlib.axes.Axes` or -:class:`~matplotlib.axes.Subplot` instances, and use the ``Axes`` instance -helper methods to create the primitives. In the example below, we create a -``Figure`` instance using :func:`matplotlib.pyplot.figure`, which is a -convenience method for instantiating ``Figure`` instances and connecting them -with your user interface or drawing toolkit ``FigureCanvas``. As we will -discuss below, this is not necessary -- you can work directly with PostScript, -PDF Gtk+, or wxPython ``FigureCanvas`` instances, instantiate your ``Figures`` -directly and connect them yourselves -- but since we are focusing here on the -``Artist`` API we'll let :mod:`~matplotlib.pyplot` handle some of those details -for us:: - - import matplotlib.pyplot as plt - fig = plt.figure() - ax = fig.add_subplot(2,1,1) # two rows, one column, first plot - -The :class:`~matplotlib.axes.Axes` is probably the most important -class in the matplotlib API, and the one you will be working with most -of the time. This is because the ``Axes`` is the plotting area into -which most of the objects go, and the ``Axes`` has many special helper -methods (:meth:`~matplotlib.axes.Axes.plot`, -:meth:`~matplotlib.axes.Axes.text`, -:meth:`~matplotlib.axes.Axes.hist`, -:meth:`~matplotlib.axes.Axes.imshow`) to create the most common -graphics primitives (:class:`~matplotlib.lines.Line2D`, -:class:`~matplotlib.text.Text`, -:class:`~matplotlib.patches.Rectangle`, -:class:`~matplotlib.image.Image`, respectively). These helper methods -will take your data (e.g., ``numpy`` arrays and strings) and create -primitive ``Artist`` instances as needed (e.g., ``Line2D``), add them to -the relevant containers, and draw them when requested. Most of you -are probably familiar with the :class:`~matplotlib.axes.Subplot`, -which is just a special case of an ``Axes`` that lives on a regular -rows by columns grid of ``Subplot`` instances. If you want to create -an ``Axes`` at an arbitrary location, simply use the -:meth:`~matplotlib.figure.Figure.add_axes` method which takes a list -of ``[left, bottom, width, height]`` values in 0-1 relative figure -coordinates:: - - fig2 = plt.figure() - ax2 = fig2.add_axes([0.15, 0.1, 0.7, 0.3]) - -Continuing with our example:: - - import numpy as np - t = np.arange(0.0, 1.0, 0.01) - s = np.sin(2*np.pi*t) - line, = ax.plot(t, s, color='blue', lw=2) - -In this example, ``ax`` is the ``Axes`` instance created by the -``fig.add_subplot`` call above (remember ``Subplot`` is just a -subclass of ``Axes``) and when you call ``ax.plot``, it creates a -``Line2D`` instance and adds it to the :attr:`Axes.lines -` list. In the interactive `ipython -`_ session below, you can see that the -``Axes.lines`` list is length one and contains the same line that was -returned by the ``line, = ax.plot...`` call: - -.. sourcecode:: ipython - - In [101]: ax.lines[0] - Out[101]: - - In [102]: line - Out[102]: - -If you make subsequent calls to ``ax.plot`` (and the hold state is "on" -which is the default) then additional lines will be added to the list. -You can remove lines later simply by calling the list methods; either -of these will work:: - - del ax.lines[0] - ax.lines.remove(line) # one or the other, not both! - -The Axes also has helper methods to configure and decorate the x-axis -and y-axis tick, tick labels and axis labels:: - - xtext = ax.set_xlabel('my xdata') # returns a Text instance - ytext = ax.set_ylabel('my ydata') - -When you call :meth:`ax.set_xlabel `, -it passes the information on the :class:`~matplotlib.text.Text` -instance of the :class:`~matplotlib.axis.XAxis`. Each ``Axes`` -instance contains an :class:`~matplotlib.axis.XAxis` and a -:class:`~matplotlib.axis.YAxis` instance, which handle the layout and -drawing of the ticks, tick labels and axis labels. - -.. I'm commenting this out, since the new Sphinx cross-references -.. sort of take care of this above - MGD - -.. Here are the most important matplotlib modules that contain the -.. classes referenced above - -.. =============== ================== -.. Artist Module -.. =============== ================== -.. Artist matplotlib.artist -.. Rectangle matplotlib.patches -.. Line2D matplotlib.lines -.. Axes matplotlib.axes -.. XAxis and YAxis matplotlib.axis -.. Figure matplotlib.figure -.. Text matplotlib.text -.. =============== ================== - -Try creating the figure below. - -.. plot:: pyplots/fig_axes_labels_simple.py - -.. _customizing-artists: - -Customizing your objects -======================== - -Every element in the figure is represented by a matplotlib -:class:`~matplotlib.artist.Artist`, and each has an extensive list of -properties to configure its appearance. The figure itself contains a -:class:`~matplotlib.patches.Rectangle` exactly the size of the figure, -which you can use to set the background color and transparency of the -figures. Likewise, each :class:`~matplotlib.axes.Axes` bounding box -(the standard white box with black edges in the typical matplotlib -plot, has a ``Rectangle`` instance that determines the color, -transparency, and other properties of the Axes. These instances are -stored as member variables :attr:`Figure.patch -` and :attr:`Axes.patch -` ("Patch" is a name inherited from -MATLAB, and is a 2D "patch" of color on the figure, e.g., rectangles, -circles and polygons). Every matplotlib ``Artist`` has the following -properties - -========== ================================================================================ -Property Description -========== ================================================================================ -alpha The transparency - a scalar from 0-1 -animated A boolean that is used to facilitate animated drawing -axes The axes that the Artist lives in, possibly None -clip_box The bounding box that clips the Artist -clip_on Whether clipping is enabled -clip_path The path the artist is clipped to -contains A picking function to test whether the artist contains the pick point -figure The figure instance the artist lives in, possibly None -label A text label (e.g., for auto-labeling) -picker A python object that controls object picking -transform The transformation -visible A boolean whether the artist should be drawn -zorder A number which determines the drawing order -rasterized Boolean; Turns vectors into rastergraphics: (for compression & eps transparency) -========== ================================================================================ - -Each of the properties is accessed with an old-fashioned setter or -getter (yes we know this irritates Pythonistas and we plan to support -direct access via properties or traits but it hasn't been done yet). -For example, to multiply the current alpha by a half:: - - a = o.get_alpha() - o.set_alpha(0.5*a) - -If you want to set a number of properties at once, you can also use -the ``set`` method with keyword arguments. For example:: - - o.set(alpha=0.5, zorder=2) - -If you are working interactively at the python shell, a handy way to -inspect the ``Artist`` properties is to use the -:func:`matplotlib.artist.getp` function (simply -:func:`~matplotlib.pylab.getp` in pylab), which lists the properties -and their values. This works for classes derived from ``Artist`` as -well, e.g., ``Figure`` and ``Rectangle``. Here are the ``Figure`` rectangle -properties mentioned above: - -.. sourcecode:: ipython - - In [149]: matplotlib.artist.getp(fig.patch) - alpha = 1.0 - animated = False - antialiased or aa = True - axes = None - clip_box = None - clip_on = False - clip_path = None - contains = None - edgecolor or ec = w - facecolor or fc = 0.75 - figure = Figure(8.125x6.125) - fill = 1 - hatch = None - height = 1 - label = - linewidth or lw = 1.0 - picker = None - transform = - verts = ((0, 0), (0, 1), (1, 1), (1, 0)) - visible = True - width = 1 - window_extent = - x = 0 - y = 0 - zorder = 1 - -.. TODO: Update these URLs - -The docstrings for all of the classes also contain the ``Artist`` -properties, so you can consult the interactive "help" or the -:ref:`artist-api` for a listing of properties for a given object. - -.. _object-containers: - -Object containers -================= - - -Now that we know how to inspect and set the properties of a given -object we want to configure, we need to now how to get at that object. -As mentioned in the introduction, there are two kinds of objects: -primitives and containers. The primitives are usually the things you -want to configure (the font of a :class:`~matplotlib.text.Text` -instance, the width of a :class:`~matplotlib.lines.Line2D`) although -the containers also have some properties as well -- for example the -:class:`~matplotlib.axes.Axes` :class:`~matplotlib.artist.Artist` is a -container that contains many of the primitives in your plot, but it -also has properties like the ``xscale`` to control whether the xaxis -is 'linear' or 'log'. In this section we'll review where the various -container objects store the ``Artists`` that you want to get at. - -.. _figure-container: - -Figure container -================ - -The top level container ``Artist`` is the -:class:`matplotlib.figure.Figure`, and it contains everything in the -figure. The background of the figure is a -:class:`~matplotlib.patches.Rectangle` which is stored in -:attr:`Figure.patch `. As -you add subplots (:meth:`~matplotlib.figure.Figure.add_subplot`) and -axes (:meth:`~matplotlib.figure.Figure.add_axes`) to the figure -these will be appended to the :attr:`Figure.axes -`. These are also returned by the -methods that create them: - -.. sourcecode:: ipython - - In [156]: fig = plt.figure() - - In [157]: ax1 = fig.add_subplot(211) - - In [158]: ax2 = fig.add_axes([0.1, 0.1, 0.7, 0.3]) - - In [159]: ax1 - Out[159]: - - In [160]: print fig.axes - [, ] - -Because the figure maintains the concept of the "current axes" (see -:meth:`Figure.gca ` and -:meth:`Figure.sca `) to support the -pylab/pyplot state machine, you should not insert or remove axes -directly from the axes list, but rather use the -:meth:`~matplotlib.figure.Figure.add_subplot` and -:meth:`~matplotlib.figure.Figure.add_axes` methods to insert, and the -:meth:`~matplotlib.figure.Figure.delaxes` method to delete. You are -free however, to iterate over the list of axes or index into it to get -access to ``Axes`` instances you want to customize. Here is an -example which turns all the axes grids on:: - - for ax in fig.axes: - ax.grid(True) - - -The figure also has its own text, lines, patches and images, which you -can use to add primitives directly. The default coordinate system for -the ``Figure`` will simply be in pixels (which is not usually what you -want) but you can control this by setting the transform property of -the ``Artist`` you are adding to the figure. - -.. TODO: Is that still true? - -More useful is "figure coordinates" where (0, 0) is the bottom-left of -the figure and (1, 1) is the top-right of the figure which you can -obtain by setting the ``Artist`` transform to :attr:`fig.transFigure -`: - -.. sourcecode:: ipython - - In [191]: fig = plt.figure() - - In [192]: l1 = matplotlib.lines.Line2D([0, 1], [0, 1], - transform=fig.transFigure, figure=fig) - - In [193]: l2 = matplotlib.lines.Line2D([0, 1], [1, 0], - transform=fig.transFigure, figure=fig) - - In [194]: fig.lines.extend([l1, l2]) - - In [195]: fig.canvas.draw() - -.. plot:: pyplots/fig_x.py - - -Here is a summary of the Artists the figure contains - -.. TODO: Add xrefs to this table - -================ =============================================================== -Figure attribute Description -================ =============================================================== -axes A list of Axes instances (includes Subplot) -patch The Rectangle background -images A list of FigureImages patches - useful for raw pixel display -legends A list of Figure Legend instances (different from Axes.legends) -lines A list of Figure Line2D instances (rarely used, see Axes.lines) -patches A list of Figure patches (rarely used, see Axes.patches) -texts A list Figure Text instances -================ =============================================================== - -.. _axes-container: - -Axes container -============== - -The :class:`matplotlib.axes.Axes` is the center of the matplotlib -universe -- it contains the vast majority of all the ``Artists`` used -in a figure with many helper methods to create and add these -``Artists`` to itself, as well as helper methods to access and -customize the ``Artists`` it contains. Like the -:class:`~matplotlib.figure.Figure`, it contains a -:class:`~matplotlib.patches.Patch` -:attr:`~matplotlib.axes.Axes.patch` which is a -:class:`~matplotlib.patches.Rectangle` for Cartesian coordinates and a -:class:`~matplotlib.patches.Circle` for polar coordinates; this patch -determines the shape, background and border of the plotting region:: - - ax = fig.add_subplot(111) - rect = ax.patch # a Rectangle instance - rect.set_facecolor('green') - -When you call a plotting method, e.g., the canonical -:meth:`~matplotlib.axes.Axes.plot` and pass in arrays or lists of -values, the method will create a :meth:`matplotlib.lines.Line2D` -instance, update the line with all the ``Line2D`` properties passed as -keyword arguments, add the line to the :attr:`Axes.lines -` container, and returns it to you: - -.. sourcecode:: ipython - - In [213]: x, y = np.random.rand(2, 100) - - In [214]: line, = ax.plot(x, y, '-', color='blue', linewidth=2) - -``plot`` returns a list of lines because you can pass in multiple x, y -pairs to plot, and we are unpacking the first element of the length -one list into the line variable. The line has been added to the -``Axes.lines`` list: - -.. sourcecode:: ipython - - In [229]: print ax.lines - [] - -Similarly, methods that create patches, like -:meth:`~matplotlib.axes.Axes.bar` creates a list of rectangles, will -add the patches to the :attr:`Axes.patches -` list: - -.. sourcecode:: ipython - - In [233]: n, bins, rectangles = ax.hist(np.random.randn(1000), 50, facecolor='yellow') - - In [234]: rectangles - Out[234]: - - In [235]: print len(ax.patches) - -You should not add objects directly to the ``Axes.lines`` or -``Axes.patches`` lists unless you know exactly what you are doing, -because the ``Axes`` needs to do a few things when it creates and adds -an object. It sets the figure and axes property of the ``Artist``, as -well as the default ``Axes`` transformation (unless a transformation -is set). It also inspects the data contained in the ``Artist`` to -update the data structures controlling auto-scaling, so that the view -limits can be adjusted to contain the plotted data. You can, -nonetheless, create objects yourself and add them directly to the -``Axes`` using helper methods like -:meth:`~matplotlib.axes.Axes.add_line` and -:meth:`~matplotlib.axes.Axes.add_patch`. Here is an annotated -interactive session illustrating what is going on: - -.. sourcecode:: ipython - - In [261]: fig = plt.figure() - - In [262]: ax = fig.add_subplot(111) - - # create a rectangle instance - In [263]: rect = matplotlib.patches.Rectangle( (1,1), width=5, height=12) - - # by default the axes instance is None - In [264]: print rect.get_axes() - None - - # and the transformation instance is set to the "identity transform" - In [265]: print rect.get_transform() - - - # now we add the Rectangle to the Axes - In [266]: ax.add_patch(rect) - - # and notice that the ax.add_patch method has set the axes - # instance - In [267]: print rect.get_axes() - Axes(0.125,0.1;0.775x0.8) - - # and the transformation has been set too - In [268]: print rect.get_transform() - - - # the default axes transformation is ax.transData - In [269]: print ax.transData - - - # notice that the xlimits of the Axes have not been changed - In [270]: print ax.get_xlim() - (0.0, 1.0) - - # but the data limits have been updated to encompass the rectangle - In [271]: print ax.dataLim.bounds - (1.0, 1.0, 5.0, 12.0) - - # we can manually invoke the auto-scaling machinery - In [272]: ax.autoscale_view() - - # and now the xlim are updated to encompass the rectangle - In [273]: print ax.get_xlim() - (1.0, 6.0) - - # we have to manually force a figure draw - In [274]: ax.figure.canvas.draw() - - -There are many, many ``Axes`` helper methods for creating primitive -``Artists`` and adding them to their respective containers. The table -below summarizes a small sampling of them, the kinds of ``Artist`` they -create, and where they store them - -============================== ==================== ======================= -Helper method Artist Container -============================== ==================== ======================= -ax.annotate - text annotations Annotate ax.texts -ax.bar - bar charts Rectangle ax.patches -ax.errorbar - error bar plots Line2D and Rectangle ax.lines and ax.patches -ax.fill - shared area Polygon ax.patches -ax.hist - histograms Rectangle ax.patches -ax.imshow - image data AxesImage ax.images -ax.legend - axes legends Legend ax.legends -ax.plot - xy plots Line2D ax.lines -ax.scatter - scatter charts PolygonCollection ax.collections -ax.text - text Text ax.texts -============================== ==================== ======================= - - -In addition to all of these ``Artists``, the ``Axes`` contains two -important ``Artist`` containers: the :class:`~matplotlib.axis.XAxis` -and :class:`~matplotlib.axis.YAxis`, which handle the drawing of the -ticks and labels. These are stored as instance variables -:attr:`~matplotlib.axes.Axes.xaxis` and -:attr:`~matplotlib.axes.Axes.yaxis`. The ``XAxis`` and ``YAxis`` -containers will be detailed below, but note that the ``Axes`` contains -many helper methods which forward calls on to the -:class:`~matplotlib.axis.Axis` instances so you often do not need to -work with them directly unless you want to. For example, you can set -the font size of the ``XAxis`` ticklabels using the ``Axes`` helper -method:: - - for label in ax.get_xticklabels(): - label.set_color('orange') - -Below is a summary of the Artists that the Axes contains - -============== ====================================== -Axes attribute Description -============== ====================================== -artists A list of Artist instances -patch Rectangle instance for Axes background -collections A list of Collection instances -images A list of AxesImage -legends A list of Legend instances -lines A list of Line2D instances -patches A list of Patch instances -texts A list of Text instances -xaxis matplotlib.axis.XAxis instance -yaxis matplotlib.axis.YAxis instance -============== ====================================== - -.. _axis-container: - -Axis containers -=============== - -The :class:`matplotlib.axis.Axis` instances handle the drawing of the -tick lines, the grid lines, the tick labels and the axis label. You -can configure the left and right ticks separately for the y-axis, and -the upper and lower ticks separately for the x-axis. The ``Axis`` -also stores the data and view intervals used in auto-scaling, panning -and zooming, as well as the :class:`~matplotlib.ticker.Locator` and -:class:`~matplotlib.ticker.Formatter` instances which control where -the ticks are placed and how they are represented as strings. - -Each ``Axis`` object contains a :attr:`~matplotlib.axis.Axis.label` attribute -(this is what :mod:`~matplotlib.pylab` modifies in calls to -:func:`~matplotlib.pylab.xlabel` and :func:`~matplotlib.pylab.ylabel`) as well -as a list of major and minor ticks. The ticks are -:class:`~matplotlib.axis.XTick` and :class:`~matplotlib.axis.YTick` instances, -which contain the actual line and text primitives that render the ticks and -ticklabels. Because the ticks are dynamically created as needed (e.g., when -panning and zooming), you should access the lists of major and minor ticks -through their accessor methods :meth:`~matplotlib.axis.Axis.get_major_ticks` -and :meth:`~matplotlib.axis.Axis.get_minor_ticks`. Although the ticks contain -all the primitives and will be covered below, the ``Axis`` methods contain -accessor methods to return the tick lines, tick labels, tick locations etc.: - -.. sourcecode:: ipython - - In [285]: axis = ax.xaxis - - In [286]: axis.get_ticklocs() - Out[286]: array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) - - In [287]: axis.get_ticklabels() - Out[287]: - - # note there are twice as many ticklines as labels because by - # default there are tick lines at the top and bottom but only tick - # labels below the xaxis; this can be customized - In [288]: axis.get_ticklines() - Out[288]: - - # by default you get the major ticks back - In [291]: axis.get_ticklines() - Out[291]: - - # but you can also ask for the minor ticks - In [292]: axis.get_ticklines(minor=True) - Out[292]: - -Here is a summary of some of the useful accessor methods of the ``Axis`` -(these have corresponding setters where useful, such as -set_major_formatter) - -====================== ========================================================= -Accessor method Description -====================== ========================================================= -get_scale The scale of the axis, e.g., 'log' or 'linear' -get_view_interval The interval instance of the axis view limits -get_data_interval The interval instance of the axis data limits -get_gridlines A list of grid lines for the Axis -get_label The axis label - a Text instance -get_ticklabels A list of Text instances - keyword minor=True|False -get_ticklines A list of Line2D instances - keyword minor=True|False -get_ticklocs A list of Tick locations - keyword minor=True|False -get_major_locator The matplotlib.ticker.Locator instance for major ticks -get_major_formatter The matplotlib.ticker.Formatter instance for major ticks -get_minor_locator The matplotlib.ticker.Locator instance for minor ticks -get_minor_formatter The matplotlib.ticker.Formatter instance for minor ticks -get_major_ticks A list of Tick instances for major ticks -get_minor_ticks A list of Tick instances for minor ticks -grid Turn the grid on or off for the major or minor ticks -====================== ========================================================= - -Here is an example, not recommended for its beauty, which customizes -the axes and tick properties - -.. plot:: pyplots/fig_axes_customize_simple.py - :include-source: - - -.. _tick-container: - -Tick containers -=============== - -The :class:`matplotlib.axis.Tick` is the final container object in our -descent from the :class:`~matplotlib.figure.Figure` to the -:class:`~matplotlib.axes.Axes` to the :class:`~matplotlib.axis.Axis` -to the :class:`~matplotlib.axis.Tick`. The ``Tick`` contains the tick -and grid line instances, as well as the label instances for the upper -and lower ticks. Each of these is accessible directly as an attribute -of the ``Tick``. In addition, there are boolean variables that determine -whether the upper labels and ticks are on for the x-axis and whether -the right labels and ticks are on for the y-axis. - -============== ========================================================== -Tick attribute Description -============== ========================================================== -tick1line Line2D instance -tick2line Line2D instance -gridline Line2D instance -label1 Text instance -label2 Text instance -gridOn boolean which determines whether to draw the tickline -tick1On boolean which determines whether to draw the 1st tickline -tick2On boolean which determines whether to draw the 2nd tickline -label1On boolean which determines whether to draw tick label -label2On boolean which determines whether to draw tick label -============== ========================================================== - -Here is an example which sets the formatter for the right side ticks with -dollar signs and colors them green on the right side of the yaxis - -.. plot:: pyplots/dollar_ticks.py - :include-source: diff --git a/doc/users/color_index.rst b/doc/users/color_index.rst deleted file mode 100644 index 84238a040f0a..000000000000 --- a/doc/users/color_index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _color-index: - -======== - Colors -======== - -.. htmlonly:: - - :Release: |version| - :Date: |today| - -.. toctree:: - - colors.rst - colormaps.rst - colormapnorms.rst diff --git a/doc/users/colormapnorms.rst b/doc/users/colormapnorms.rst deleted file mode 100644 index e7ea14251340..000000000000 --- a/doc/users/colormapnorms.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. _colormapnorm-tutorial: - -Colormap Normalization -====================== - -Objects that use colormaps by default linearly map the colors in the -colormap from data values *vmin* to *vmax*. For example:: - - pcm = ax.pcolormesh(x, y, Z, vmin=-1., vmax=1., cmap='RdBu_r') - -will map the data in *Z* linearly from -1 to +1, so *Z=0* will -give a color at the center of the colormap *RdBu_r* (white in this -case). - -Matplotlib does this mapping in two steps, with a normalization from -[0,1] occurring first, and then mapping onto the indices in the -colormap. Normalizations are classes defined in the -:func:`matplotlib.colors` module. The default, linear normalization is -:func:`matplotlib.colors.Normalize`. - -Artists that map data to color pass the arguments *vmin* and *vmax* to -construct a :func:`matplotlib.colors.Normalize` instance, then call it: - -.. ipython:: - - In [1]: import matplotlib as mpl - - In [2]: norm = mpl.colors.Normalize(vmin=-1.,vmax=1.) - - In [3]: norm(0.) - Out[3]: 0.5 - -However, there are sometimes cases where it is useful to map data to -colormaps in a non-linear fashion. - -Logarithmic ------------ - -One of the most common transformations is to plot data by taking -its logarithm (to the base-10). This transformation is useful to -display changes across disparate scales. Using :func:`colors.LogNorm` -normalizes the data via :math:`log_{10}`. In the example below, -there are two bumps, one much smaller than the other. Using -:func:`colors.LogNorm`, the shape and location of each bump can clearly -be seen: - -.. plot:: users/plotting/examples/colormap_normalizations_lognorm.py - :include-source: - -Symmetric logarithmic ---------------------- - -Similarly, it sometimes happens that there is data that is positive -and negative, but we would still like a logarithmic scaling applied to -both. In this case, the negative numbers are also scaled -logarithmically, and mapped to smaller numbers; e.g., if `vmin=-vmax`, -then they the negative numbers are mapped from 0 to 0.5 and the -positive from 0.5 to 1. - -Since the logarithm of values close to zero tends toward infinity, a -small range around zero needs to be mapped linearly. The parameter -*linthresh* allows the user to specify the size of this range -(-*linthresh*, *linthresh*). The size of this range in the colormap is -set by *linscale*. When *linscale* == 1.0 (the default), the space used -for the positive and negative halves of the linear range will be equal -to one decade in the logarithmic range. - -.. plot:: users/plotting/examples/colormap_normalizations_symlognorm.py - :include-source: - -Power-law ---------- - -Sometimes it is useful to remap the colors onto a power-law -relationship (i.e. :math:`y=x^{\gamma}`, where :math:`\gamma` is the -power). For this we use the :func:`colors.PowerNorm`. It takes as an -argument *gamma* (*gamma* == 1.0 will just yield the default linear -normalization): - -.. note:: - - There should probably be a good reason for plotting the data using - this type of transformation. Technical viewers are used to linear - and logarithmic axes and data transformations. Power laws are less - common, and viewers should explicitly be made aware that they have - been used. - - -.. plot:: users/plotting/examples/colormap_normalizations_power.py - :include-source: - -Discrete bounds ---------------- - -Another normaization that comes with matplolib is -:func:`colors.BoundaryNorm`. In addition to *vmin* and *vmax*, this -takes as arguments boundaries between which data is to be mapped. The -colors are then linearly distributed between these "bounds". For -instance: - -.. ipython:: - - In [2]: import matplotlib.colors as colors - - In [3]: bounds = np.array([-0.25, -0.125, 0, 0.5, 1]) - - In [4]: norm = colors.BoundaryNorm(boundaries=bounds, ncolors=4) - - In [5]: print(norm([-0.2,-0.15,-0.02, 0.3, 0.8, 0.99])) - [0 0 1 2 3 3] - -Note unlike the other norms, this norm returns values from 0 to *ncolors*-1. - -.. plot:: users/plotting/examples/colormap_normalizations_bounds.py - :include-source: - - -Custom normalization: Two linear ranges ---------------------------------------- - -It is possible to define your own normalization. In the following -example, we modify :func:`colors:SymLogNorm` to use different linear -maps for the negative data values and the positive. (Note that this -example is simple, and does not validate inputs or account for complex -cases such as masked data) - -.. note:: - This may appear soon as :func:`colors.OffsetNorm`. - - As above, non-symmetric mapping of data to color is non-standard - practice for quantitative data, and should only be used advisedly. A - practical example is having an ocean/land colormap where the land and - ocean data span different ranges. - -.. plot:: users/plotting/examples/colormap_normalizations_custom.py - :include-source: diff --git a/doc/users/colormaps.rst b/doc/users/colormaps.rst deleted file mode 100644 index b22846dab5be..000000000000 --- a/doc/users/colormaps.rst +++ /dev/null @@ -1,191 +0,0 @@ -.. _colormaps: - -****************** -Choosing Colormaps -****************** - - -Overview -======== - -The idea behind choosing a good colormap is to find a good representation in 3D -colorspace for your data set. The best colormap for any given data set depends -on many things including: - -- Whether representing form or metric data ([Ware]_) - -- Your knowledge of the data set (*e.g.*, is there a critical value - from which the other values deviate?) - -- If there is an intuitive color scheme for the parameter you are plotting - -- If there is a standard in the field the audience may be expecting - -For many applications, a perceptually uniform colormap is the best -choice --- one in which equal steps in data are perceived as equal -steps in the color space. Researchers have found that the human brain -perceives changes in the lightness parameter as changes in the data -much better than, for example, changes in hue. Therefore, colormaps -which have monotonically increasing lightness through the colormap -will be better interpreted by the viewer. - -Color can be represented in 3D space in various ways. One way to represent color -is using CIELAB. In CIELAB, color space is represented by lightness, -:math:`L^*`; red-green, :math:`a^*`; and yellow-blue, :math:`b^*`. The lightness -parameter :math:`L^*` can then be used to learn more about how the matplotlib -colormaps will be perceived by viewers. - -An excellent starting resource for learning about human perception of colormaps -is from [IBM]_. - - -Classes of colormaps -==================== - -Colormaps are often split into several categories based on their function (see, -*e.g.*, [Moreland]_): - -1. Sequential: change in lightness and often saturation of color - incrementally, often using a single hue; should be used for - representing information that has ordering. - -2. Diverging: change in lightness and possibly saturation of two - different colors that meet in the middle at an unsaturated color; - should be used when the information being plotted has a critical - middle value, such as topography or when the data deviates around - zero. - -3. Qualitative: often are miscellaneous colors; should be used to - represent information which does not have ordering or - relationships. - - -Lightness of matplotlib colormaps -================================= - -Here we examine the lightness values of the matplotlib colormaps. Note that some -documentation on the colormaps is available ([list-colormaps]_). - -Sequential ----------- - -For the Sequential plots, the lightness value increases monotonically through -the colormaps. This is good. Some of the :math:`L^*` values in the colormaps -span from 0 to 100 (binary and the other grayscale), and others start around -:math:`L^*=20`. Those that have a smaller range of :math:`L^*` will accordingly -have a smaller perceptual range. Note also that the :math:`L^*` function varies -amongst the colormaps: some are approximately linear in :math:`L^*` and others -are more curved. - -Sequential2 ------------ - -Many of the :math:`L^*` values from the Sequential2 plots are monotonically -increasing, but some (autumn, cool, spring, and winter) plateau or even go both -up and down in :math:`L^*` space. Others (afmhot, copper, gist_heat, and hot) -have kinks in the :math:`L^*` functions. Data that is being represented in a -region of the colormap that is at a plateau or kink will lead to a perception of -banding of the data in those values in the colormap (see [mycarta-banding]_ for -an excellent example of this). - -Diverging ---------- - -For the Diverging maps, we want to have monotonically increasing :math:`L^*` -values up to a maximum, which should be close to :math:`L^*=100`, followed by -monotonically decreasing :math:`L^*` values. We are looking for approximately -equal minimum :math:`L^*` values at opposite ends of the colormap. By these -measures, BrBG and RdBu are good options. coolwarm is a good option, but it -doesn't span a wide range of :math:`L^*` values (see grayscale section below). - -Qualitative ------------ - -Qualitative colormaps are not aimed at being perceptual maps, but looking at the -lightness parameter can verify that for us. The :math:`L^*` values move all over -the place throughout the colormap, and are clearly not monotonically increasing. -These would not be good options for use as perceptual colormaps. - -Miscellaneous -------------- - -Some of the miscellaneous colormaps have particular uses for which -they have been created. For example, gist_earth, ocean, and terrain -all seem to be created for plotting topography (green/brown) and water -depths (blue) together. We would expect to see a divergence in these -colormaps, then, but multiple kinks may not be ideal, such as in -gist_earth and terrain. CMRmap was created to convert well to -grayscale, though it does appear to have some small kinks in -:math:`L^*`. cubehelix was created to vary smoothly in both lightness -and hue, but appears to have a small hump in the green hue area. - -The often-used jet colormap is included in this set of colormaps. We can see -that the :math:`L^*` values vary widely throughout the colormap, making it a -poor choice for representing data for viewers to see perceptually. See an -extension on this idea at [mycarta-jet]_. - -.. plot:: users/plotting/colormaps/lightness.py - -Grayscale conversion -==================== - -It is important to pay attention to conversion to grayscale for color -plots, since they may be printed on black and white printers. If not -carefully considered, your readers may end up with indecipherable -plots because the grayscale changes unpredictably through the -colormap. - -Conversion to grayscale is done in many different ways [bw]_. Some of the better -ones use a linear combination of the rgb values of a pixel, but weighted -according to how we perceive color intensity. A nonlinear method of conversion -to grayscale is to use the :math:`L^*` values of the pixels. In general, similar -principles apply for this question as they do for presenting one's information -perceptually; that is, if a colormap is chosen that is monotonically increasing -in :math:`L^*` values, it will print in a reasonable manner to grayscale. - -With this in mind, we see that the Sequential colormaps have reasonable -representations in grayscale. Some of the Sequential2 colormaps have decent -enough grayscale representations, though some (autumn, spring, summer, winter) -have very little grayscale change. If a colormap like this was used in a plot -and then the plot was printed to grayscale, a lot of the information may map to -the same gray values. The Diverging colormaps mostly vary from darker gray on -the outer edges to white in the middle. Some (PuOr and seismic) have noticably -darker gray on one side than the other and therefore are not very symmetric. -coolwarm has little range of gray scale and would print to a more uniform plot, -losing a lot of detail. Note that overlaid, labeled contours could help -differentiate between one side of the colormap vs. the other since color cannot -be used once a plot is printed to grayscale. Many of the Qualitative and -Miscellaneous colormaps, such as Accent, hsv, and jet, change from darker to -lighter and back to darker gray throughout the colormap. This would make it -impossible for a viewer to interpret the information in a plot once it is -printed in grayscale. - -.. plot:: users/plotting/colormaps/grayscale.py - - -Color vision deficiencies -========================= - -There is a lot of information available about color blindness (*e.g.*, -[colorblindness]_). Additionally, there are tools available to convert images to -how they look for different types of color vision deficiencies (*e.g.*, [asp]_). - -The most common form of color vision deficiency involves differentiating between -red and green. Thus, avoiding colormaps with both red and green will avoid many -problems in general. - - -References -========== - -.. [Ware] http://ccom.unh.edu/sites/default/files/publications/Ware_1988_CGA_Color_sequences_univariate_maps.pdf -.. [Moreland] http://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf -.. [list-colormaps] https://gist.github.com/endolith/2719900#id7 -.. [mycarta-banding] http://mycarta.wordpress.com/2012/10/14/the-rainbow-is-deadlong-live-the-rainbow-part-4-cie-lab-heated-body/ -.. [mycarta-jet] http://mycarta.wordpress.com/2012/10/06/the-rainbow-is-deadlong-live-the-rainbow-part-3/ -.. [mycarta-lablinear] http://mycarta.wordpress.com/2012/12/06/the-rainbow-is-deadlong-live-the-rainbow-part-5-cie-lab-linear-l-rainbow/ -.. [mycarta-cubelaw] http://mycarta.wordpress.com/2013/02/21/perceptual-rainbow-palette-the-method/ -.. [bw] http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ -.. [colorblindness] http://aspnetresources.com/tools/colorBlindness -.. [asp] http://aspnetresources.com/tools/colorBlindness -.. [IBM] http://www.research.ibm.com/people/l/lloydt/color/color.HTM diff --git a/doc/users/colors.rst b/doc/users/colors.rst deleted file mode 100644 index 18a5756f0ab5..000000000000 --- a/doc/users/colors.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. _colors: - -***************** -Specifying Colors -***************** - -In almost all places in matplotlib where a color can be specified by the user -it can be provided as: - -* an RGB or RGBA tuple of float values in ``[0, 1]`` - (e.g., ``(0.1, 0.2, 0.5)`` or ``(0.1, 0.2, 0.5, 0.3)``) -* a hex RGB or RGBA string (e.g., ``'#0F0F0F'`` or ``'#0F0F0F0F'``) -* a string representation of a float value in ``[0, 1]`` - inclusive for gray level (e.g., ``'0.5'``) -* one of ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}`` -* a X11/CSS4 color name -* a name from the `xkcd color survey `__ - prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``) -* one of ``{'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9'}`` - -All string specifications of color are case-insensitive. - - -``'CN'`` color selection ------------------------- - -Color can be specified by a string matching the regex ``C[0-9]``. -This can be passed any place that a color is currently accepted and -can be used as a 'single character color' in format-string to -`matplotlib.Axes.plot`. - -The single digit is the index into the default property cycle -(``matplotlib.rcParams['axes.prop_cycle']``). If the property cycle does not -include ``'color'`` then black is returned. The color is evaluated when the -artist is created. For example, - -.. plot:: - :include-source: True - - import numpy as np - import matplotlib.pyplot as plt - import matplotlib as mpl - th = np.linspace(0, 2*np.pi, 128) - - def demo(sty): - mpl.style.use(sty) - fig, ax = plt.subplots(figsize=(3, 3)) - - ax.set_title('style: {!r}'.format(sty), color='C0') - - ax.plot(th, np.cos(th), 'C1', label='C1') - ax.plot(th, np.sin(th), 'C2', label='C2') - ax.legend() - - demo('default') - demo('seaborn') - -will use the first color for the title and then plot using the second -and third colors of each style's ``mpl.rcParams['axes.prop_cycle']``. - - -xkcd v X11/CSS4 ---------------- - -The xkcd colors are derived from a user survey conducted by the -webcomic xkcd. `Details of the survey are available on the xkcd blog -`__. - -Out of 148 colors in the CSS color list, there are 95 name collisions -between the X11/CSS4 names and the xkcd names, all but 3 of which have -different hex values. For example ``'blue'`` maps to ``'#0000FF'`` -where as ``'xkcd:blue'`` maps to ``'#0343DF'``. Due to these name -collisions all of the xkcd colors have ``'xkcd:'`` prefixed. As noted in -the blog post, while it might be interesting to re-define the X11/CSS4 names -based on such a survey, we do not do so unilaterally. - -The name collisions are shown in the table below; the color names -where the hex values agree are shown in bold. - - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib._color_data as mcd - import matplotlib.patches as mpatch - - overlap = {name for name in mcd.CSS4_COLORS - if "xkcd:" + name in mcd.XKCD_COLORS} - - fig = plt.figure(figsize=[4.8, 16]) - ax = fig.add_axes([0, 0, 1, 1]) - - for j, n in enumerate(sorted(overlap, reverse=True)): - weight = None - cn = mcd.CSS4_COLORS[n] - xkcd = mcd.XKCD_COLORS["xkcd:" + n].upper() - if cn == xkcd: - weight = 'bold' - - r1 = mpatch.Rectangle((0, j), 1, 1, color=cn) - r2 = mpatch.Rectangle((1, j), 1, 1, color=xkcd) - txt = ax.text(2, j+.5, ' ' + n, va='center', fontsize=10, - weight=weight) - ax.add_patch(r1) - ax.add_patch(r2) - ax.axhline(j, color='k') - - ax.text(.5, j + 1.5, 'X11', ha='center', va='center') - ax.text(1.5, j + 1.5, 'xkcd', ha='center', va='center') - ax.set_xlim(0, 3) - ax.set_ylim(0, j + 2) - ax.axis('off') diff --git a/doc/users/credits.rst b/doc/users/credits.rst deleted file mode 100644 index 611defd97c66..000000000000 --- a/doc/users/credits.rst +++ /dev/null @@ -1,218 +0,0 @@ -.. _credits: - -******* -Credits -******* - - -matplotlib was written by John Hunter and is now developed and -maintained by a number of `active -`_ developers. -The current co-lead developers of matplotlib are Michael Droettboom -and Thomas A. Caswell. - -Special thanks to those who have made valuable contributions (roughly -in order of first contribution by date). Any list like this is bound -to be incomplete and can't capture the thousands and thousands of -contributions over the years from these and others: - -Jeremy O'Donoghue - wrote the wx backend - -Andrew Straw - Provided much of the log scaling architecture, the fill command, PIL - support for imshow, and provided many examples. He also wrote the - support for dropped axis spines and the original buildbot unit - testing infrastructure which triggered the JPL/James Evans platform - specific builds and regression test image comparisons from svn - matplotlib across platforms on svn commits. - -Charles Twardy - provided the impetus code for the legend class and has made - countless bug reports and suggestions for improvement. - -Gary Ruben - made many enhancements to errorbar to support x and y - errorbar plots, and added a number of new marker types to plot. - -John Gill - wrote the table class and examples, helped with support for - auto-legend placement, and added support for legending scatter - plots. - -David Moore - wrote the paint backend (no longer used) - -Todd Miller - supported by `STSCI `_ contributed the TkAgg - backend and the numerix module, which allows matplotlib to work with - either numeric or numarray. He also ported image support to the - postscript backend, with much pain and suffering. - -Paul Barrett - supported by `STSCI `_ overhauled font - management to provide an improved, free-standing, platform - independent font manager with a WC3 compliant font finder and cache - mechanism and ported truetype and mathtext to PS. - -Perry Greenfield - supported by `STSCI `_ overhauled and - modernized the goals and priorities page, implemented an improved - colormap framework, and has provided many suggestions and a lot of - insight to the overall design and organization of matplotlib. - -Jared Wahlstrand - wrote the initial SVG backend. - -Steve Chaplin - served as the GTK maintainer and wrote the Cairo and - GTKCairo backends. - -Jim Benson - provided the patch to handle vertical mathttext. - -Gregory Lielens - provided the FltkAgg backend and several patches for the frontend, - including contributions to toolbar2, and support for log ticking - with alternate bases and major and minor log ticking. - -Darren Dale - - did the work to do mathtext exponential labeling for log plots, - added improved support for scalar formatting, and did the lions - share of the `psfrag - `_ - LaTeX support for postscript. He has made substantial contributions - to extending and maintaining the PS and Qt backends, and wrote the - site.cfg and matplotlib.conf build and runtime configuration - support. He setup the infrastructure for the sphinx documentation - that powers the mpl docs. - -Paul Mcguire - provided the pyparsing module on which mathtext relies, and made a - number of optimizations to the matplotlib mathtext grammar. - - -Fernando Perez - has provided numerous bug reports and patches for cleaning up - backend imports and expanding pylab functionality, and provided - matplotlib support in the pylab mode for `ipython - `_. He also provided the - :func:`~matplotlib.pyplot.matshow` command, and wrote TConfig, which - is the basis for the experimental traited mpl configuration. - -Andrew Dalke - of `Dalke Scientific Software `_ contributed the - strftime formatting code to handle years earlier than 1900. - -Jochen Voss - served as PS backend maintainer and has contributed several - bugfixes. - -Nadia Dencheva - - supported by `STSCI `_ provided the contouring and - contour labeling code. - -Baptiste Carvello - provided the key ideas in a patch for proper - shared axes support that underlies ganged plots and multiscale - plots. - -Jeffrey Whitaker - at `NOAA `_ wrote the - :ref:`toolkit_basemap` toolkit - -Sigve Tjoraand, Ted Drain, James Evans - and colleagues at the `JPL `_ collaborated - on the QtAgg backend and sponsored development of a number of - features including custom unit types, datetime support, scale free - ellipses, broken bar plots and more. The JPL team wrote the unit - testing image comparison `infrastructure - `_ - for regression test image comparisons. - -James Amundson - did the initial work porting the qt backend to qt4 - -Eric Firing - has contributed significantly to contouring, masked - array, pcolor, image and quiver support, in addition to ongoing - support and enhancements in performance, design and code quality in - most aspects of matplotlib. - -Daishi Harada - added support for "Dashed Text". See `dashpointlabel.py - <../examples/pylab_examples/dashpointlabel.html>`_ and - :class:`~matplotlib.text.TextWithDash`. - -Nicolas Young - added support for byte images to imshow, which are - more efficient in CPU and memory, and added support for irregularly - sampled images. - -The `brainvisa `_ Orsay team and Fernando Perez - added Qt support to `ipython `_ in pylab mode. - - -Charlie Moad - contributed work to matplotlib's Cocoa support and has done a lot of work on the OSX and win32 binary releases. - -Jouni K. Seppänen - wrote the PDF backend and contributed numerous - fixes to the code, to tex support and to the get_sample_data handler - -Paul Kienzle - improved the picking infrastructure for interactive plots, and with - Alex Mont contributed fast rendering code for quadrilateral meshes. - -Michael Droettboom - supported by `STSCI `_ wrote the enhanced - mathtext support, implementing Knuth's box layout algorithms, saving - to file-like objects across backends, and is responsible for - numerous bug-fixes, much better font and unicode support, and - feature and performance enhancements across the matplotlib code - base. He also rewrote the transformation infrastructure to support - custom projections and scales. - -John Porter, Jonathon Taylor and Reinier Heeres - John Porter wrote the mplot3d module for basic 3D plotting in - matplotlib, and Jonathon Taylor and Reinier Heeres ported it to the - refactored transform trunk. - -Jae-Joon Lee - Implemented fancy arrows and boxes, rewrote the legend - support to handle multiple columns and fancy text boxes, wrote the - axes grid toolkit, and has made numerous contributions to the code - and documentation - -Paul Ivanov - Has worked on getting matplotlib integrated better with other tools, - such as Sage and IPython, and getting the test infrastructure - faster, lighter and meaner. Listen to his podcast. - -Tony Yu - Has been involved in matplotlib since the early days, and recently - has contributed stream plotting among many other improvements. He - is the author of mpltools. - -Michiel de Hoon - Wrote and maintains the macosx backend. - -Ian Thomas - Contributed, among other things, the triangulation (tricolor and - tripcontour) methods. - -Benjamin Root - Has significantly improved the capabilities of the 3D plotting. He - has improved matplotlib's documentation and code quality throughout, - and does invaluable triaging of pull requests and bugs. - -Phil Elson - Fixed some deep-seated bugs in the transforms framework, and has - been laser-focused on improving polish throughout matplotlib, - tackling things that have been considered to large and daunting for - a long time. - -Damon McDougall - Added triangulated 3D surfaces and stack plots to matplotlib. diff --git a/doc/users/customizing.rst b/doc/users/customizing.rst deleted file mode 100644 index 4e2c0c9b8cd3..000000000000 --- a/doc/users/customizing.rst +++ /dev/null @@ -1,182 +0,0 @@ -.. _customizing-matplotlib: - -======================== - Customizing matplotlib -======================== - -Using style sheets -================== - -The ``style`` package adds support for easy-to-switch plotting "styles" with -the same parameters as a matplotlibrc_ file (which is read at startup to -configure matplotlib). - -There are a number of pre-defined styles provided by matplotlib. For -example, there's a pre-defined style called "ggplot", which emulates the -aesthetics of ggplot_ (a popular plotting package for R_). To use this style, -just add:: - - >>> import matplotlib.pyplot as plt - >>> plt.style.use('ggplot') - -To list all available styles, use:: - - >>> print(plt.style.available) - - -Defining your own style ------------------------ - -You can create custom styles and use them by calling ``style.use`` with the -path or URL to the style sheet. Additionally, if you add your -``.mplstyle`` file to ``mpl_configdir/stylelib``, you can reuse -your custom style sheet with a call to ``style.use()``. By default -``mpl_configdir`` should be ``~/.config/matplotlib``, but you can check where -yours is with ``matplotlib.get_configdir()``; you may need to create this -directory. You also can change the directory where matplotlib looks for -the stylelib/ folder by setting the MPLCONFIGDIR environment variable, -see :ref:`locating-matplotlib-config-dir`. - -Note that a custom style sheet in ``mpl_configdir/stylelib`` will -override a style sheet defined by matplotlib if the styles have the same name. - -For example, you might want to create -``mpl_configdir/stylelib/presentation.mplstyle`` with the following:: - - axes.titlesize : 24 - axes.labelsize : 20 - lines.linewidth : 3 - lines.markersize : 10 - xtick.labelsize : 16 - ytick.labelsize : 16 - -Then, when you want to adapt a plot designed for a paper to one that looks -good in a presentation, you can just add:: - - >>> import matplotlib.pyplot as plt - >>> plt.style.use('presentation') - - -Composing styles ----------------- - -Style sheets are designed to be composed together. So you can have a style -sheet that customizes colors and a separate style sheet that alters element -sizes for presentations. These styles can easily be combined by passing -a list of styles:: - - >>> import matplotlib.pyplot as plt - >>> plt.style.use(['dark_background', 'presentation']) - -Note that styles further to the right will overwrite values that are already -defined by styles on the left. - - -Temporary styling ------------------ - -If you only want to use a style for a specific block of code but don't want -to change the global styling, the style package provides a context manager -for limiting your changes to a specific scope. To isolate your styling -changes, you can write something like the following:: - - >>> import numpy as np - >>> import matplotlib.pyplot as plt - >>> - >>> with plt.style.context(('dark_background')): - >>> plt.plot(np.sin(np.linspace(0, 2 * np.pi)), 'r-o') - >>> - >>> # Some plotting code with the default style - >>> - >>> plt.show() - - -matplotlib rcParams -=================== - -.. _customizing-with-dynamic-rc-settings: - -Dynamic rc settings -------------------- - -You can also dynamically change the default rc settings in a python script or -interactively from the python shell. All of the rc settings are stored in a -dictionary-like variable called :data:`matplotlib.rcParams`, which is global to -the matplotlib package. rcParams can be modified directly, for example:: - - import matplotlib as mpl - mpl.rcParams['lines.linewidth'] = 2 - mpl.rcParams['lines.color'] = 'r' - -Matplotlib also provides a couple of convenience functions for modifying rc -settings. The :func:`matplotlib.rc` command can be used to modify multiple -settings in a single group at once, using keyword arguments:: - - import matplotlib as mpl - mpl.rc('lines', linewidth=2, color='r') - -The :func:`matplotlib.rcdefaults` command will restore the standard matplotlib -default settings. - -There is some degree of validation when setting the values of rcParams, see -:mod:`matplotlib.rcsetup` for details. - -.. _customizing-with-matplotlibrc-files: - -The :file:`matplotlibrc` file ------------------------------ - -matplotlib uses :file:`matplotlibrc` configuration files to customize all kinds -of properties, which we call `rc settings` or `rc parameters`. You can control -the defaults of almost every property in matplotlib: figure size and dpi, line -width, color and style, axes, axis and grid properties, text and font -properties and so on. matplotlib looks for :file:`matplotlibrc` in four -locations, in the following order: - -1. :file:`matplotlibrc` in the current working directory, usually used for - specific customizations that you do not want to apply elsewhere. - -2. :file:`$MATPLOTLIBRC` if it is a file, else :file:`$MATPLOTLIBRC/matplotlibrc`. - -3. It next looks in a user-specific place, depending on your platform: - - - On Linux, it looks in :file:`.config/matplotlib/matplotlibrc` (or - `$XDG_CONFIG_HOME/matplotlib/matplotlibrc`) if you've customized - your environment. - - - On other platforms, it looks in :file:`.matplotlib/matplotlibrc`. - - See :ref:`locating-matplotlib-config-dir`. - -4. :file:`{INSTALL}/matplotlib/mpl-data/matplotlibrc`, where - :file:`{INSTALL}` is something like - :file:`/usr/lib/python3.5/site-packages` on Linux, and maybe - :file:`C:\\Python35\\Lib\\site-packages` on Windows. Every time you - install matplotlib, this file will be overwritten, so if you want - your customizations to be saved, please move this file to your - user-specific matplotlib directory. - -To display where the currently active :file:`matplotlibrc` file was -loaded from, one can do the following:: - - >>> import matplotlib - >>> matplotlib.matplotlib_fname() - '/home/foo/.config/matplotlib/matplotlibrc' - -See below for a sample :ref:`matplotlibrc file`. - -.. _matplotlibrc-sample: - -A sample matplotlibrc file -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. htmlonly:: - - `(download) <../_static/matplotlibrc>`__ - -.. literalinclude:: ../_static/matplotlibrc - - -.. _matplotlibrc: http://matplotlib.org/users/customizing.html -.. _ggplot: http://ggplot2.org/ -.. _R: https://www.r-project.org/ diff --git a/doc/users/event_handling.rst b/doc/users/event_handling.rst deleted file mode 100644 index 21c474bb648e..000000000000 --- a/doc/users/event_handling.rst +++ /dev/null @@ -1,566 +0,0 @@ -.. _event-handling-tutorial: - -************************** -Event handling and picking -************************** - -matplotlib works with a number of user interface toolkits (wxpython, -tkinter, qt4, gtk, and macosx) and in order to support features like -interactive panning and zooming of figures, it is helpful to the -developers to have an API for interacting with the figure via key -presses and mouse movements that is "GUI neutral" so we don't have to -repeat a lot of code across the different user interfaces. Although -the event handling API is GUI neutral, it is based on the GTK model, -which was the first user interface matplotlib supported. The events -that are triggered are also a bit richer vis-a-vis matplotlib than -standard GUI events, including information like which -:class:`matplotlib.axes.Axes` the event occurred in. The events also -understand the matplotlib coordinate system, and report event -locations in both pixel and data coordinates. - -.. _event-connections: - -Event connections -================= - -To receive events, you need to write a callback function and then -connect your function to the event manager, which is part of the -:class:`~matplotlib.backend_bases.FigureCanvasBase`. Here is a simple -example that prints the location of the mouse click and which button -was pressed:: - - fig = plt.figure() - ax = fig.add_subplot(111) - ax.plot(np.random.rand(10)) - - def onclick(event): - print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % - (event.button, event.x, event.y, event.xdata, event.ydata)) - - cid = fig.canvas.mpl_connect('button_press_event', onclick) - -The ``FigureCanvas`` method -:meth:`~matplotlib.backend_bases.FigureCanvasBase.mpl_connect` returns -a connection id which is simply an integer. When you want to -disconnect the callback, just call:: - - fig.canvas.mpl_disconnect(cid) - -.. note:: - The canvas retains only weak references to the callbacks. Therefore - if a callback is a method of a class instance, you need to retain - a reference to that instance. Otherwise the instance will be - garbage-collected and the callback will vanish. - - -Here are the events that you can connect to, the class instances that -are sent back to you when the event occurs, and the event descriptions - - -======================= ====================================================================================== -Event name Class and description -======================= ====================================================================================== -'button_press_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is pressed -'button_release_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse button is released -'draw_event' :class:`~matplotlib.backend_bases.DrawEvent` - canvas draw -'key_press_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is pressed -'key_release_event' :class:`~matplotlib.backend_bases.KeyEvent` - key is released -'motion_notify_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse motion -'pick_event' :class:`~matplotlib.backend_bases.PickEvent` - an object in the canvas is selected -'resize_event' :class:`~matplotlib.backend_bases.ResizeEvent` - figure canvas is resized -'scroll_event' :class:`~matplotlib.backend_bases.MouseEvent` - mouse scroll wheel is rolled -'figure_enter_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse enters a new figure -'figure_leave_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse leaves a figure -'axes_enter_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse enters a new axes -'axes_leave_event' :class:`~matplotlib.backend_bases.LocationEvent` - mouse leaves an axes -======================= ====================================================================================== - -.. _event-attributes: - -Event attributes -================ - -All matplotlib events inherit from the base class -:class:`matplotlib.backend_bases.Event`, which store the attributes: - - ``name`` - the event name - - ``canvas`` - the FigureCanvas instance generating the event - - ``guiEvent`` - the GUI event that triggered the matplotlib event - - -The most common events that are the bread and butter of event handling -are key press/release events and mouse press/release and movement -events. The :class:`~matplotlib.backend_bases.KeyEvent` and -:class:`~matplotlib.backend_bases.MouseEvent` classes that handle -these events are both derived from the LocationEvent, which has the -following attributes - - ``x`` - x position - pixels from left of canvas - - ``y`` - y position - pixels from bottom of canvas - - ``inaxes`` - the :class:`~matplotlib.axes.Axes` instance if mouse is over axes - - ``xdata`` - x coord of mouse in data coords - - ``ydata`` - y coord of mouse in data coords - -Let's look a simple example of a canvas, where a simple line segment -is created every time a mouse is pressed:: - - from matplotlib import pyplot as plt - - class LineBuilder: - def __init__(self, line): - self.line = line - self.xs = list(line.get_xdata()) - self.ys = list(line.get_ydata()) - self.cid = line.figure.canvas.mpl_connect('button_press_event', self) - - def __call__(self, event): - print('click', event) - if event.inaxes!=self.line.axes: return - self.xs.append(event.xdata) - self.ys.append(event.ydata) - self.line.set_data(self.xs, self.ys) - self.line.figure.canvas.draw() - - fig = plt.figure() - ax = fig.add_subplot(111) - ax.set_title('click to build line segments') - line, = ax.plot([0], [0]) # empty line - linebuilder = LineBuilder(line) - - plt.show() - - -The :class:`~matplotlib.backend_bases.MouseEvent` that we just used is a -:class:`~matplotlib.backend_bases.LocationEvent`, so we have access to -the data and pixel coordinates in event.x and event.xdata. In -addition to the ``LocationEvent`` attributes, it has - - ``button`` - button pressed None, 1, 2, 3, 'up', 'down' (up and down are used for scroll events) - - ``key`` - the key pressed: None, any character, 'shift', 'win', or 'control' - -Draggable rectangle exercise ----------------------------- - -Write draggable rectangle class that is initialized with a -:class:`~matplotlib.patches.Rectangle` instance but will move its x,y -location when dragged. Hint: you will need to store the original -``xy`` location of the rectangle which is stored as rect.xy and -connect to the press, motion and release mouse events. When the mouse -is pressed, check to see if the click occurs over your rectangle (see -:meth:`matplotlib.patches.Rectangle.contains`) and if it does, store -the rectangle xy and the location of the mouse click in data coords. -In the motion event callback, compute the deltax and deltay of the -mouse movement, and add those deltas to the origin of the rectangle -you stored. The redraw the figure. On the button release event, just -reset all the button press data you stored as None. - -Here is the solution:: - - import numpy as np - import matplotlib.pyplot as plt - - class DraggableRectangle: - def __init__(self, rect): - self.rect = rect - self.press = None - - def connect(self): - 'connect to all the events we need' - self.cidpress = self.rect.figure.canvas.mpl_connect( - 'button_press_event', self.on_press) - self.cidrelease = self.rect.figure.canvas.mpl_connect( - 'button_release_event', self.on_release) - self.cidmotion = self.rect.figure.canvas.mpl_connect( - 'motion_notify_event', self.on_motion) - - def on_press(self, event): - 'on button press we will see if the mouse is over us and store some data' - if event.inaxes != self.rect.axes: return - - contains, attrd = self.rect.contains(event) - if not contains: return - print('event contains', self.rect.xy) - x0, y0 = self.rect.xy - self.press = x0, y0, event.xdata, event.ydata - - def on_motion(self, event): - 'on motion we will move the rect if the mouse is over us' - if self.press is None: return - if event.inaxes != self.rect.axes: return - x0, y0, xpress, ypress = self.press - dx = event.xdata - xpress - dy = event.ydata - ypress - #print('x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f' % - # (x0, xpress, event.xdata, dx, x0+dx)) - self.rect.set_x(x0+dx) - self.rect.set_y(y0+dy) - - self.rect.figure.canvas.draw() - - - def on_release(self, event): - 'on release we reset the press data' - self.press = None - self.rect.figure.canvas.draw() - - def disconnect(self): - 'disconnect all the stored connection ids' - self.rect.figure.canvas.mpl_disconnect(self.cidpress) - self.rect.figure.canvas.mpl_disconnect(self.cidrelease) - self.rect.figure.canvas.mpl_disconnect(self.cidmotion) - - fig = plt.figure() - ax = fig.add_subplot(111) - rects = ax.bar(range(10), 20*np.random.rand(10)) - drs = [] - for rect in rects: - dr = DraggableRectangle(rect) - dr.connect() - drs.append(dr) - - plt.show() - - -**Extra credit**: use the animation blit techniques discussed in the -`animations recipe -`_ to make the -animated drawing faster and smoother. - -Extra credit solution:: - - # draggable rectangle with the animation blit techniques; see - # http://www.scipy.org/Cookbook/Matplotlib/Animations - import numpy as np - import matplotlib.pyplot as plt - - class DraggableRectangle: - lock = None # only one can be animated at a time - def __init__(self, rect): - self.rect = rect - self.press = None - self.background = None - - def connect(self): - 'connect to all the events we need' - self.cidpress = self.rect.figure.canvas.mpl_connect( - 'button_press_event', self.on_press) - self.cidrelease = self.rect.figure.canvas.mpl_connect( - 'button_release_event', self.on_release) - self.cidmotion = self.rect.figure.canvas.mpl_connect( - 'motion_notify_event', self.on_motion) - - def on_press(self, event): - 'on button press we will see if the mouse is over us and store some data' - if event.inaxes != self.rect.axes: return - if DraggableRectangle.lock is not None: return - contains, attrd = self.rect.contains(event) - if not contains: return - print('event contains', self.rect.xy) - x0, y0 = self.rect.xy - self.press = x0, y0, event.xdata, event.ydata - DraggableRectangle.lock = self - - # draw everything but the selected rectangle and store the pixel buffer - canvas = self.rect.figure.canvas - axes = self.rect.axes - self.rect.set_animated(True) - canvas.draw() - self.background = canvas.copy_from_bbox(self.rect.axes.bbox) - - # now redraw just the rectangle - axes.draw_artist(self.rect) - - # and blit just the redrawn area - canvas.blit(axes.bbox) - - def on_motion(self, event): - 'on motion we will move the rect if the mouse is over us' - if DraggableRectangle.lock is not self: - return - if event.inaxes != self.rect.axes: return - x0, y0, xpress, ypress = self.press - dx = event.xdata - xpress - dy = event.ydata - ypress - self.rect.set_x(x0+dx) - self.rect.set_y(y0+dy) - - canvas = self.rect.figure.canvas - axes = self.rect.axes - # restore the background region - canvas.restore_region(self.background) - - # redraw just the current rectangle - axes.draw_artist(self.rect) - - # blit just the redrawn area - canvas.blit(axes.bbox) - - def on_release(self, event): - 'on release we reset the press data' - if DraggableRectangle.lock is not self: - return - - self.press = None - DraggableRectangle.lock = None - - # turn off the rect animation property and reset the background - self.rect.set_animated(False) - self.background = None - - # redraw the full figure - self.rect.figure.canvas.draw() - - def disconnect(self): - 'disconnect all the stored connection ids' - self.rect.figure.canvas.mpl_disconnect(self.cidpress) - self.rect.figure.canvas.mpl_disconnect(self.cidrelease) - self.rect.figure.canvas.mpl_disconnect(self.cidmotion) - - fig = plt.figure() - ax = fig.add_subplot(111) - rects = ax.bar(range(10), 20*np.random.rand(10)) - drs = [] - for rect in rects: - dr = DraggableRectangle(rect) - dr.connect() - drs.append(dr) - - plt.show() - - -.. _enter-leave-events: - -Mouse enter and leave -====================== - -If you want to be notified when the mouse enters or leaves a figure or -axes, you can connect to the figure/axes enter/leave events. Here is -a simple example that changes the colors of the axes and figure -background that the mouse is over:: - - """ - Illustrate the figure and axes enter and leave events by changing the - frame colors on enter and leave - """ - import matplotlib.pyplot as plt - - def enter_axes(event): - print('enter_axes', event.inaxes) - event.inaxes.patch.set_facecolor('yellow') - event.canvas.draw() - - def leave_axes(event): - print('leave_axes', event.inaxes) - event.inaxes.patch.set_facecolor('white') - event.canvas.draw() - - def enter_figure(event): - print('enter_figure', event.canvas.figure) - event.canvas.figure.patch.set_facecolor('red') - event.canvas.draw() - - def leave_figure(event): - print('leave_figure', event.canvas.figure) - event.canvas.figure.patch.set_facecolor('grey') - event.canvas.draw() - - fig1 = plt.figure() - fig1.suptitle('mouse hover over figure or axes to trigger events') - ax1 = fig1.add_subplot(211) - ax2 = fig1.add_subplot(212) - - fig1.canvas.mpl_connect('figure_enter_event', enter_figure) - fig1.canvas.mpl_connect('figure_leave_event', leave_figure) - fig1.canvas.mpl_connect('axes_enter_event', enter_axes) - fig1.canvas.mpl_connect('axes_leave_event', leave_axes) - - fig2 = plt.figure() - fig2.suptitle('mouse hover over figure or axes to trigger events') - ax1 = fig2.add_subplot(211) - ax2 = fig2.add_subplot(212) - - fig2.canvas.mpl_connect('figure_enter_event', enter_figure) - fig2.canvas.mpl_connect('figure_leave_event', leave_figure) - fig2.canvas.mpl_connect('axes_enter_event', enter_axes) - fig2.canvas.mpl_connect('axes_leave_event', leave_axes) - - plt.show() - - -.. _object-picking: - -Object picking -============== - -You can enable picking by setting the ``picker`` property of an -:class:`~matplotlib.artist.Artist` (e.g., a matplotlib -:class:`~matplotlib.lines.Line2D`, :class:`~matplotlib.text.Text`, -:class:`~matplotlib.patches.Patch`, :class:`~matplotlib.patches.Polygon`, -:class:`~matplotlib.patches.AxesImage`, etc...) - -There are a variety of meanings of the ``picker`` property: - - ``None`` - picking is disabled for this artist (default) - - ``boolean`` - if True then picking will be enabled and the artist will fire a - pick event if the mouse event is over the artist - - ``float`` - if picker is a number it is interpreted as an epsilon tolerance in - points and the artist will fire off an event if its data is - within epsilon of the mouse event. For some artists like lines - and patch collections, the artist may provide additional data to - the pick event that is generated, e.g., the indices of the data - within epsilon of the pick event. - - ``function`` - if picker is callable, it is a user supplied function which - determines whether the artist is hit by the mouse event. The - signature is ``hit, props = picker(artist, mouseevent)`` to - determine the hit test. If the mouse event is over the artist, - return ``hit=True`` and props is a dictionary of properties you - want added to the :class:`~matplotlib.backend_bases.PickEvent` - attributes - - -After you have enabled an artist for picking by setting the ``picker`` -property, you need to connect to the figure canvas pick_event to get -pick callbacks on mouse press events. e.g.:: - - def pick_handler(event): - mouseevent = event.mouseevent - artist = event.artist - # now do something with this... - - -The :class:`~matplotlib.backend_bases.PickEvent` which is passed to -your callback is always fired with two attributes: - - ``mouseevent`` the mouse event that generate the pick event. The - mouse event in turn has attributes like ``x`` and ``y`` (the - coords in display space, e.g., pixels from left, bottom) and xdata, - ydata (the coords in data space). Additionally, you can get - information about which buttons were pressed, which keys were - pressed, which :class:`~matplotlib.axes.Axes` the mouse is over, - etc. See :class:`matplotlib.backend_bases.MouseEvent` for - details. - - ``artist`` - the :class:`~matplotlib.artist.Artist` that generated the pick - event. - -Additionally, certain artists like :class:`~matplotlib.lines.Line2D` -and :class:`~matplotlib.collections.PatchCollection` may attach -additional meta data like the indices into the data that meet the -picker criteria (e.g., all the points in the line that are within the -specified epsilon tolerance) - -Simple picking example ----------------------- - -In the example below, we set the line picker property to a scalar, so -it represents a tolerance in points (72 points per inch). The onpick -callback function will be called when the pick event it within the -tolerance distance from the line, and has the indices of the data -vertices that are within the pick distance tolerance. Our onpick -callback function simply prints the data that are under the pick -location. Different matplotlib Artists can attach different data to -the PickEvent. For example, ``Line2D`` attaches the ind property, -which are the indices into the line data under the pick point. See -:meth:`~matplotlib.lines.Line2D.pick` for details on the ``PickEvent`` -properties of the line. Here is the code:: - - import numpy as np - import matplotlib.pyplot as plt - - fig = plt.figure() - ax = fig.add_subplot(111) - ax.set_title('click on points') - - line, = ax.plot(np.random.rand(100), 'o', picker=5) # 5 points tolerance - - def onpick(event): - thisline = event.artist - xdata = thisline.get_xdata() - ydata = thisline.get_ydata() - ind = event.ind - points = tuple(zip(xdata[ind], ydata[ind])) - print('onpick points:', points) - - fig.canvas.mpl_connect('pick_event', onpick) - - plt.show() - - -Picking exercise ----------------- - -Create a data set of 100 arrays of 1000 Gaussian random numbers and -compute the sample mean and standard deviation of each of them (hint: -numpy arrays have a mean and std method) and make a xy marker plot of -the 100 means vs the 100 standard deviations. Connect the line -created by the plot command to the pick event, and plot the original -time series of the data that generated the clicked on points. If more -than one point is within the tolerance of the clicked on point, you -can use multiple subplots to plot the multiple time series. - -Exercise solution:: - - """ - compute the mean and stddev of 100 data sets and plot mean vs stddev. - When you click on one of the mu, sigma points, plot the raw data from - the dataset that generated the mean and stddev - """ - import numpy as np - import matplotlib.pyplot as plt - - X = np.random.rand(100, 1000) - xs = np.mean(X, axis=1) - ys = np.std(X, axis=1) - - fig = plt.figure() - ax = fig.add_subplot(111) - ax.set_title('click on point to plot time series') - line, = ax.plot(xs, ys, 'o', picker=5) # 5 points tolerance - - - def onpick(event): - - if event.artist!=line: return True - - N = len(event.ind) - if not N: return True - - - figi = plt.figure() - for subplotnum, dataind in enumerate(event.ind): - ax = figi.add_subplot(N,1,subplotnum+1) - ax.plot(X[dataind]) - ax.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f'%(xs[dataind], ys[dataind]), - transform=ax.transAxes, va='top') - ax.set_ylim(-0.5, 1.5) - figi.show() - return True - - fig.canvas.mpl_connect('pick_event', onpick) - - plt.show() diff --git a/doc/users/examples_index.rst b/doc/users/examples_index.rst deleted file mode 100644 index e71c1db6d8a8..000000000000 --- a/doc/users/examples_index.rst +++ /dev/null @@ -1,8 +0,0 @@ -=================== - Selected Examples -=================== - -.. toctree:: - - screenshots.rst - recipes.rst diff --git a/doc/users/faq.rst b/doc/users/faq.rst new file mode 100644 index 000000000000..5aec1e08fb14 --- /dev/null +++ b/doc/users/faq.rst @@ -0,0 +1,405 @@ +.. _howto-faq: + +.. redirect-from:: /faq/howto_faq +.. redirect-from:: /users/faq/howto_faq +.. redirect-from:: /faq/index + +========================== +Frequently Asked Questions +========================== + +.. _how-do-no-figure: + +I don't see a figure window +--------------------------- + +Please see :ref:`figures-not-showing`. + +.. _how-to-too-many-ticks: + +Why do I have so many ticks, and/or why are they out of order? +-------------------------------------------------------------- + +One common cause for unexpected tick behavior is passing a *list of strings +instead of numbers or datetime objects*. This can easily happen without notice +when reading in a comma-delimited text file. Matplotlib treats lists of strings +as *categorical* variables +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default +puts one tick per category, and plots them in the order in which they are +supplied. + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + fig, ax = plt.subplots(1, 2, layout='constrained', figsize=(6, 2)) + + ax[0].set_title('Ticks seem out of order / misplaced') + x = ['5', '20', '1', '9'] # strings + y = [5, 20, 1, 9] + ax[0].plot(x, y, 'd') + ax[0].tick_params(axis='x', labelcolor='red', labelsize=14) + + ax[1].set_title('Many ticks') + x = [str(xx) for xx in np.arange(100)] # strings + y = np.arange(100) + ax[1].plot(x, y) + ax[1].tick_params(axis='x', labelcolor='red', labelsize=14) + +The solution is to convert the list of strings to numbers or +datetime objects (often ``np.asarray(numeric_strings, dtype='float')`` or +``np.asarray(datetime_strings, dtype='datetime64[s]')``). + +For more information see :doc:`/gallery/ticks/ticks_too_many`. + +.. _howto-determine-artist-extent: + +Determine the extent of Artists in the Figure +--------------------------------------------- + +Sometimes we want to know the extent of an Artist. Matplotlib `.Artist` objects +have a method `.Artist.get_window_extent` that will usually return the extent of +the artist in pixels. However, some artists, in particular text, must be +rendered at least once before their extent is known. Matplotlib supplies +`.Figure.draw_without_rendering`, which should be called before calling +``get_window_extent``. + +.. _howto-figure-empty: + +Check whether a figure is empty +------------------------------- +Empty can actually mean different things. Does the figure contain any artists? +Does a figure with an empty `~.axes.Axes` still count as empty? Is the figure +empty if it was rendered pure white (there may be artists present, but they +could be outside the drawing area or transparent)? + +For the purpose here, we define empty as: "The figure does not contain any +artists except its background patch." The exception for the background is +necessary, because by default every figure contains a `.Rectangle` as its +background patch. This definition could be checked via:: + + def is_empty(figure): + """ + Return whether the figure contains no Artists (other than the default + background patch). + """ + contained_artists = figure.get_children() + return len(contained_artists) <= 1 + +We've decided not to include this as a figure method because this is only one +way of defining empty, and checking the above is only rarely necessary. +Whether or not something has been added to the figure is usually defined +within the context of the program. + +The only reliable way to check whether a figure would render empty is to +actually perform such a rendering and inspect the result. + +.. _howto-findobj: + +Find all objects in a figure of a certain type +---------------------------------------------- + +Every Matplotlib artist (see :ref:`artists_tutorial`) has a method +called :meth:`~matplotlib.artist.Artist.findobj` that can be used to +recursively search the artist for any artists it may contain that meet +some criteria (e.g., match all :class:`~matplotlib.lines.Line2D` +instances or match some arbitrary filter function). For example, the +following snippet finds every object in the figure which has a +``set_color`` property and makes the object blue:: + + def myfunc(x): + return hasattr(x, 'set_color') + + for o in fig.findobj(myfunc): + o.set_color('blue') + +You can also filter on class instances:: + + import matplotlib.text as text + for o in fig.findobj(text.Text): + o.set_fontstyle('italic') + +.. _howto-suppress_offset: + +Prevent ticklabels from having an offset +---------------------------------------- +The default formatter will use an offset to reduce +the length of the ticklabels. To turn this feature +off on a per-axis basis:: + + ax.xaxis.get_major_formatter().set_useOffset(False) + +set :rc:`axes.formatter.useoffset`, or use a different +formatter. See :mod:`~matplotlib.ticker` for details. + +.. _howto-transparent: + +Save transparent figures +------------------------ + +The :meth:`~matplotlib.pyplot.savefig` command has a keyword argument +*transparent* which, if 'True', will make the figure and axes +backgrounds transparent when saving, but will not affect the displayed +image on the screen. + +If you need finer grained control, e.g., you do not want full transparency +or you want to affect the screen displayed version as well, you can set +the alpha properties directly. The figure has a +:class:`~matplotlib.patches.Rectangle` instance called *patch* +and the axes has a Rectangle instance called *patch*. You can set +any property on them directly (*facecolor*, *edgecolor*, *linewidth*, +*linestyle*, *alpha*). e.g.:: + + fig = plt.figure() + fig.patch.set_alpha(0.5) + ax = fig.add_subplot(111) + ax.patch.set_alpha(0.5) + +If you need *all* the figure elements to be transparent, there is +currently no global alpha setting, but you can set the alpha channel +on individual elements, e.g.:: + + ax.plot(x, y, alpha=0.5) + ax.set_xlabel('volts', alpha=0.5) + +.. _howto-multipage: + +Save multiple plots to one pdf file +----------------------------------- + +Many image file formats can only have one image per file, but some formats +support multi-page files. Currently, Matplotlib only provides multi-page +output to pdf files, using either the pdf or pgf backends, via the +`.backend_pdf.PdfPages` and `.backend_pgf.PdfPages` classes. + +.. _howto-auto-adjust: + +Make room for tick labels +------------------------- + +By default, Matplotlib uses fixed percentage margins around subplots. This can +lead to labels overlapping or being cut off at the figure boundary. There are +multiple ways to fix this: + +- Manually adapt the subplot parameters using `.Figure.subplots_adjust` / + `.pyplot.subplots_adjust`. +- Use one of the automatic layout mechanisms: + + - constrained layout (:ref:`constrainedlayout_guide`) + - tight layout (:ref:`tight_layout_guide`) + +- Calculate good values from the size of the plot elements yourself + (:doc:`/gallery/subplots_axes_and_figures/auto_subplots_adjust`) + +.. _howto-align-label: + +Align my ylabels across multiple subplots +----------------------------------------- + +If you have multiple subplots over one another, and the y data have +different scales, you can often get ylabels that do not align +vertically across the multiple subplots, which can be unattractive. +By default, Matplotlib positions the x location of the ylabel so that +it does not overlap any of the y ticks. You can override this default +behavior by specifying the coordinates of the label. To learn how, see +:doc:`/gallery/subplots_axes_and_figures/align_labels_demo` + +.. _howto-set-zorder: + +Control the draw order of plot elements +--------------------------------------- + +The draw order of plot elements, and thus which elements will be on top, is +determined by the `~.Artist.set_zorder` property. +See :doc:`/gallery/misc/zorder_demo` for a detailed description. + +.. _howto-axis-equal: + +Make the aspect ratio for plots equal +------------------------------------- + +The Axes property :meth:`~matplotlib.axes.Axes.set_aspect` controls the +aspect ratio of the axes. You can set it to be 'auto', 'equal', or +some ratio which controls the ratio:: + + ax = fig.add_subplot(111, aspect='equal') + +.. only:: html + + See :doc:`/gallery/subplots_axes_and_figures/axis_equal_demo` for a + complete example. + +.. _howto-twoscale: + +Draw multiple y-axis scales +--------------------------- + +A frequent request is to have two scales for the left and right +y-axis, which is possible using :func:`~matplotlib.pyplot.twinx` (more +than two scales are not currently supported, though it is on the wish +list). This works pretty well, though there are some quirks when you +are trying to interactively pan and zoom, because both scales do not get +the signals. + +The approach uses :func:`~matplotlib.pyplot.twinx` (and its sister +:func:`~matplotlib.pyplot.twiny`) to use *2 different axes*, +turning the axes rectangular frame off on the 2nd axes to keep it from +obscuring the first, and manually setting the tick locs and labels as +desired. You can use separate ``matplotlib.ticker`` formatters and +locators as desired because the two axes are independent. + +.. plot:: + + import numpy as np + import matplotlib.pyplot as plt + + fig = plt.figure() + ax1 = fig.add_subplot(111) + t = np.arange(0.01, 10.0, 0.01) + s1 = np.exp(t) + ax1.plot(t, s1, 'b-') + ax1.set_xlabel('time (s)') + ax1.set_ylabel('exp') + + ax2 = ax1.twinx() + s2 = np.sin(2*np.pi*t) + ax2.plot(t, s2, 'r.') + ax2.set_ylabel('sin') + plt.show() + + +.. only:: html + + See :doc:`/gallery/subplots_axes_and_figures/two_scales` for a + complete example. + +.. _howto-batch: + +Generate images without having a window appear +---------------------------------------------- + +The recommended approach since Matplotlib 3.1 is to explicitly create a Figure +instance:: + + from matplotlib.figure import Figure + fig = Figure() + ax = fig.subplots() + ax.plot([1, 2, 3]) + fig.savefig('myfig.png') + +This prevents any interaction with GUI frameworks and the window manager. + +It's alternatively still possible to use the pyplot interface: instead of +calling `matplotlib.pyplot.show`, call `matplotlib.pyplot.savefig`. In that +case, you must close the figure after saving it. Not closing the figure causes +a memory leak, because pyplot keeps references to all not-yet-shown figures. :: + + import matplotlib.pyplot as plt + plt.plot([1, 2, 3]) + plt.savefig('myfig.png') + plt.close() + +.. seealso:: + + :doc:`/gallery/user_interfaces/web_application_server_sgskip` for + information about running matplotlib inside of a web application. + +.. _how-to-threads: + +Work with threads +----------------- + +Matplotlib is not thread-safe: in fact, there are known race conditions +that affect certain artists. Hence, if you work with threads, it is your +responsibility to set up the proper locks to serialize access to Matplotlib +artists. + +You may be able to work on separate figures from separate threads. However, +you must in that case use a *non-interactive backend* (typically Agg), because +most GUI backends *require* being run from the main thread as well. + +.. _reporting-problems: +.. _get-help: + +Get help +-------- + +There are a number of good resources for getting help with Matplotlib. +There is a good chance your question has already been asked: + +- The `mailing list archive + `_. + +- `GitHub issues `_. + +- Stackoverflow questions tagged `matplotlib + `_. + +If you are unable to find an answer to your question through search, please +provide the following information in your e-mail to the `mailing list +`_: + +* Your operating system (Linux/Unix users: post the output of ``uname -a``). + +* Matplotlib version:: + + python -c "import matplotlib; print(matplotlib.__version__)" + +* Where you obtained Matplotlib (e.g., your Linux distribution's packages, + GitHub, PyPI, or `Anaconda `_). + +* Any customizations to your ``matplotlibrc`` file (see + :ref:`customizing`). + +* If the problem is reproducible, please try to provide a *minimal*, standalone + Python script that demonstrates the problem. This is *the* critical step. + If you can't post a piece of code that we can run and reproduce your error, + the chances of getting help are significantly diminished. Very often, the + mere act of trying to minimize your code to the smallest bit that produces + the error will help you find a bug in *your* code that is causing the + problem. + +* Matplotlib provides debugging information through the `logging` library, and + a helper function to set the logging level: one can call :: + + plt.set_loglevel("INFO") # or "DEBUG" for more info + + to obtain this debugging information. + + Standard functions from the `logging` module are also applicable; e.g. one + could call ``logging.basicConfig(level="DEBUG")`` even before importing + Matplotlib (this is in particular necessary to get the logging info emitted + during Matplotlib's import), or attach a custom handler to the "matplotlib" + logger. This may be useful if you use a custom logging configuration. + +If you compiled Matplotlib yourself, please also provide: + +* your compiler version -- e.g., ``gcc --version``. +* the output of:: + + pip install --verbose + + The beginning of the build output contains lots of details about your + platform that are useful for the Matplotlib developers to diagnose your + problem. + +If you compiled an older version of Matplotlib using the pre-Meson build system, instead +provide: + +* any changes you have made to ``setup.py``/``setupext.py``, +* the output of:: + + rm -rf build + python setup.py build + +Including this information in your first e-mail to the mailing list +will save a lot of time. + +You will likely get a faster response writing to the mailing list than +filing a bug in the bug tracker. Most developers check the bug +tracker only periodically. If your problem has been determined to be +a bug and cannot be quickly solved, you may be asked to file a bug in +the tracker so the issue doesn't get lost. diff --git a/doc/users/getting_started/index.rst b/doc/users/getting_started/index.rst new file mode 100644 index 000000000000..dfbbd615b5cd --- /dev/null +++ b/doc/users/getting_started/index.rst @@ -0,0 +1,36 @@ +Getting started +=============== + +Installation quick-start +------------------------ + +.. include:: /install/quick_install.inc.rst + +Draw a first plot +----------------- + +Here is a minimal example plot: + +.. plot:: + :include-source: + :align: center + + import matplotlib.pyplot as plt + import numpy as np + + x = np.linspace(0, 2 * np.pi, 200) + y = np.sin(x) + + fig, ax = plt.subplots() + ax.plot(x, y) + plt.show() + +If a plot does not show up please check :ref:`troubleshooting-faq`. + +Where to go next +---------------- + +- Check out :doc:`Plot types ` to get an overview of the + types of plots you can create with Matplotlib. +- Learn Matplotlib from the ground up in the :ref:`Quick-start guide + `. diff --git a/doc/users/github_stats.rst b/doc/users/github_stats.rst deleted file mode 100644 index aca6121775cb..000000000000 --- a/doc/users/github_stats.rst +++ /dev/null @@ -1,1280 +0,0 @@ -.. _github-stats: - -Github Stats -============ - -GitHub stats for 2015/10/29 - 2016/07/13 (tag: v1.5.0) - -These lists are automatically generated, and may be incomplete or contain duplicates. - -We closed 605 issues and merged 483 pull requests. - -The following 173 authors contributed 2121 commits. - -* 4over7 -* AbdealiJK -* Acanthostega -* Adrien Chardon -* Adrien F. Vincent -* Adrien VINCENT -* Alberto -* Alex Rothberg -* Ali Uneri -* Andreas Hilboll -* Andreas Mayer -* Anton Akhmerov -* Antony Lee -* bcongdon -* Ben Congdon -* Ben Root -* Benjamin Berg -* Benjamin Congdon -* BHT -* Cameron Davidson-Pilon -* Chen Karako -* Chris Holdgraf -* Christian Stade-Schuldt -* Christoph Gohlke -* Cimarron Mittelsteadt -* CJ Carey -* DaCoEx -* DanHickstein -* danhickstein -* David Stansby -* deeenes -* Devashish Deshpande -* Diego Mora Cespedes -* dlmccaffrey -* Dora Fraeman -* dsquareindia -* Dylan Evans -* E. G. Patrick Bos -* Elliott Sales de Andrade -* Elvis Stansvik -* Endolith -* Eric Dill -* Eric Firing -* Federico Ariza -* fibersnet -* Florian LB -* flothesof -* Francoise Provencher -* Frank Yu -* Gaute Hope -* gcallah -* gepcel -* Grillard -* hamogu -* hannah -* Hans Moritz Günther -* Hassan Kibirige -* Hastings Greer -* Heiko Oberdiek -* Henning Pohl -* Herbert Kruitbosch -* Ian Thomas -* Ilia Kurenkov -* ImSoErgodic -* Isaac Schwabacher -* Isaac Slavitt -* J. Goutin -* Jacob McDonald -* Jae-Joon Lee -* James A. Bednar -* Jan Schlüter -* Jan Schulz -* Jason Zheng -* Jeffrey Hokanson @ Loki -* Jens Hedegaard Nielsen -* John Vandenberg -* jonchar -* Joseph Fox-Rabinovitz -* Joseph Jon Booker -* Jouni K. Seppänen -* Julian Mehne -* Julian V. Modesto -* Julien Lhermitte -* Julien Schueller -* Kanwar245 -* Kevin Keating -* khyox -* Kjartan Myrdal -* klaus -* klonuo -* Kristen M. Thyng -* Kristen Thyng -* Kyle Bridgemohansingh -* Kyler Brown -* Leo Singer -* LindyBalboa -* Luis Pedro Coelho -* Magnus Nord -* maluethi -* mamrehn -* Matt Hancock -* Matthew Brett -* matthias -* Matthias Bussonnier -* Maximilian Albert -* Maximilian Maahn -* Michael Droettboom -* Michiel de Hoon -* Min RK -* mlub -* muahah -* myyc -* Nathan Goldbaum -* Nathan Musoke -* Nelle Varoquaux -* Nicolas P. Rougier -* Nicolas Tessore -* Nikita Kniazev -* Nils Werner -* none -* Orso Meneghini -* Pankaj Pandey -* paul -* Paul Ganssle -* Paul Hobson -* Paul Kirow -* Paul Romano -* Pete Peterson -* Peter Mortensen -* Peter Würtz -* Phil Elson -* Przemysław Dąbek -* Ramiro Gómez -* Pierre de Buyl -* Qingpeng "Q.P." Zhang -* Randy Olson -* rasbt -* Robin Dunn -* Robin Wilson -* ryan -* Ryan May -* RyanPan -* Samuel St-Jean -* Sander -* scls19fr -* Scott Howard -* scott-vsi -* Sebastián Vanrell -* Simon Gibbons -* Stefan Pfenninger -* Sterling Smith -* Steven Silvester -* story645 -* Terrence J. Katzenbaer -* Thomas A Caswell -* Thomas Hisch -* Thomas Robitaille -* Thorsten Liebig -* Till Stensitzki -* tmdavison -* tomoemon -* Trish Gillett-Kawamoto -* u55 -* ultra-andy -* Valentin Schmidt -* Victor Zabalza -* Víctor Zabalza -* Warren Weckesser -* Wieland Hoffmann -* Xufeng Wang -* Zbigniew Jędrzejewski-Szmek - -GitHub issues and pull requests: - -Pull Requests (483): - -* :ghpull:`7037`: DOC change axhspan to numpydoc format -* :ghpull:`7047`: DOC - SpanSelector widget documentation -* :ghpull:`7049`: [MRG] Documentated dependencies to the doc and remove unecessary dependencies. -* :ghpull:`7063`: Tweek tol for test_hist_steplog to fix tests on appveyor -* :ghpull:`7055`: FIX: testings.nose was not installed -* :ghpull:`7058`: Minor animation fixes -* :ghpull:`7057`: FIX: Removed financial demos that stalled because of yahoo requests -* :ghpull:`7052`: Uncaught exns are fatal for PyQt5, so catch them. -* :ghpull:`7048`: FIX: remove unused variable -* :ghpull:`7042`: FIX: ticks filtered by Axis, not in Tick.draw -* :ghpull:`7026`: Merge 2.x to master -* :ghpull:`6988`: Text box widget, take over of PR5375 -* :ghpull:`6957`: DOC: clearing out some instances of using pylab in the docs -* :ghpull:`7012`: Don't blacklist test_usetex using pytest -* :ghpull:`7011`: TST: Fixed ``skip_if_command_unavailable`` decorator problem -* :ghpull:`6918`: enable previously leftout test_usetex -* :ghpull:`7006`: FIX: sphinx 1.4.0 details -* :ghpull:`6900`: Enh: break website screenshot banner into 4 pieces and introduce a responsive layout -* :ghpull:`6997`: FIX: slow plots of pandas objects (Second try) -* :ghpull:`6792`: PGF Backend: Support interpolation='none' -* :ghpull:`6983`: Catch invalid interactive switch to log scale. -* :ghpull:`6491`: Don't warn in Collections.contains if picker is not numlike. -* :ghpull:`6978`: Add link to O'Reilly video course covering matplotlib -* :ghpull:`6930`: BUG: PcolorImage handles non-contiguous arrays, provides data readout -* :ghpull:`6889`: support for updating axis ticks for categorical data -* :ghpull:`6974`: Fixed wrong expression -* :ghpull:`6730`: Add Py.test testing framework support -* :ghpull:`6904`: Use edgecolor rather than linewidth to control edge display. -* :ghpull:`6919`: Rework MaxNLocator, eliminating infinite loop; closes #6849 -* :ghpull:`6955`: Add parameter checks to DayLocator initiator -* :ghpull:`5161`: [WIP] Proposed change to default log scale tick formatting -* :ghpull:`6875`: Add keymap (default: G) to toggle minor grid. -* :ghpull:`6920`: Prepare for cross-framework test suite -* :ghpull:`6944`: Restore cbook.report_memory, which was deleted in d063dee. -* :ghpull:`6961`: remove extra "a" -* :ghpull:`6947`: Changed error message. Issue #6933 -* :ghpull:`6923`: Make sure nose is only imported when needed -* :ghpull:`6851`: Do not restrict coverage to ``matplotlib`` module only -* :ghpull:`6938`: Image interpolation selector in Qt figure options. -* :ghpull:`6787`: Python3.5 dictview support -* :ghpull:`6407`: adding default toggled state for toggle tools -* :ghpull:`6898`: Fix read mode when loading cached AFM fonts -* :ghpull:`6892`: Don't force anncoords to fig coords upon dragging. -* :ghpull:`6895`: Prevent forced alpha in figureoptions. -* :ghpull:`6877`: Fix Path deepcopy signature -* :ghpull:`6822`: Use travis native cache -* :ghpull:`6821`: Break reference cycle Line2D <-> Line2D._lineFunc. -* :ghpull:`6879`: Delete font cache in one of the configurations -* :ghpull:`6832`: Fix for ylabel title in example tex_unicode_demo.py -* :ghpull:`6848`: ``test_tinypages``: pytest compatible module level setup -* :ghpull:`6881`: add doi to bibtex entry for Hunter (2007) -* :ghpull:`6842`: Clarify Axes.hexbin *extent* docstring -* :ghpull:`6861`: Update ggplot URLs -* :ghpull:`6878`: DOC: use venv instead of virtualenv on python 3 -* :ghpull:`6837`: Fix Normalize(). -* :ghpull:`6874`: Update bachelors_degree_by_gender example. -* :ghpull:`6867`: Mark ``make_all_2d_testfuncs`` as not a test -* :ghpull:`6854`: Fix for PyQt5.7 support. -* :ghpull:`6862`: Change default doc image format to png and pdf -* :ghpull:`6819`: Add mpl_toolkits to coveragerc. -* :ghpull:`6840`: Fixed broken ``test_pickle.test_complete`` test -* :ghpull:`6841`: DOC: Switch to OO code style & ensure fixed y-range in ``psd_demo3`` -* :ghpull:`6843`: DOC: Fix ``psd_demo_complex`` similarly to ``psd_demo3`` -* :ghpull:`6829`: Tick label rotation via ``set_tick_params`` -* :ghpull:`6799`: Allow creating annotation arrows w/ default props. -* :ghpull:`6262`: Properly handle UTC conversion in date2num. -* :ghpull:`6777`: Raise lock timeout as actual exception -* :ghpull:`6817`: DOC: Fix a few typos and formulations -* :ghpull:`6826`: Clarify doc for "norm" kwarg to ``imshow``. -* :ghpull:`6807`: Deprecate ``{get,set}_cursorprops``. -* :ghpull:`6811`: Add xkcd font as one of the options -* :ghpull:`6815`: Rename tests in ``test_mlab.py`` -* :ghpull:`6808`: Don't forget to disconnect callbacks for dragging. -* :ghpull:`6803`: better freetype version checking -* :ghpull:`6778`: Added contribute information to readme -* :ghpull:`6786`: 2.0 Examples fixes. See #6762 -* :ghpull:`6774`: Appveyor: use newer conda packages and only run all tests on one platform -* :ghpull:`6779`: Fix tutorial pyplot scales (issue #6775) -* :ghpull:`6768`: Takeover #6535 -* :ghpull:`6763`: Invalidate test cache on gs/inkscape version -* :ghpull:`6765`: Get more rcParams for 3d -* :ghpull:`6764`: Support returning polylines from to_polygons -* :ghpull:`6760`: DOC: clean up of demo_annotation_box.py -* :ghpull:`6735`: Added missing side tick rcParams -* :ghpull:`6761`: Fixed warnings catching and counting with ``warnings.catch_warnings`` -* :ghpull:`5349`: Add a Gitter chat badge to README.rst -* :ghpull:`6755`: PEP: fix minor formatting issues -* :ghpull:`6699`: Warn if MPLBACKEND is invalid. -* :ghpull:`6754`: Fixed error handling in ``ImageComparisonTest.setup_class`` -* :ghpull:`6734`: register IPython's eventloop integration in plt.install_repl_displayhook -* :ghpull:`6745`: DOC: typo in broken_axis pylab example -* :ghpull:`6747`: Also output the actual error on svg backend tests using subprocess -* :ghpull:`6744`: Add workaround for failures due to newer miktex -* :ghpull:`6741`: Missing ``cleanup`` decorator in ``test_subplots.test_exceptions`` -* :ghpull:`6736`: doc: fix unescaped backslash -* :ghpull:`6733`: Mergev2.x to master -* :ghpull:`6729`: Fix crash if byte-compiled level 2 -* :ghpull:`6575`: setup.py: Recommend installation command for pkgs -* :ghpull:`6645`: Fix containment and subslice optim. for steps. -* :ghpull:`6619`: Hide "inner" {x,y}labels in label_outer too. -* :ghpull:`6639`: Simplify get_legend_handler method -* :ghpull:`6694`: Improve Line2D and MarkerStyle instantiation -* :ghpull:`6692`: Remove explicit children invalidation in update_position method -* :ghpull:`6703`: DOC: explain behavior of notches beyond quartiles -* :ghpull:`6707`: Call ``gc.collect`` after each test only if the user asks for it -* :ghpull:`6711`: Added support for ``mgs`` to Ghostscript dependecy checker -* :ghpull:`6700`: Don't convert vmin, vmax to floats. -* :ghpull:`6714`: fixed font_manager.is_opentype_cff_font() -* :ghpull:`6701`: Colours like 'XeYYYY' don't get recognised properly if X, Y's are numbers -* :ghpull:`6512`: Add computer modern font family -* :ghpull:`6383`: Qt editor alpha -* :ghpull:`6381`: Fix canonical name for "None" linestyle. -* :ghpull:`6689`: Str Categorical Axis Support -* :ghpull:`6686`: Merged _bool from axis into cbook._string_to_bool -* :ghpull:`6683`: New entry in ``.mailmap`` -* :ghpull:`6520`: Appveyor overhaul -* :ghpull:`6697`: Fixed path caching bug in ``Path.unit_regular_star`` -* :ghpull:`6688`: DOC: fix radial increase of size & OO style in polar_scatter_demo -* :ghpull:`6681`: Fix #6680 (minor typo in IdentityTransform docstring) -* :ghpull:`6676`: Fixed AppVeyor building script -* :ghpull:`6672`: Fix example of streamplot ``start_points`` option -* :ghpull:`6601`: BF: protect against locale in sphinext text -* :ghpull:`6662`: [MRG+1] adding from_list to custom cmap tutorial -* :ghpull:`6666`: Guard against too-large figures -* :ghpull:`6659`: Fix image alpha -* :ghpull:`6642`: Fix rectangle selector release bug -* :ghpull:`6652`: Minor doc updates. -* :ghpull:`6653`: DOC: Incorrect rendering of dashes -* :ghpull:`6648`: adding a new color and editing an existing color in fivethirtyeight.mplstyle -* :ghpull:`6548`: Fix typo. -* :ghpull:`6628`: fix the swab bug to compile on solaris system -* :ghpull:`6622`: colors: ensure masked array data is an ndarray -* :ghpull:`6625`: DOC: Found a typo. -* :ghpull:`6614`: Fix docstring for PickEvent. -* :ghpull:`6554`: Update mpl_toolkits.gtktools. -* :ghpull:`6564`: Cleanup for drawstyles. -* :ghpull:`6577`: Fix mlab.rec_join. -* :ghpull:`6596`: Added a new example to create error boxes using a PatchCollection -* :ghpull:`2370`: Implement draw_markers in the cairo backend. -* :ghpull:`6599`: Drop conditional import of figureoptions. -* :ghpull:`6573`: Some general cleanups -* :ghpull:`6568`: Add OSX to travis tests -* :ghpull:`6600`: Typo: markeredgewith -> markeredgewidth -* :ghpull:`6526`: ttconv: Also replace carriage return with spaces. -* :ghpull:`6530`: Update make.py -* :ghpull:`6405`: ToolManager/Tools adding methods to set figure after initialization -* :ghpull:`6553`: Drop prettyplotlib from the list of toolkits. -* :ghpull:`6557`: Merge 2.x to master -* :ghpull:`5626`: New toolbar icons -* :ghpull:`6555`: Fix docstrings for ``warn_deprecated``. -* :ghpull:`6544`: Fix typo in margins handling. -* :ghpull:`6014`: Patch for issue #6009 -* :ghpull:`6517`: Fix conversion of string grays with alpha. -* :ghpull:`6522`: DOC: made sure boxplot demos share y-axes -* :ghpull:`6529`: TST Remove plt.show() from test_axes.test_dash_offset -* :ghpull:`6519`: Fix FigureCanvasAgg.print_raw(...) -* :ghpull:`6481`: Default boxplot style rebase -* :ghpull:`6504`: Patch issue 6035 rebase -* :ghpull:`5593`: ENH: errorbar color cycle clean up -* :ghpull:`6497`: Line2D._path obeys drawstyle. -* :ghpull:`6487`: Added docstring to scatter_with_legend.py [MEP12] -* :ghpull:`6485`: Barchart demo example clean up [MEP 12] -* :ghpull:`6472`: Install all dependencies from pypi -* :ghpull:`6482`: Skip test broken with numpy 1.11 -* :ghpull:`6475`: Do not turn on interactive mode on in example script -* :ghpull:`6442`: MRG: loading TCL / Tk symbols dynamically -* :ghpull:`6467`: ENH: add unified seaborn style sheet -* :ghpull:`6465`: updated boxplot figure -* :ghpull:`6462`: CI: Use Miniconda already installed on AppVeyor. -* :ghpull:`6456`: FIX: unbreak master after 2.x merge -* :ghpull:`6445`: Offset text colored by labelcolor param -* :ghpull:`6417`: Showraise gtk gtk3 -* :ghpull:`6423`: TST: splitlines in rec2txt test -* :ghpull:`6427`: Output pdf dicts in deterministic order -* :ghpull:`6431`: Merge from v2.x -* :ghpull:`6433`: Make the frameworkpython script compatible with Python 3 -* :ghpull:`6358`: Stackplot weighted_wiggle zero-area fix -* :ghpull:`6382`: New color conversion machinery. -* :ghpull:`6372`: DOC: add whats_new for qt configuration editor. -* :ghpull:`6415`: removing unused DialogLineprops from gtk3 -* :ghpull:`6390`: Use xkcd: prefix to avoid color name clashes. -* :ghpull:`6397`: key events handler return value to True to stop propagation -* :ghpull:`6402`: more explicit message for missing image -* :ghpull:`5785`: Better choice of offset-text. -* :ghpull:`6302`: FigureCanvasQT key auto repeat -* :ghpull:`6334`: ENH: webagg: Handle ioloop shutdown correctly -* :ghpull:`5267`: AutoMinorLocator and and logarithmic axis -* :ghpull:`6386`: Minor improvements concerning #6353 and #6357 -* :ghpull:`6388`: Remove wrongly commited test.txt -* :ghpull:`6379`: Install basemap from git trying to fix build issue with docs -* :ghpull:`6369`: Update demo_floating_axes.py with comments -* :ghpull:`6377`: Remove unused variable in GeoAxes class -* :ghpull:`6373`: Remove misspelled and unused variable in GeoAxes class -* :ghpull:`6376`: Update index.rst - add Windrose as third party tool -* :ghpull:`6371`: Set size of static figure to match widget on hidp displays -* :ghpull:`6370`: Restore webagg backend following the merge of widget nbagg backend -* :ghpull:`6366`: Sort default labels numerically in Qt editor. -* :ghpull:`6367`: Remove stray nonascii char from nbagg -* :ghpull:`5754`: IPython Widget -* :ghpull:`6146`: ticker.LinearLocator view_limits algorithm improvement closes #6142 -* :ghpull:`6287`: ENH: add axisbelow option 'line', make it the default -* :ghpull:`6339`: Fix #6335: Queue boxes to update -* :ghpull:`6347`: Allow setting image clims in Qt options editor. -* :ghpull:`6354`: Update events handling documentation to work with Python 3. -* :ghpull:`6356`: Merge 2.x to master -* :ghpull:`6304`: Updating animation file writer to allow keywork arguments when using ``with`` construct -* :ghpull:`6328`: Add default scatter marker option to rcParams -* :ghpull:`6342`: Remove shebang lines from all examples. [MEP12] -* :ghpull:`6337`: Add a 'useMathText' param to method 'ticklabel_format' -* :ghpull:`6346`: Avoid duplicate cmap in image options. -* :ghpull:`6253`: MAINT: Updates to formatters in ``matplotlib.ticker`` -* :ghpull:`6291`: Color cycle handling -* :ghpull:`6340`: BLD: make minimum cycler version 0.10.0 -* :ghpull:`6322`: Typo fixes and wording modifications (minor) -* :ghpull:`6319`: Add PyUpSet as extension -* :ghpull:`6314`: Only render markers on a line when markersize > 0 -* :ghpull:`6303`: DOC Clean up on about half the Mplot3d examples -* :ghpull:`6311`: Seaborn sheets -* :ghpull:`6300`: Remake of #6286 -* :ghpull:`6297`: removed duplicate word in Choosing Colormaps documentation -* :ghpull:`6200`: Tick vertical alignment -* :ghpull:`6203`: Fix #5998: Support fallback font correctly -* :ghpull:`6198`: Make hatch linewidth an rcParam -* :ghpull:`6275`: Fix cycler validation -* :ghpull:`6283`: Use ``figure.stale`` instead of internal member in macosx -* :ghpull:`6247`: DOC: Clarify fillbetween_x example. -* :ghpull:`6251`: ENH: Added a ``PercentFormatter`` class to ``matplotlib.ticker`` -* :ghpull:`6267`: MNT: trap inappropriate use of color kwarg in scatter; closes #6266 -* :ghpull:`6249`: Adjust test tolerance to pass for me on OSX -* :ghpull:`6263`: TST: skip broken test -* :ghpull:`6260`: Bug fix and general touch ups for hist3d_demo example (#1702) -* :ghpull:`6239`: Clean warnings in examples -* :ghpull:`6170`: getter for ticks for colorbar -* :ghpull:`6246`: Merge v2.x into master -* :ghpull:`6238`: Fix sphinx 1.4.0 issues -* :ghpull:`6241`: Force Qt validator to use C locale. -* :ghpull:`6234`: Limit Sphinx to 1.3.6 for the time being -* :ghpull:`6178`: Use Agg for rendering in the Mac OSX backend -* :ghpull:`6232`: MNT: use stdlib tools in allow_rasterization -* :ghpull:`6211`: A method added to Colormap classes to reverse the colormap -* :ghpull:`6205`: Use io.BytesIO instead of io.StringIO in examples -* :ghpull:`6229`: Add a locator to AutoDateFormatters example code -* :ghpull:`6222`: ENH: Added ``file`` keyword to ``setp`` to redirect output -* :ghpull:`6217`: BUG: Made ``setp`` accept arbitrary iterables -* :ghpull:`6154`: Some small cleanups based on Quantified code -* :ghpull:`4446`: Label outer offset text -* :ghpull:`6218`: DOC: fix typo -* :ghpull:`6202`: Fix #6136: Don't hardcode default scatter size -* :ghpull:`6195`: Documentation bug #6180 -* :ghpull:`6194`: Documentation bug fix: #5517 -* :ghpull:`6011`: Fix issue #6003 -* :ghpull:`6179`: Issue #6105: Adds targetfig parameter to the subplot2grid function -* :ghpull:`6185`: Fix to csv2rec bug for review -* :ghpull:`6192`: More precise choice of axes limits. -* :ghpull:`6176`: DOC: Updated docs for rc_context -* :ghpull:`5617`: Legend tuple handler improve -* :ghpull:`6188`: Merge 2x into master -* :ghpull:`6158`: Fix: pandas series of strings -* :ghpull:`6156`: Bug: Fixed regression of ``drawstyle=None`` -* :ghpull:`5343`: Boxplot stats w/ equal quartiles -* :ghpull:`6132`: Don't check if in range if the caller passed norm -* :ghpull:`6091`: Fix for issue 5575 along with testing -* :ghpull:`6123`: docstring added -* :ghpull:`6145`: BUG: Allowing unknown drawstyles -* :ghpull:`6148`: Fix: Pandas indexing Error in collections -* :ghpull:`6140`: clarified color argument in scatter -* :ghpull:`6137`: Fixed outdated link to thirdpartypackages, and simplified the page -* :ghpull:`6095`: Bring back the module level 'backend' -* :ghpull:`6124`: Fix about dialog on Qt 5 -* :ghpull:`6110`: Fixes matplotlib/matplotlib#1235 -* :ghpull:`6122`: MNT: improve image array argument checking in to_rgba. Closes #2499. -* :ghpull:`6047`: bug fix related #5479 -* :ghpull:`6119`: added comment on "usetex=False" to ainde debugging when latex not ava… -* :ghpull:`6073`: fixed bug 6028 -* :ghpull:`6116`: CI: try explicitly including msvc_runtime -* :ghpull:`6100`: Update INSTALL -* :ghpull:`6099`: Fix #6069. Handle image masks correctly -* :ghpull:`6079`: Fixed Issue 4346 -* :ghpull:`6102`: Update installing_faq.rst -* :ghpull:`6101`: Update INSTALL -* :ghpull:`6074`: Fixes an error in the documentation, linestyle is dash_dot and should be dashdot -* :ghpull:`6068`: Text class: changed __str__ method and added __repr__ method -* :ghpull:`6018`: Added get_status() function to the CheckButtons widget -* :ghpull:`6013`: Mnt cleanup pylab setup -* :ghpull:`5984`: Suggestion for Rasterization to docs pgf-backend -* :ghpull:`5911`: Fix #5895: Properly clip MOVETO commands -* :ghpull:`6039`: DOC: added missing import to navigation_toolbar.rst -* :ghpull:`6036`: BUG: fix ListedColormap._resample, hence plt.get_cmap; closes #6025 -* :ghpull:`6029`: TST: Always use / in URLs for visual results. -* :ghpull:`6022`: Make @cleanup *really* support generative tests. -* :ghpull:`6024`: Add Issue template with some guidelines -* :ghpull:`5718`: Rewrite of image infrastructure -* :ghpull:`3973`: WIP: BUG: Convert qualitative colormaps to ListedColormap -* :ghpull:`6005`: FIX: do not short-cut all white-space strings -* :ghpull:`5727`: Refresh pgf baseline images. -* :ghpull:`5975`: ENH: add kwarg normalization function to cbook -* :ghpull:`5931`: use ``locale.getpreferredencoding()`` to prevent OS X locale issues -* :ghpull:`5972`: add support for PySide2, #5971 -* :ghpull:`5625`: DOC: add FAQ about np.datetime64 -* :ghpull:`5131`: fix #4854: set default numpoints of legend entries to 1 -* :ghpull:`5926`: Fix #5917. New dash patterns. Scale dashes by lw -* :ghpull:`5976`: Lock calls to latex in texmanager -* :ghpull:`5628`: Reset the available animation movie writer on rcParam change -* :ghpull:`5951`: tkagg: raise each new window; partially addresses #596 -* :ghpull:`5958`: TST: add a test for tilde in tempfile for the PS backend -* :ghpull:`5957`: Win: add mgs as a name for ghostscript executable -* :ghpull:`5928`: fix for latex call on PS backend (Issue #5895) -* :ghpull:`5954`: Fix issues with getting tempdir when unknown uid -* :ghpull:`5922`: Fixes for Windows test failures on appveyor -* :ghpull:`5953`: Fix typos in Axes.boxplot and Axes.bxp docstrings -* :ghpull:`5947`: Fix #5944: Fix PNG writing from notebook backend -* :ghpull:`5936`: Merge 2x to master -* :ghpull:`5629`: WIP: more windows build and CI changes -* :ghpull:`5914`: Make barbs draw correctly (Fixes #5803) -* :ghpull:`5906`: Merge v2x to master -* :ghpull:`5809`: Support generative tests in @cleanup. -* :ghpull:`5910`: Fix reading/writing from urllib.request objects -* :ghpull:`5882`: mathtext: Fix comma behaviour at start of string -* :ghpull:`5880`: mathtext: Fix bugs in conversion of apostrophes to primes -* :ghpull:`5872`: Fix issue with Sphinx 1.3.4 -* :ghpull:`5894`: Boxplot concept figure update -* :ghpull:`5870`: Docs / examples fixes. -* :ghpull:`5892`: Fix gridspec.Gridspec: check ratios for consistency with rows and columns -* :ghpull:`5901`: Fixes incorrect ipython sourcecode -* :ghpull:`5893`: Show significant digits by default in QLineEdit. -* :ghpull:`5881`: Allow build children to run -* :ghpull:`5886`: Revert "Build the docs with python 3.4 which should fix the Traitlets… -* :ghpull:`5877`: DOC: added blurb about external mpl-proscale package -* :ghpull:`5879`: Build the docs with python 3.4 which should fix the Traitlets/IPython… -* :ghpull:`5871`: Fix sized delimiters for regular-sized mathtext (#5863) -* :ghpull:`5852`: FIX: create _dashSeq and _dashOfset before use -* :ghpull:`5832`: Rewordings for normalizations docs. -* :ghpull:`5849`: Update setupext.py to solve issue #5846 -* :ghpull:`5853`: Typo: fix some typos in patches.FancyArrowPatch -* :ghpull:`5842`: Allow image comparison outside tests module -* :ghpull:`5845`: V2.x merge to master -* :ghpull:`5813`: mathtext: no space after comma in brackets -* :ghpull:`5828`: FIX: overzealous clean up of imports -* :ghpull:`5826`: Strip spaces in properties doc after newline. -* :ghpull:`5815`: Properly minimize the rasterized layers -* :ghpull:`5752`: Reorganise mpl_toolkits documentation -* :ghpull:`5788`: Fix ImportError: No module named 'StringIO' on Python 3 -* :ghpull:`5797`: Build docs on python3.5 with linkcheck running on python 2.7 -* :ghpull:`5778`: Fix #5777. Don't warn when applying default style -* :ghpull:`4857`: Toolbars keep history if axes change (navtoolbar2 + toolmanager) -* :ghpull:`5790`: Fix ImportError: No module named 'Tkinter' on Python 3 -* :ghpull:`5789`: Index.html template. Only insert snippet if found -* :ghpull:`5783`: MNT: remove reference to deleted example -* :ghpull:`5780`: Choose offset text from ticks, not axes limits. -* :ghpull:`5776`: Add .noseids to .gitignore. -* :ghpull:`5466`: Fixed issue with ``rasterized`` not working for errorbar -* :ghpull:`5773`: Fix eb rasterize -* :ghpull:`5440`: Fix #4855: Blacklist rcParams that aren't style -* :ghpull:`5764`: BUG: make clabel obey fontsize kwarg -* :ghpull:`5771`: Remove no longer used Scikit image code -* :ghpull:`5766`: Deterministic LaTeX text in SVG images -* :ghpull:`5762`: Don't fallback to old ipython_console_highlighting -* :ghpull:`5728`: Use custom RNG for sketch path -* :ghpull:`5454`: ENH: Create an abstract base class for movie writers. -* :ghpull:`5600`: Fix #5572: Allow passing empty range to broken_barh -* :ghpull:`4874`: Document mpl_toolkits.axes_grid1.anchored_artists -* :ghpull:`5746`: Clarify that easy_install may be used to install all dependencies -* :ghpull:`5739`: Silence labeled data warning in tests -* :ghpull:`5732`: RF: fix annoying parens bug -* :ghpull:`5735`: Correct regex in filterwarnings -* :ghpull:`5640`: Warning message prior to fc-list command -* :ghpull:`5686`: Remove banner about updating styles in 2.0 -* :ghpull:`5676`: Fix #5646: bump the font manager version -* :ghpull:`5719`: Fix #5693: Implemented is_sorted in C -* :ghpull:`5721`: Remove unused broken doc example axes_zoom_effect -* :ghpull:`5664`: Low-hanging performance improvements -* :ghpull:`5709`: Addresses issue #5704. Makes usage of parameters clearer -* :ghpull:`5716`: Fix #5715. -* :ghpull:`5690`: Fix #5687: Don't pass unicode to QApplication() -* :ghpull:`5707`: Fix string format substitution key missing error -* :ghpull:`5706`: Fix SyntaxError on Python 3 -* :ghpull:`5700`: BUG: handle colorbar ticks with boundaries and NoNorm; closes #5673 -* :ghpull:`5702`: Add missing substitution value -* :ghpull:`5701`: str.formatter invalid -* :ghpull:`5697`: TST: add missing decorator -* :ghpull:`5683`: Include outward ticks in bounding box -* :ghpull:`5688`: Improved documentation for FuncFormatter formatter class -* :ghpull:`5469`: Image options -* :ghpull:`5677`: Fix #5573: Use SVG in docs -* :ghpull:`4864`: Add documentation for mpl_toolkits.axes_grid1.inset_locator -* :ghpull:`5434`: Remove setup.py tests and adapt docs to use tests.py -* :ghpull:`5586`: Fix errorbar extension arrows -* :ghpull:`5653`: Update banner logo on main website -* :ghpull:`5667`: Nicer axes names in selector for figure options. -* :ghpull:`5672`: Fix #5670. No double endpoints in Path.to_polygon -* :ghpull:`5553`: qt: raise each new window -* :ghpull:`5594`: FIX: formatting in LogFormatterExponent -* :ghpull:`5588`: Adjust number of ticks based on length of axis -* :ghpull:`5671`: Deterministic svg -* :ghpull:`5659`: Change ``savefig.dpi`` and ``figure.dpi`` defaults -* :ghpull:`5662`: Bugfix for test_triage tool on Python 2 -* :ghpull:`5661`: Fix #5660. No FileNotFoundError on Py2 -* :ghpull:`4921`: Add a quit_all key to the default keymap -* :ghpull:`5651`: Shorter svg files -* :ghpull:`5656`: Fix #5495. Combine two tests to prevent race cond -* :ghpull:`5383`: Handle HiDPI displays in WebAgg/NbAgg backends -* :ghpull:`5307`: Lower test tolerance -* :ghpull:`5631`: WX/WXagg backend add code that zooms properly on a Mac with a Retina display -* :ghpull:`5644`: Fix typo in pyplot_scales.py -* :ghpull:`5639`: Test if a frame is not already being deleted before trying to Destroy. -* :ghpull:`5583`: Use data limits plus a little padding by default -* :ghpull:`4702`: sphinxext/plot_directive does not accept a caption -* :ghpull:`5612`: mathtext: Use DejaVu display symbols when available -* :ghpull:`5374`: MNT: Mailmap fixes and simplification -* :ghpull:`5516`: OSX virtualenv fixing by creating a simple alias -* :ghpull:`5546`: Fix #5524: Use large, but finite, values for contour extensions -* :ghpull:`5621`: Tst up coverage -* :ghpull:`5620`: FIX: quiver key pivot location -* :ghpull:`5607`: Clarify error when plot() args have bad shapes. -* :ghpull:`5604`: WIP: testing on windows and conda packages/ wheels for master -* :ghpull:`5611`: Update colormap user page -* :ghpull:`5587`: No explicit mathdefault in log formatter -* :ghpull:`5591`: fixed ordering of lightness plots and changed from getting lightness … -* :ghpull:`5605`: Fix DeprecationWarning in stackplot.py -* :ghpull:`5603`: Draw markers around center of pixels -* :ghpull:`5596`: No edges on filled things by default -* :ghpull:`5249`: Keep references to modules required in pgf LatexManager destructor -* :ghpull:`5589`: return extension metadata -* :ghpull:`5566`: DOC: Fix typo in Axes.bxp.__doc__ -* :ghpull:`5570`: use base64.encodestring on python2.7 -* :ghpull:`5578`: Fix #5576: Handle CPLUS_INCLUDE_PATH -* :ghpull:`5555`: Use shorter float repr in figure options dialog. -* :ghpull:`5552`: Dep contourset vminmax -* :ghpull:`5433`: ENH: pass dash_offset through to gc for Line2D -* :ghpull:`5342`: Sort and uniquify style entries in figure options. -* :ghpull:`5484`: fix small typo in documentation about CheckButtons. -* :ghpull:`5547`: Fix #5545: Fix collection scale in data space -* :ghpull:`5500`: Fix #5475: Support tolerance when picking patches -* :ghpull:`5501`: Use facecolor instead of axisbg/axis_bgcolor -* :ghpull:`5544`: Revert "Fix #5524. Use finfo.max instead of np.inf" -* :ghpull:`5146`: Move impl. of plt.subplots to Figure.add_subplots. -* :ghpull:`5534`: Fix #5524. Use finfo.max instead of np.inf -* :ghpull:`5521`: Add test triage tool -* :ghpull:`5537`: Fix for broken maplotlib.test function -* :ghpull:`5539`: Fix docstring of violin{,plot} for return value. -* :ghpull:`5515`: Fix some theoretical problems with png reading -* :ghpull:`5526`: Add boxplot params to rctemplate -* :ghpull:`5533`: Fixes #5522, bug in custom scale example -* :ghpull:`5514`: adding str to force string in format -* :ghpull:`5512`: V2.0.x -* :ghpull:`5465`: Better test for isarray in figaspect(). Closes #5464. -* :ghpull:`5503`: Fix #4487: Take hist bins from rcParam -* :ghpull:`5485`: Contour levels must be increasing -* :ghpull:`4678`: TST: Enable coveralls/codecov code coverage -* :ghpull:`5437`: Make "classic" style have effect -* :ghpull:`5458`: Removed normalization of arrows in 3D quiver -* :ghpull:`5480`: make sure an autoreleasepool is in place -* :ghpull:`5451`: [Bug] masking of NaN Z values in pcolormesh -* :ghpull:`5453`: Force frame rate of FFMpegFileWriter input -* :ghpull:`5452`: Fix axes.set_prop_cycle to handle any generic iterable sequence. -* :ghpull:`5448`: Fix #5444: do not access subsuper nucleus _metrics if not available -* :ghpull:`5439`: Use DejaVu Sans as default fallback font -* :ghpull:`5204`: Minor cleanup work on navigation, text, and customization files. -* :ghpull:`5432`: Don't draw text when it's completely clipped away -* :ghpull:`5426`: MNT: examples: Set the aspect ratio to "equal" in the double pendulum animation. -* :ghpull:`5214`: Use DejaVu fonts as default for text and mathtext -* :ghpull:`5306`: Use a specific version of Freetype for testing -* :ghpull:`5410`: Remove uses of font.get_charmap -* :ghpull:`5407`: DOC: correct indentation -* :ghpull:`4863`: [mpl_toolkits] Allow "figure" kwarg for host functions in parasite_axes -* :ghpull:`5166`: [BUG] Don't allow 1d-arrays in plot_surface. -* :ghpull:`5360`: Add a new memleak script that does everything -* :ghpull:`5361`: Fix #347: Faster text rendering in Agg -* :ghpull:`5373`: Remove various Python 2.6 related workarounds -* :ghpull:`5398`: Updating 2.0 schedule -* :ghpull:`5389`: Faster image generation in WebAgg/NbAgg backends -* :ghpull:`4970`: Fixed ZoomPanBase to work with log plots -* :ghpull:`5387`: Fix #3314 assert mods.pop(0) fails -* :ghpull:`5385`: Faster event delegation in WebAgg/NbAgg backends -* :ghpull:`5384`: BUG: Make webagg work without IPython installed -* :ghpull:`5358`: Fix #5337. Turn off --no-capture (-s) on nose -* :ghpull:`5379`: DOC: Fix typo, broken link in references -* :ghpull:`5371`: DOC: Add what's new entry for TransformedPatchPath. -* :ghpull:`5299`: Faster character mapping -* :ghpull:`5356`: Replace numpy funcs for scalars. -* :ghpull:`5359`: Fix memory leaks found by memleak_hawaii3.py -* :ghpull:`5357`: Fixed typo -* :ghpull:`4920`: ENH: Add TransformedPatchPath for clipping. - -Issues (605): - -* :ghissue:`7009`: No good way to disable SpanSelector -* :ghissue:`7040`: It is getting increasingly difficult to build the matplotlib documentation -* :ghissue:`6965`: ArtistAnimation cannot animate Figure-only artists -* :ghissue:`6964`: Docstring for ArtistAnimation is incorrect -* :ghissue:`7062`: remove the contour on a Basemap object -* :ghissue:`7061`: remove the contour on Basemap -* :ghissue:`7054`: Whether the new version 2.0 will support high-definition screen? -* :ghissue:`7053`: When will release 2.0 official version? -* :ghissue:`6797`: Undefined Symbol Error On Ubuntu -* :ghissue:`6523`: matplotlib-2.0.0b1 test errors on Windows -* :ghissue:`4753`: rubber band in qt5agg slow -* :ghissue:`6959`: extra box on histogram plot with a single value -* :ghissue:`6816`: Segmentation fault on Qt5Agg when using the wrong linestyle -* :ghissue:`4212`: Hist showing wrong first bin -* :ghissue:`4602`: bar / hist : gap between first bar and other bars with lw=0.0 -* :ghissue:`6641`: Edge ticks sometimes disappear -* :ghissue:`7041`: Python 3.5.2 crashes when launching matplotlib 1.5.1 -* :ghissue:`7028`: Latex Greek fonts not working in legend -* :ghissue:`6998`: dash pattern scaling with linewidth should get it's own rcParam -* :ghissue:`7021`: How to prevent matplotlib from importing qt4 libraries when only -* :ghissue:`7020`: Using tick_right() removes any styling applied to tick labels. -* :ghissue:`7018`: Website Down -* :ghissue:`6785`: Callbacks of draggable artists should check that they have not been removed -* :ghissue:`6783`: Draggable annotations specified in offset coordinates switch to figure coordinates after dragging -* :ghissue:`7015`: pcolor() not using "data" keyword argument -* :ghissue:`7014`: matplotlib works well in ipython note book but can't display in a terminal running -* :ghissue:`6999`: cycler 0.10 is required due to change_key() usage -* :ghissue:`6794`: Incorrect text clipping in presence of multiple subplots -* :ghissue:`7004`: Zooming with a large range in y-values while using the linestyle "--" is very slow -* :ghissue:`6828`: Spikes in small wedges of a pie chart -* :ghissue:`6940`: large memory leak in new contour routine -* :ghissue:`6894`: bar(..., linewidth=None) doesn't display bar edges with mpl2.0b3 -* :ghissue:`6989`: bar3d no longer allows default colors -* :ghissue:`6980`: problem accessing canvas on MacOS 10.11.6 with matplotlib 2.0.0b3 -* :ghissue:`6804`: Histogram of xarray.DataArray can be extremely slow -* :ghissue:`6859`: Update URL for links to ggplot -* :ghissue:`6852`: Switching to log scale when there is no positive data crashes the Qt5 backend, causes inconsistent internal state in others -* :ghissue:`6740`: PGF Backend: Support interpolation='none'? -* :ghissue:`6665`: regression: builtin latex rendering doesn't find the right mathematical fonts -* :ghissue:`6984`: plt.annotate(): segmentation fault when coordinates are too high -* :ghissue:`6979`: plot won't show with plt.show(block=False) -* :ghissue:`6981`: link to ggplot is broken... -* :ghissue:`6975`: [Feature request] Simple ticks generator for given range -* :ghissue:`6905`: pcolorfast results in invalid cursor data -* :ghissue:`6970`: quiver problems when angles is an array of values rather than 'uv' or 'xy' -* :ghissue:`6966`: No Windows wheel available on PyPI for new version of matplotlib (1.5.2) -* :ghissue:`6721`: Font cache building of matplotlib blocks requests made to HTTPd -* :ghissue:`6844`: scatter edgecolor is broken in Matplotlib 2.0.0b3 -* :ghissue:`6849`: BUG: endless loop with MaxNLocator integer kwarg and short axis -* :ghissue:`6935`: matplotlib.dates.DayLocator cannot handle invalid input -* :ghissue:`6951`: Ring over A in \AA is too high in Matplotlib 1.5.1 -* :ghissue:`6960`: axvline is sometimes not shown -* :ghissue:`6473`: Matplotlib manylinux wheel - ready to ship? -* :ghissue:`5013`: Add Hershey Fonts a la IDL -* :ghissue:`6953`: ax.vlines adds unwanted padding, changes ticks -* :ghissue:`6946`: No Coveralls reports on GitHub -* :ghissue:`6933`: Misleading error message for matplotlib.pyplot.errorbar() -* :ghissue:`6945`: Matplotlib 2.0.0b3 wheel can't load libpng in OS X 10.6 -* :ghissue:`3865`: Improvement suggestions for matplotlib.Animation.save('video.mp4') -* :ghissue:`6932`: Investigate issue with pyparsing 2.1.6 -* :ghissue:`6941`: Interfering with yahoo_finance -* :ghissue:`6913`: Cant get currency from yahoo finance with matplotlib -* :ghissue:`6901`: Add API function for removing legend label from graph -* :ghissue:`6510`: 2.0 beta: Boxplot patches zorder differs from lines -* :ghissue:`6911`: freetype build won't become local -* :ghissue:`6866`: examples/misc/longshort.py is outdated -* :ghissue:`6912`: Matplotlib fail to compile matplotlib._png -* :ghissue:`1711`: Autoscale to automatically include a tiny margin with ``Axes.errorbar()`` -* :ghissue:`6903`: RuntimeError('Invalid DISPLAY variable') - With docker and django -* :ghissue:`6888`: Can not maintain zoom level when left key is pressed -* :ghissue:`6855`: imsave-generated PNG files missing edges for certain resolutions -* :ghissue:`6479`: Hexbin with log scale takes extent range as logarithm of the data along the log axis -* :ghissue:`6795`: suggestion: set_xticklabels and set_yticklabels default to current labels -* :ghissue:`6825`: I broke imshow() :-( -* :ghissue:`6858`: PyQt5 pyplot error -* :ghissue:`6853`: PyQt5 (v5.7) backend - TypeError upon calling figure() -* :ghissue:`6835`: Which image formats to build in docs. -* :ghissue:`6856`: Incorrect plotting for versions > 1.3.1 and GTK. -* :ghissue:`6838`: Figures not showing in interactive mode with macosx backend -* :ghissue:`6846`: GTK Warning -* :ghissue:`6839`: Test ``test_pickle.test_complete`` is broken -* :ghissue:`6691`: rcParam missing tick side parameters -* :ghissue:`6833`: plot contour with levels from discrete data -* :ghissue:`6636`: DOC: gallery supplies 2 pngs, neither of which is default -* :ghissue:`3896`: dates.date2num bug with daylight switching hour -* :ghissue:`6685`: 2.0 dev legend breaks on scatterplot -* :ghissue:`3655`: ensure removal of font cache on version upgrade -* :ghissue:`6818`: Failure to build docs: unknown property -* :ghissue:`6798`: clean and regenerate travis cache -* :ghissue:`6782`: 2.x: Contour level count is not respected -* :ghissue:`6796`: plot/lines not working for datetime objects that span old dates -* :ghissue:`6660`: cell focus/cursor issue when plotting to nbagg -* :ghissue:`6775`: Last figure in http://matplotlib.org/users/pyplot_tutorial.html is not displayed correctly -* :ghissue:`5981`: Increased tick width in 3D plots looks odd -* :ghissue:`6771`: ImportError: No module named artist -* :ghissue:`6289`: Grids are not rendered in backend implementation -* :ghissue:`6621`: Change in the result of test_markevery_linear_scales_zoomed -* :ghissue:`6515`: Dotted grid lines in v2.0.0b1 -* :ghissue:`6511`: Dependencies in installation of 2.0.0b1 -* :ghissue:`6668`: “Bachelor's degrees…” picture in the gallery is cropped -* :ghissue:`6751`: Tableau style -* :ghissue:`6742`: import matplotlib.pyplot as plt throws an erro -* :ghissue:`6097`: anaconda package missing nose dependency -* :ghissue:`6299`: savefig() to eps/pdf does not work -* :ghissue:`6393`: Pair of floats breaks plotting renderer (weirdest bug I've ever seen) -* :ghissue:`6387`: import matplotlib causes UnicodeDecodeError -* :ghissue:`6471`: Colorbar label position different when executing a block of code -* :ghissue:`6732`: Adding ``pairplot`` functionality? -* :ghissue:`6749`: Step diagram does not support xlim() and ylim() -* :ghissue:`6748`: Step diagram does not suppot -* :ghissue:`6615`: Bad event index for step plots -* :ghissue:`6588`: Different line styles between PNG and PDF exports. -* :ghissue:`6693`: linestyle="None" argument for fill_between() doesn't work -* :ghissue:`6592`: Linestyle pattern depends on current style, not style set at creation -* :ghissue:`5430`: Linestyle: dash tuple with offset -* :ghissue:`6728`: Can't install matplotlib with specific python version -* :ghissue:`6546`: Recommendation to install packages for various OS -* :ghissue:`6536`: get_sample_data() in cbook.py duplicates code from _get_data_path() __init__.py -* :ghissue:`3631`: Better document meaning of notches in boxplots -* :ghissue:`6705`: The test suite spends 20% of it's time in ``gc.collect()`` -* :ghissue:`6698`: Axes3D scatter crashes without alpha keyword -* :ghissue:`5860`: Computer Modern Roman should be the default serif when using TeX backend -* :ghissue:`6702`: Bad fonts crashes matplotlib on startup -* :ghissue:`6671`: Issue plotting big endian images -* :ghissue:`6196`: Qt properties editor discards color alpha -* :ghissue:`6509`: pylab image_masked is broken -* :ghissue:`6657`: appveyor is failing on pre-install -* :ghissue:`6610`: Icons for Tk are not antialiased. -* :ghissue:`6687`: Small issues with the example ``polar_scatter_demo.py`` -* :ghissue:`6541`: Time to deprecate the GTK backend -* :ghissue:`6680`: Minor typo in the docstring of ``IdentityTransform``? -* :ghissue:`6670`: plt.text object updating incorrectly with blit=False -* :ghissue:`6646`: Incorrect fill_between chart when use set_xscale('log') -* :ghissue:`6540`: imshow(..., alpha=0.5) produces different results in 2.x -* :ghissue:`6650`: fill_between() not working properly -* :ghissue:`6566`: Regression: Path.contains_points now returns uint instead of bool -* :ghissue:`6624`: bus error: fc-list -* :ghissue:`6655`: Malware found on matplotlib components -* :ghissue:`6623`: RectangleSelector disappears after resizing -* :ghissue:`6629`: matplotlib version error -* :ghissue:`6638`: get_ticklabels returns '' in ipython/python interpreter -* :ghissue:`6631`: can't build matplotlib on smartos system(open solaris) -* :ghissue:`6562`: 2.x: Cairo backends cannot render images -* :ghissue:`6507`: custom scatter marker demo broken -* :ghissue:`6591`: DOC: update static image for interpolation_none_vs_nearest.py example -* :ghissue:`6607`: BUG: saving image to png changes colors -* :ghissue:`6587`: please copy http://matplotlib.org/devdocs/users/colormaps.html to http://matplotlib.org/users -* :ghissue:`6594`: Documentation Typo -* :ghissue:`5784`: dynamic ticking (#5588) should avoid (if possible) single ticks -* :ghissue:`6492`: mpl_toolkits.mplot3d has a null byte somewhere -* :ghissue:`5862`: Some Microsoft fonts produce unreadable EPS -* :ghissue:`6537`: bundled six 1.9.0 causes ImportError: No module named 'winreg' in Pympler -* :ghissue:`6563`: pyplot.errorbar attempts to plot 0 on a log axis in SVGs -* :ghissue:`6571`: Unexpected behavior with ttk.Notebook - graph not loaded unless tab preselected -* :ghissue:`6570`: Unexpected behavior with ttk.Notebook - graph not loaded unless tab preselected -* :ghissue:`6539`: network tests are not skipped when running tests.py with --no-network -* :ghissue:`6567`: qt_compat fails to identify PyQt5 -* :ghissue:`6559`: mpl 1.5.1 requires pyqt even with a wx backend -* :ghissue:`6009`: No space before unit symbol when there is no SI prefix in ticker.EngFormatter -* :ghissue:`6528`: Fail to install matplotlib by "pip install" on SmartOS(like open solaris system) -* :ghissue:`6531`: Segmentation fault with any backend (matplotlib 1.4.3 and 1.5.1) when calling pyplot.show() -* :ghissue:`6513`: Using gray shade from string ignores alpha parameters -* :ghissue:`6477`: Savefig() to pdf renders markers differently than show() -* :ghissue:`6525`: PS export issue with custom font -* :ghissue:`6514`: LaTeX axis labels can no longer have custom fonts -* :ghissue:`2663`: Multi Cursor disable broken -* :ghissue:`6083`: Figure linewidth default in rcparams -* :ghissue:`1069`: Add a donation information page -* :ghissue:`6035`: Issue(?): head size of FancyArrowPatch changes between interactive figure and picture export -* :ghissue:`6495`: new figsize is bad for subplots with fontsize 12 -* :ghissue:`6493`: Stepfilled color cycle for background and edge different -* :ghissue:`6380`: Implicit addition of "color" to property_cycle breaks semantics -* :ghissue:`6447`: Line2D.contains does not take drawstyle into account. -* :ghissue:`6257`: option for default space between title and axes -* :ghissue:`5868`: tight_layout doesn't leave enough space between outwards ticks and axes title -* :ghissue:`5987`: Outward ticks cause labels to be clipped by default -* :ghissue:`5269`: Default changes: legend -* :ghissue:`6489`: Test errors with numpy 1.11.1rc1 -* :ghissue:`5960`: Misplaced shadows when using FilteredArtistList -* :ghissue:`6452`: Please add a generic "seaborn" style -* :ghissue:`6469`: Test failures testing matplotlib 1.5.1 manylinux wheels -* :ghissue:`5854`: New cycler does not work with bar plots -* :ghissue:`5977`: legend needs logic to deal with new linestyle scaling by linewidth -* :ghissue:`6365`: Default format time series xtick labels changed -* :ghissue:`6104`: docs: latex required for PDF plotting? -* :ghissue:`6451`: Inequality error on web page http://matplotlib.org/faq/howto_faq.html -* :ghissue:`6459`: use conda already installed on appveyor -* :ghissue:`6043`: Advanced hillshading example looks strange with new defaults. -* :ghissue:`6440`: BUG: set_tick_params labelcolor should apply to offset -* :ghissue:`6458`: Wrong package name in INSTALL file -* :ghissue:`2842`: matplotlib.tests.test_basic.test_override_builtins() fails with Python >=3.4 -* :ghissue:`2375`: matplotlib 1.3.0 doesn't compile with Solaris Studio 12.1 CC -* :ghissue:`2667`: matplotlib.tests.test_mathtext.test_mathtext_{cm,stix,stixsans}_{37,53}.test are failing -* :ghissue:`2243`: axes limits with aspect='equal' -* :ghissue:`1758`: y limit with dashed or dotted lines hangs with somewhat big data -* :ghissue:`5994`: Points annotation coords not working in 2.x -* :ghissue:`6444`: matplotlib.path.contains_points is a LOT slower in 1.51 -* :ghissue:`5461`: Feature request: allow a default line alpha to be set in mpl.rcParams -* :ghissue:`5132`: ENH: Set the alpha value for plots in rcParams -* :ghissue:`6449`: axhline and axvline linestyle as on-off seq doesn't work if set directly in function call -* :ghissue:`6416`: animation with 'ffmpeg' backend and 'savefig.bbox = tight' garbles video -* :ghissue:`6437`: Improperly spaced time axis -* :ghissue:`5974`: scatter is not changing color in Axes3D -* :ghissue:`6436`: clabels plotting outside of projection limb -* :ghissue:`6438`: Cant get emoji working in Pie chart legend with google app engine. Need help. -* :ghissue:`6362`: greyscale scatter points appearing blue -* :ghissue:`6301`: tricky bug in ticker due to special behaviour of numpy -* :ghissue:`6276`: Ticklabel format not preserved after editing plot limits -* :ghissue:`6173`: ``linestyle`` parameter does not support default cycler through ``None``, crashes instead. -* :ghissue:`6109`: colorbar _ticker +_locate bug -* :ghissue:`6231`: Segfault when figures are deleted in random order -* :ghissue:`6432`: micro sign doesn't show in EngFormatter -* :ghissue:`6057`: Infinite Loop: LogLocator Colorbar & update_ticks -* :ghissue:`6270`: pyplot.contour() not working with matplotlib.ticker.LinearLocator() -* :ghissue:`6058`: "Configure subplots" tool is initialized very inefficiently in the Qt backends -* :ghissue:`6363`: Change ``legend`` to accept ``alpha`` instead of (only) ``framealpha``. -* :ghissue:`6394`: Severe bug in ````imshow```` when plotting images with small values -* :ghissue:`6368`: Bug: matplotlib.pyplot.spy: does not work correctly for sparse matrices with many entries (>= 2**32) -* :ghissue:`6419`: Imshow does not copy data array but determines colormap values upon call -* :ghissue:`3615`: mouse scroll event in Gtk3 backend -* :ghissue:`3373`: add link to gtk embedding cookbook to website -* :ghissue:`6121`: opening the configure subplots menu moves the axes by a tiny amount -* :ghissue:`2511`: NavigationToolbar breaks if axes are added during use. -* :ghissue:`6349`: Down arrow on GTK3 backends selects toolbar, which eats furthur keypress events -* :ghissue:`6408`: minor ticks don't respect rcParam xtick.top / ytick.right -* :ghissue:`6398`: sudden install error with pip (pyparsing 2.1.2 related) -* :ghissue:`5819`: 1.5.1rc1: dont use absolute links in the "new updates" on the homepage -* :ghissue:`5969`: urgent bug after 1.5.0: offset of LineCollection when apply agg_filter -* :ghissue:`5767`: axes limits (in old "round_numbers" mode) affected by floating point issues -* :ghissue:`5755`: Better choice of axes offset value -* :ghissue:`5938`: possible bug with ax.set_yscale('log') when all values in array are zero -* :ghissue:`5836`: Repeated warning about fc-list -* :ghissue:`6399`: pyparsing version 2.1.2 not supported (2.1.1 works though) -* :ghissue:`5884`: ``numpy`` as no Attribute ``string0`` -* :ghissue:`6395`: Deprecation warning for axes.color_cycle -* :ghissue:`6385`: Possible division by zero in new ``get_tick_space()`` methods; is rotation ignored? -* :ghissue:`6344`: Installation issue -* :ghissue:`6315`: Qt properties editor could sort lines labels using natsort -* :ghissue:`5219`: Notebook backend: possible to remove javascript/html when figure is closed? -* :ghissue:`5111`: nbagg backend captures exceptions raised by callbacks -* :ghissue:`4940`: NBAgg figure management issues -* :ghissue:`4582`: Matplotlib IPython Widget -* :ghissue:`6142`: matplotlib.ticker.LinearLocator view_limits algorithm improvement? -* :ghissue:`6326`: Unicode invisible after image saved -* :ghissue:`5980`: Gridlines on top of plot by default in 2.0? -* :ghissue:`6272`: Ability to set default scatter marker in matplotlibrc -* :ghissue:`6335`: subplots animation example is broken on OS X with qt4agg -* :ghissue:`6357`: pyplot.hist: normalization fails -* :ghissue:`6352`: clim doesn't update after draw -* :ghissue:`6353`: hist won't norm for small numbers -* :ghissue:`6343`: prop_cycle breaks keyword aliases -* :ghissue:`6226`: Issue saving figure as eps when using gouraud shaded triangulation -* :ghissue:`6330`: ticklabel_format reset to default by ScalarFormatter -* :ghissue:`4975`: Non-default ``color_cycle`` not working in Pie plot -* :ghissue:`5990`: Scatter markers do not follow new colour cycle -* :ghissue:`5577`: Handling of "next color in cycle" should be handled differently -* :ghissue:`5489`: Special color names to pull colors from the currently active color cycle -* :ghissue:`6325`: Master requires cycler 0.10.0 -* :ghissue:`6278`: imshow with pgf backend does not render transparency -* :ghissue:`5945`: Figures in the notebook backend are too large following DPI changes -* :ghissue:`6332`: Animation with blit broken -* :ghissue:`6331`: matplotlib pcolormesh seems to slide some data around on the plot -* :ghissue:`6307`: Seaborn style sheets don't edit ``patch.facecolor`` -* :ghissue:`6294`: Zero size ticks show up as single pixels in rendered pdf -* :ghissue:`6318`: Cannot import mpl_toolkits in Python3 -* :ghissue:`6316`: Viridis exists but not in plt.cm.datad.keys() -* :ghissue:`6082`: Cannot interactively edit axes limits using Qt5 backend -* :ghissue:`6309`: Make CheckButtons based on subplots automatically -* :ghissue:`6306`: Can't show images when plt.show() was executed -* :ghissue:`2527`: Vertical alignment of text is too high -* :ghissue:`4827`: Pickled Figure Loses sharedx Properties -* :ghissue:`5998`: \math??{} font styles are ignored in 2.x -* :ghissue:`6293`: matplotlib notebook magic cells with output plots - skips next cell for computation -* :ghissue:`235`: hatch linewidth patch -* :ghissue:`5875`: Manual linestyle specification ignored if 'prop_cycle' contains 'ls' -* :ghissue:`5959`: imshow rendering issue -* :ghissue:`6237`: MacOSX agg version: doesn't redraw after keymap.grid keypress -* :ghissue:`6266`: Better fallback when color is a float -* :ghissue:`6002`: Potential bug with 'start_points' argument of 'pyplot.streamplot' -* :ghissue:`6265`: Document how to set viridis as default colormap in mpl 1.x -* :ghissue:`6258`: Rendering vector graphics: parsing polygons? -* :ghissue:`1702`: Bug in 3D histogram documentation -* :ghissue:`5937`: xticks/yticks default behaviour -* :ghissue:`4706`: Documentation - Basemap -* :ghissue:`6255`: Can't build matplotlib.ft2font in cygwin -* :ghissue:`5792`: Not easy to get colorbar tick mark locations -* :ghissue:`6233`: ImportError from Sphinx plot_directive from Cython -* :ghissue:`6235`: Issue with building docs with Sphinx 1.4.0 -* :ghissue:`4383`: xkcd color names -* :ghissue:`6219`: Example embedding_in_tk.py freezes in Python3.5.1 -* :ghissue:`5067`: improve whats_new entry for prop cycler -* :ghissue:`4614`: Followup items from the matplotlib 2.0 BoF -* :ghissue:`5986`: mac osx backend does not scale dashes by linewidth -* :ghissue:`4680`: Set forward=True by default when setting the figure size -* :ghissue:`4597`: use mkdtemp in _create_tmp_config_dir -* :ghissue:`3437`: Interactive save should respect 'savefig.facecolor' rcParam. -* :ghissue:`2467`: Improve default colors and layouts -* :ghissue:`4194`: matplotlib crashes on OS X when saving to JPEG and then displaying the plot -* :ghissue:`4320`: Pyplot.imshow() "None" interpolation is not supported on Mac OSX -* :ghissue:`1266`: Draggable legend results RuntimeError and AttributeError on Mac OS 10.8.1 -* :ghissue:`5442`: xkcd plots rendered as regular plots on Mac OS X -* :ghissue:`2697`: Path snapping does not respect quantization scale appropriate for Retina displays -* :ghissue:`6049`: Incorrect TextPath display under interactive mode -* :ghissue:`1319`: macosx backend lacks support for cursor-type widgets -* :ghissue:`531`: macosx backend does not work with blitting -* :ghissue:`5964`: slow rendering with backend_macosx on El Capitan -* :ghissue:`5847`: macosx backend color rendering -* :ghissue:`6224`: References to non-existing class FancyBoxPatch -* :ghissue:`781`: macosx backend doesn't find fonts the same way as other backends -* :ghissue:`4271`: general colormap reverser -* :ghissue:`6201`: examples svg_histogram.html failes with UnicodeEncodeError -* :ghissue:`6212`: ENH? BUG? ``pyplot.setp``/``Artist.setp`` does not accept non-indexable iterables of handles. -* :ghissue:`4445`: Two issues with the axes offset indicator -* :ghissue:`6209`: Qt4 backend uses Qt5 backend -* :ghissue:`6136`: Feature request: configure default scatter plot marker size -* :ghissue:`6180`: Minor typos in the style sheets users' guide -* :ghissue:`5517`: "interactive example" not working with PySide -* :ghissue:`4607`: bug in font_manager.FontManager.score_family() -* :ghissue:`4400`: Setting annotation background covers arrow -* :ghissue:`596`: Add "bring window to front" functionality -* :ghissue:`4674`: Default marker edge width in plot vs. scatter -* :ghissue:`5988`: rainbow_text example is missing some text -* :ghissue:`6165`: MacOSX backend hangs drawing lines with many dashes/dots -* :ghissue:`6155`: Deprecation warnings with Dateutil 2.5 -* :ghissue:`6003`: In 'pyplot.streamplot', starting points near the same streamline raise 'InvalidIndexError' -* :ghissue:`6105`: Accepting figure argument in subplot2grid -* :ghissue:`6184`: csv2rec handles dates differently to datetimes when datefirst is specified. -* :ghissue:`6164`: Unable to use PySide with gui=qt -* :ghissue:`6166`: legends do not refresh -* :ghissue:`3897`: bug: inconsistent types accepted in DateLocator subclasses -* :ghissue:`6160`: EPS issues with rc parameters used in seaborn library on Win 8.1 -* :ghissue:`6163`: Can´t make matplotlib run in my computer -* :ghissue:`5331`: Boxplot with zero IQR sets whiskers to max and min and leaves no outliers -* :ghissue:`5575`: plot_date() ignores timezone -* :ghissue:`6143`: drawstyle accepts anything as default rather than raising -* :ghissue:`6151`: Matplotlib 1.5.1 ignores annotation_clip parameter -* :ghissue:`6147`: colormaps issue -* :ghissue:`5916`: Headless get_window_extent or equivalent -* :ghissue:`6141`: Matplotlib subplots and datetime x-axis functionality not working as intended? -* :ghissue:`6138`: No figure shows, no error -* :ghissue:`6134`: Cannot plot a line of width=1 without antialiased -* :ghissue:`6120`: v2.x failures on travis -* :ghissue:`6092`: %matplotlib notebook broken with current matplotlib master -* :ghissue:`1235`: Legend placement bug -* :ghissue:`2499`: Showing np.uint16 images of the form (h,w,3) is broken -* :ghissue:`5479`: Table: auto_set_column_width not working -* :ghissue:`6028`: Appearance of non-math hyphen changes with math in text -* :ghissue:`6113`: ValueError after moving legend and rcParams.update -* :ghissue:`6111`: patches fails when data are array, not list -* :ghissue:`6108`: Plot update issue within event callback for multiple updates -* :ghissue:`6069`: imshow no longer correctly handles 'bad' (``nan``) values -* :ghissue:`6103`: ticklabels empty when not interactive -* :ghissue:`6084`: Despined figure is cropped -* :ghissue:`6067`: pyplot.savefig doesn't expand ~ (tilde) in path -* :ghissue:`4754`: Change default color cycle -* :ghissue:`6063`: Axes.relim() seems not to work when copying Line2D objects -* :ghissue:`6065`: Proposal to change color -- 'indianred' -* :ghissue:`6056`: quiver plot in polar projection - how to make the quiver density latitude-dependent ? -* :ghissue:`6051`: Matplotlib v1.5.1 apparently not compatible with python-dateutil 2.4.2 -* :ghissue:`5513`: Call get_backend in pylab_setup -* :ghissue:`5983`: Option to Compress Graphs for pgf-backend -* :ghissue:`5895`: Polar Projection PDF Issue -* :ghissue:`5948`: tilted line visible in generated pdf file -* :ghissue:`5737`: matplotlib 1.5 compatibility with wxPython -* :ghissue:`5645`: Missing line in a self-sufficient example in navigation_toolbar.rst :: a minor bug in docs -* :ghissue:`6037`: Matplotlib xtick appends .%f after %H:%M%:%S on chart -* :ghissue:`6025`: Exception in Tkinter/to_rgb with new colormaps -* :ghissue:`6034`: colormap name is broken for ListedColormap? -* :ghissue:`5982`: Styles need update after default style changes -* :ghissue:`6017`: Include tests.py in archive of release -* :ghissue:`5520`: 'nearest' interpolation not working with low dpi -* :ghissue:`4280`: imsave reduces 1row from the image -* :ghissue:`3057`: DPI-connected bug of imshow when using multiple masked arrays -* :ghissue:`5490`: Don't interpolate images in RGB space -* :ghissue:`5996`: 2.x: Figure.add_axes(..., facecolor='color') does not set axis background colour -* :ghissue:`4760`: Default linewidth thicker than axes linewidth -* :ghissue:`2698`: ax.text() fails to draw a box if the text content is full of blank spaces and linefeeds. -* :ghissue:`3948`: a weird thing in the source code comments -* :ghissue:`5921`: test_backend.pgf.check_for(texsystem) does not do what it says... -* :ghissue:`4295`: Draggable annotation position wrong with negative x/y -* :ghissue:`1986`: Importing pyplot messes with command line argument parsing -* :ghissue:`5885`: matplotlib stepfilled histogram breaks at the value 10^-1 on xubuntu -* :ghissue:`5050`: pandas v0.17.0rc1 -* :ghissue:`3658`: axes.locator_params fails with LogLocator (and most Locator subclasses) -* :ghissue:`3742`: Square plots -* :ghissue:`3900`: debugging Segmentation fault with Qt5 backend -* :ghissue:`4192`: Error when color value is None -* :ghissue:`4210`: segfault: fill_between with Python3 -* :ghissue:`4325`: FancyBboxPatch wrong size -* :ghissue:`4340`: Histogram gap artifacts -* :ghissue:`5096`: Add xtick.top.visible, xtick.bottom.visible, ytick.left.visible, ytick.right.visible to rcParams -* :ghissue:`5120`: custom axis scale doesn't work in 1.4.3 -* :ghissue:`5212`: shifted(?) bin positions when plotting multiple histograms at the same time -* :ghissue:`5293`: Qt4Agg: RuntimeError: call __init__ twice -* :ghissue:`5971`: Add support for PySide2 (Qt5) -* :ghissue:`5993`: Basemap readshapefile should read shapefile for the long/lat specified in the Basemap instance. -* :ghissue:`5991`: basemap crashes with no error message when passed numpy nan's -* :ghissue:`5883`: New colormaps : Inferno, Viridis, ... -* :ghissue:`5841`: extra label for non-existent tick -* :ghissue:`4502`: Default style proposal: outward tick marks -* :ghissue:`875`: Replace "jet" as the default colormap -* :ghissue:`5047`: Don't draw end caps on error bars by default -* :ghissue:`4700`: Overlay blend mode -* :ghissue:`4671`: Change default legend location to 'best'. -* :ghissue:`5419`: Default setting of figure transparency in NbAgg is a performance problem -* :ghissue:`4815`: Set default axis limits in 2D-plots to the limits of the data -* :ghissue:`4854`: set numpoints to 1 -* :ghissue:`5917`: improved dash styles -* :ghissue:`5900`: Incorrect Image Tutorial Inline Sample Code -* :ghissue:`5965`: xkcd example in gallery -* :ghissue:`5616`: Better error message if no animation writer is available -* :ghissue:`5920`: How to rotate secondary y axis label so it doesn't overlap with y-ticks, matplotlib -* :ghissue:`5966`: SEGFAULT if ``pyplot`` is imported -* :ghissue:`5967`: savefig SVG and PDF output for scatter plots is excessively complex, crashses Inkscape -* :ghissue:`1943`: legend doesn't work with stackplot -* :ghissue:`5923`: Windows usetex=True error in long usernames -* :ghissue:`5940`: KeyError: 'getpwuid(): uid not found: 5001' -* :ghissue:`5748`: Windows test failures on appveyor -* :ghissue:`5944`: Notebook backend broken on Master -* :ghissue:`5946`: Calling subplots_adjust breaks savefig output -* :ghissue:`5929`: Fallback font doesn't work on windows? -* :ghissue:`5925`: Data points beyond axes range plotted when saved to SVG -* :ghissue:`5918`: Pyplot.savefig is very slow with some combinations of data/ylim scales -* :ghissue:`5919`: Error when trying to import matplotlib into IPython notebook -* :ghissue:`5803`: Barbs broken -* :ghissue:`5846`: setupext.py: problems parsing setup.cfg (not updated to changes in configparser) -* :ghissue:`5309`: Differences between function and keywords for savefig.bbox and axes.facecolor -* :ghissue:`5889`: Factual errors in HowTo FAQ Box Plot Image -* :ghissue:`5618`: New rcParams requests -* :ghissue:`5810`: Regression in test_remove_shared_axes -* :ghissue:`5281`: plt.tight_layout(pad=0) cuts away outer ticks -* :ghissue:`5909`: The documentation for LinearLocator's presets keyword is unclear -* :ghissue:`5864`: mathtext mishandling of certain exponents -* :ghissue:`5869`: doc build fails with mpl-1.5.1 and sphinx-1.3.4 (sphinx-1.3.3 is fine) -* :ghissue:`5835`: gridspec.Gridspec doesn't check for consistency in arguments -* :ghissue:`5867`: No transparency in \*.pgf file when using pgf Backend. -* :ghissue:`5863`: \left( ... \right) are too small -* :ghissue:`5850`: prop_cycler for custom dashes -- linestyle such as (, (, )) throws error -* :ghissue:`5861`: Marker style request -* :ghissue:`5851`: Bar and box plots use the 'default' matplotlib colormap, even if the style is changed -* :ghissue:`5857`: FAIL: matplotlib.tests.test_coding_standards.test_pep8_conformance_examples -* :ghissue:`5831`: tests.py is missing from pypi tarball -* :ghissue:`5829`: test_rasterize_dpi fails with 1.5.1 -* :ghissue:`5843`: what is the source code of ax.pcolormesh(T, R, Z,vmin=0,vmax=255,cmap='jet') ? -* :ghissue:`5799`: mathtext kerning around comma -* :ghissue:`2841`: There is no set_linestyle_cycle in the matplotlib axes API -* :ghissue:`5821`: Consider using an offline copy of Raleway font -* :ghissue:`5822`: FuncAnimation.save() only saving 1 frame -* :ghissue:`5449`: Incomplete dependency list for installation from source -* :ghissue:`5793`: GTK backends -* :ghissue:`5814`: Adding colorbars to row subplots doesn't render the main plots when saving to .eps in 1.5.0 -* :ghissue:`5816`: matplotlib.pyplot.boxplot ignored showmeans keyword -* :ghissue:`5086`: Default date format for axis formatting -* :ghissue:`4808`: AutoDateFormatter shows too much precision -* :ghissue:`5812`: Widget event issue -* :ghissue:`5794`: --no-network not recognized as valid option for tests.py -* :ghissue:`5801`: No such file or directory: '/usr/share/matplotlib/stylelib' -* :ghissue:`5777`: Using default style raises warnings about non style parameters -* :ghissue:`5738`: Offset text should be computed based on lowest and highest ticks, not actual axes limits -* :ghissue:`5403`: Document minimal MovieWriter sub-class -* :ghissue:`5558`: The link to the John Hunter Memorial fund is a 404 -* :ghissue:`5757`: Several axes_grid1 and axisartist examples broken on master -* :ghissue:`5557`: plt.hist throws KeyError when passed a pandas.Series without 0 in index -* :ghissue:`5550`: Plotting datetime values from Pandas dataframe -* :ghissue:`4855`: Limit what ``style.use`` can affect? -* :ghissue:`5765`: import matplotlib._png as _png ImportError: libpng16.so.16: cannot open shared object -* :ghissue:`5753`: Handling of zero in log shared axes depends on whether axes are shared -* :ghissue:`5756`: 3D rendering, scatterpoints disapear near edges of surfaces -* :ghissue:`5747`: Figure.suptitle does not respect ``size`` argument -* :ghissue:`5641`: plt.errorbar error with empty list -* :ghissue:`5476`: annotate doesn't trigger redraw -* :ghissue:`5572`: Matplotlib 1.5 broken_barh fails on empty data. -* :ghissue:`5089`: axes.properties calls get_axes internally -* :ghissue:`5745`: Using internal qhull despite the presence of pyqhull installed in the system -* :ghissue:`5744`: cycler is required, is missing, yet build succeeds. -* :ghissue:`5592`: Problem with _init_func in ArtistAnimation -* :ghissue:`5729`: Test matplotlib.tests.test_backend_svg.test_determinism fails on OSX in virtual envs. -* :ghissue:`4756`: font_manager.py takes multiple seconds to import -* :ghissue:`5435`: Unable to upgrade matplotlib 1.5.0 through pip -* :ghissue:`5636`: Generating legend from figure options panel of qt backend raise exception for large number of plots -* :ghissue:`5365`: Warning in test_lines.test_nan_is_sorted -* :ghissue:`5646`: Version the font cache -* :ghissue:`5692`: Can't remove StemContainer -* :ghissue:`5635`: RectangleSelector creates not wanted lines in axes -* :ghissue:`5427`: BUG? Normalize modifies pandas Series inplace -* :ghissue:`5693`: Invalid caching of long lines with nans -* :ghissue:`5705`: doc/users/plotting/examples/axes_zoom_effect.py is not a Python file -* :ghissue:`4359`: savefig crashes with malloc error on os x -* :ghissue:`5715`: Minor error in set up fork -* :ghissue:`5687`: Segfault on plotting with PySide as backend.qt4 -* :ghissue:`5708`: Segfault with Qt4Agg backend in 1.5.0 -* :ghissue:`5704`: Issue with xy and xytext -* :ghissue:`5673`: colorbar labelling bug (1.5 regression) -* :ghissue:`4491`: Document how to get a framework build in a virtual env -* :ghissue:`5468`: axes selection in axes editor -* :ghissue:`5684`: AxesGrid demo exception with LogNorm: 'XAxis' object has no attribute 'set_scale' -* :ghissue:`5663`: AttributeError: 'NoneType' object has no attribute 'canvas' -* :ghissue:`5573`: Support HiDPI (retina) displays in docs -* :ghissue:`5680`: SpanSelector span_stays fails with use_blit=True -* :ghissue:`5679`: Y-axis switches to log scale when an X-axis is shared multiple times. -* :ghissue:`5655`: Problems installing basemap behind a proxy -* :ghissue:`5670`: Doubling of coordinates in polygon clipping -* :ghissue:`4725`: change set_adjustable for share axes with aspect ratio of 1 -* :ghissue:`5488`: The default number of ticks should be based on the length of the axis -* :ghissue:`5543`: num2date ignoring tz in v1.5.0 -* :ghissue:`305`: Change canvas.print_figure default resolution -* :ghissue:`5660`: Cannot raise FileNotFoundError in python2 -* :ghissue:`5658`: A way to remove the image of plt.figimage()? -* :ghissue:`5495`: Something fishy in png reading -* :ghissue:`5549`: test_streamplot:test_colormap test broke unintentionally -* :ghissue:`5381`: HiDPI support in Notebook backend -* :ghissue:`5531`: test_mplot3d:test_quiver3d broke unintentionally -* :ghissue:`5530`: test_axes:test_polar_unit broke unintentionally -* :ghissue:`5525`: Comparison failure in text_axes:test_phase_spectrum_freqs -* :ghissue:`5650`: Wrong backend selection with PyQt4 -* :ghissue:`5649`: Documentation metadata (release version) does not correspond with some of the 'younger' documentation content -* :ghissue:`5648`: Some tests require non-zero tolerance -* :ghissue:`3980`: zoom in wx with retnia behaves badly -* :ghissue:`5642`: Mistype in pyplot_scales.py of pyplot_tutorial.rst :: a minor bug in docs -* :ghissue:`3316`: wx crashes on exit if figure not shown and not explicitly closed -* :ghissue:`5624`: Cannot manually close matplotlib plot window in Mac OS X Yosemite -* :ghissue:`4891`: Better auto-selection of axis limits -* :ghissue:`5633`: No module named externals -* :ghissue:`5634`: No module named 'matplotlib.tests' -* :ghissue:`5473`: Strange OS warning when import pyplot after upgrading to 1.5.0 -* :ghissue:`5524`: Change in colorbar extensions -* :ghissue:`5627`: Followup for Windows CI stuff -* :ghissue:`5613`: Quiverkey() positions arrow incorrectly with labelpos 'N' or 'S' -* :ghissue:`5615`: tornado now a requirement? -* :ghissue:`5582`: FuncAnimation crashes the interpreter (win7, 64bit) -* :ghissue:`5610`: Testfailures on windows -* :ghissue:`5595`: automatically build windows conda packages and wheels in master -* :ghissue:`5535`: test_axes:test_rc_grid image comparison test has always been broken -* :ghissue:`4396`: Qt5 is not mentioned in backends list in doc -* :ghissue:`5205`: pcolor does not handle non-array C data -* :ghissue:`4839`: float repr in axes parameter editing window (aka the green tick button) -* :ghissue:`5542`: Bad superscript positioning for some fonts -* :ghissue:`3791`: Update colormap examples. -* :ghissue:`4679`: Relationship between line-art markers and the markeredgewidth parameter -* :ghissue:`5601`: Scipy/matplotlib recipe with plt.connect() has trouble in python 3 (AnnoteFinder) -* :ghissue:`4211`: Axes3D quiver: variable length arrows -* :ghissue:`773`: mplot3d enhancement -* :ghissue:`395`: need 3D examples for tricontour and tricontourf -* :ghissue:`186`: Axes3D with PolyCollection broken -* :ghissue:`178`: Incorrect mplot3d contourf rendering -* :ghissue:`5508`: Animation.to_html5_video requires python3 base64 module -* :ghissue:`5576`: Improper reliance upon pkg-config when C_INCLUDE_PATH is set -* :ghissue:`5369`: Change in zorder of streamplot between 1.3.1 and 1.4.0 -* :ghissue:`5569`: Stackplot does not handle NaNs -* :ghissue:`5565`: label keyword is not interpreted proporly in errorbar() for pandas.DataFrame-like objects -* :ghissue:`5561`: interactive mode doesn't display images with standard python interpreter -* :ghissue:`5559`: Setting window titles when in interactive mode -* :ghissue:`5554`: Cropping text to axes -* :ghissue:`5545`: EllipseCollection renders incorrectly when passed a sequence of widths -* :ghissue:`5475`: artist picker tolerance has no effect -* :ghissue:`5529`: Wrong image/code for legend_demo (pylab) -* :ghissue:`5139`: plt.subplots for already existing Figure -* :ghissue:`5497`: violin{,plot} return value -* :ghissue:`5441`: boxplot rcParams are not in matplotlibrc.template -* :ghissue:`5522`: axhline fails on custom scale example -* :ghissue:`5528`: $\rho$ in text for plots erroring -* :ghissue:`4799`: Probability axes scales -* :ghissue:`5487`: Trouble importing image_comparison decorator in v1.5 -* :ghissue:`5464`: figaspect not working with numpy floats -* :ghissue:`4487`: Should default hist() bins be changed in 2.0? -* :ghissue:`5499`: UnicodeDecodeError in IPython Notebook caused by negative numbers in plt.legend() -* :ghissue:`5498`: Labels' collisions while plotting named DataFrame iterrows -* :ghissue:`5491`: clippedline.py example should be removed -* :ghissue:`5482`: RuntimeError: could not open display -* :ghissue:`5481`: value error : unknown locale: UTF-8 -* :ghissue:`4780`: Non-interactive backend calls draw more than 100 times -* :ghissue:`5470`: colorbar values could take advantage of offsetting and/or scientific notation -* :ghissue:`5471`: FuncAnimation video saving results in one frame file -* :ghissue:`5457`: Example of new colormaps is misleading -* :ghissue:`3920`: Please fix pip install, so that plt.show() etc works correctly -* :ghissue:`5418`: install backend gtk in Cygwin -* :ghissue:`5368`: New axes.set_prop_cycle method cannot handle any generic iterable -* :ghissue:`5446`: Tests fail to run (killed manually after 7000 sec) -* :ghissue:`5225`: Rare race condition in makedirs with parallel processes -* :ghissue:`5444`: \overline and subscripts/superscripts in mathtext -* :ghissue:`4859`: Call ``tight_layout()`` by default -* :ghissue:`5429`: Segfault in matplotlib.tests.test_image:test_get_window_extent_for_AxisImage on python3.5 -* :ghissue:`5431`: Matplotlib 1.4.3 broken on Windows -* :ghissue:`5409`: Match zdata cursor display scalling with colorbar ? -* :ghissue:`5128`: ENH: Better default font -* :ghissue:`5420`: [Mac OS X 10.10.5] Macports install error :unknown locale: UTF-8 -* :ghissue:`3867`: OSX compile broken since CXX removal (conda only?) -* :ghissue:`5411`: XKCD style fails except for inline mode -* :ghissue:`5406`: Hangs on OS X 10.11.1: No such file or directory: '~/.matplotlib/fontList.json' -* :ghissue:`3116`: mplot3d: argument checking in plot_surface should be improved. -* :ghissue:`347`: Faster Text drawing needed -* :ghissue:`5399`: FuncAnimation w/o init_func breaks when saving -* :ghissue:`5395`: Style changes doc has optimistic release date -* :ghissue:`5393`: wrong legend in errorbar plot for pandas series -* :ghissue:`5396`: fill_between() with gradient -* :ghissue:`5221`: infinite range for hist(histtype="step") -* :ghissue:`4901`: Error running double pendulum animation example -* :ghissue:`3314`: assert mods.pop(0) == 'tests' errors for multiprocess tests on OSX -* :ghissue:`5337`: Remove --nocapture from nosetests on .travis.yml? -* :ghissue:`5378`: errorbar fails with pandas data frame -* :ghissue:`5367`: histogram and digitize do not agree on the definition of a bin -* :ghissue:`5314`: ValueError: insecure string pickle -* :ghissue:`5347`: Problem with importing matplotlib.animation -* :ghissue:`4788`: Modified axes patch will not re-clip artists -* :ghissue:`4968`: Lasso-ing in WxAgg causes flickering of the entire figure diff --git a/doc/users/glossary.rst b/doc/users/glossary.rst new file mode 100644 index 000000000000..8a2a3fd96bd1 --- /dev/null +++ b/doc/users/glossary.rst @@ -0,0 +1,44 @@ +======== +Glossary +======== + +.. Note for glossary authors: + The glossary is primarily intended for Matplotlib's own concepts and + terminology, e.g. figure, artist, backend, etc. We don't want to list + general terms like "GUI framework", "event loop" or similar. + The glossary should contain a short definition of the term, aiming at + a high-level understanding. Use links to redirect to more comprehensive + explanations and API reference when possible. + +This glossary defines concepts and terminology specific to Matplotlib. + +.. glossary:: + + Figure + The outermost container for a Matplotlib graphic. Think of this as the + canvas to draw on. + + This is implemented in the class `.Figure`. For more details see + :ref:`figure-intro`. + + Axes + This is a container for what is often colloquially called a plot/chart/graph. + It's a data area with :term:`Axis`\ es, i.e. coordinate directions, + and includes data artists like lines, bars etc. as well as + decorations like title, axis labels, legend. + + Since most "plotting operations" are realized as methods on `~.axes.Axes` + this is the object users will mostly interact with. + + Note: The term *Axes* was taken over from MATLAB. Think of this as + a container spanned by the *x*- and *y*-axis, including decoration + and data. + + Axis + A direction with a scale. The scale defines the mapping from + data coordinates to screen coordinates. The Axis also includes + the ticks and axis label. + + Artist + The base class for all graphical element that can be drawn. + Examples are Lines, Rectangles, Text, Ticks, Legend, Axes, ... diff --git a/doc/users/gridspec.rst b/doc/users/gridspec.rst deleted file mode 100644 index b946fed05caf..000000000000 --- a/doc/users/gridspec.rst +++ /dev/null @@ -1,161 +0,0 @@ -.. _gridspec-guide: - - -********************************************** -Customizing Location of Subplot Using GridSpec -********************************************** - - ``GridSpec`` - specifies the geometry of the grid that a subplot will be - placed. The number of rows and number of columns of the grid - need to be set. Optionally, the subplot layout parameters - (e.g., left, right, etc.) can be tuned. - - ``SubplotSpec`` - specifies the location of the subplot in the given *GridSpec*. - - ``subplot2grid`` - a helper function that is similar to "pyplot.subplot" but uses - 0-based indexing and let subplot to occupy multiple cells. - - -Basic Example of using subplot2grid -=================================== - -To use subplot2grid, you provide geometry of the grid and the location -of the subplot in the grid. For a simple single-cell subplot:: - - ax = plt.subplot2grid((2,2),(0, 0)) - -is identical to :: - - ax = plt.subplot(2,2,1) - -Note that, unlike matplotlib's subplot, the index starts from 0 in gridspec. - -To create a subplot that spans multiple cells, :: - - ax2 = plt.subplot2grid((3,3), (1, 0), colspan=2) - ax3 = plt.subplot2grid((3,3), (1, 2), rowspan=2) - -For example, the following commands :: - - ax1 = plt.subplot2grid((3,3), (0,0), colspan=3) - ax2 = plt.subplot2grid((3,3), (1,0), colspan=2) - ax3 = plt.subplot2grid((3,3), (1, 2), rowspan=2) - ax4 = plt.subplot2grid((3,3), (2, 0)) - ax5 = plt.subplot2grid((3,3), (2, 1)) - -creates - -.. plot:: users/plotting/examples/demo_gridspec01.py - - -GridSpec and SubplotSpec -======================== - -You can create GridSpec explicitly and use them to create a Subplot. - -For example, :: - - ax = plt.subplot2grid((2,2),(0, 0)) - -is equal to :: - - import matplotlib.gridspec as gridspec - gs = gridspec.GridSpec(2, 2) - ax = plt.subplot(gs[0, 0]) - -A gridspec instance provides array-like (2d or 1d) indexing that -returns the SubplotSpec instance. For, SubplotSpec that spans multiple -cells, use slice. :: - - ax2 = plt.subplot(gs[1,:-1]) - ax3 = plt.subplot(gs[1:, -1]) - -The above example becomes :: - - gs = gridspec.GridSpec(3, 3) - ax1 = plt.subplot(gs[0, :]) - ax2 = plt.subplot(gs[1,:-1]) - ax3 = plt.subplot(gs[1:, -1]) - ax4 = plt.subplot(gs[-1,0]) - ax5 = plt.subplot(gs[-1,-2]) - -.. plot:: users/plotting/examples/demo_gridspec02.py - -Adjust GridSpec layout -====================== - -When a GridSpec is explicitly used, you can adjust the layout -parameters of subplots that are created from the gridspec. :: - - gs1 = gridspec.GridSpec(3, 3) - gs1.update(left=0.05, right=0.48, wspace=0.05) - -This is similar to *subplots_adjust*, but it only affects the subplots -that are created from the given GridSpec. - -The code below :: - - gs1 = gridspec.GridSpec(3, 3) - gs1.update(left=0.05, right=0.48, wspace=0.05) - ax1 = plt.subplot(gs1[:-1, :]) - ax2 = plt.subplot(gs1[-1, :-1]) - ax3 = plt.subplot(gs1[-1, -1]) - - gs2 = gridspec.GridSpec(3, 3) - gs2.update(left=0.55, right=0.98, hspace=0.05) - ax4 = plt.subplot(gs2[:, :-1]) - ax5 = plt.subplot(gs2[:-1, -1]) - ax6 = plt.subplot(gs2[-1, -1]) - -creates - -.. plot:: users/plotting/examples/demo_gridspec03.py - -GridSpec using SubplotSpec -========================== - -You can create GridSpec from the SubplotSpec, in which case its layout -parameters are set to that of the location of the given SubplotSpec. :: - - gs0 = gridspec.GridSpec(1, 2) - - gs00 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[0]) - gs01 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[1]) - - -.. plot:: users/plotting/examples/demo_gridspec04.py - - -A Complex Nested GridSpec using SubplotSpec -=========================================== - -Here's a more sophisticated example of nested gridspec where we put -a box around each cell of the outer 4x4 grid, by hiding appropriate -spines in each of the inner 3x3 grids. - -.. plot:: users/plotting/examples/demo_gridspec06.py - - -GridSpec with Varying Cell Sizes -================================ - -By default, GridSpec creates cells of equal sizes. You can adjust -relative heights and widths of rows and columns. Note that absolute -values are meaningless, only their relative ratios matter. :: - - gs = gridspec.GridSpec(2, 2, - width_ratios=[1,2], - height_ratios=[4,1] - ) - - ax1 = plt.subplot(gs[0]) - ax2 = plt.subplot(gs[1]) - ax3 = plt.subplot(gs[2]) - ax4 = plt.subplot(gs[3]) - - -.. plot:: users/plotting/examples/demo_gridspec05.py - diff --git a/doc/users/image_tutorial.rst b/doc/users/image_tutorial.rst deleted file mode 100644 index e4a3b07982ba..000000000000 --- a/doc/users/image_tutorial.rst +++ /dev/null @@ -1,384 +0,0 @@ -.. _image_tutorial: - - -************** -Image tutorial -************** - -.. _imaging_startup: - -Startup commands -=================== - -First, let's start IPython. It is a most excellent enhancement to the -standard Python prompt, and it ties in especially well with -Matplotlib. Start IPython either at a shell, or the IPython Notebook now. - -With IPython started, we now need to connect to a GUI event loop. This -tells IPython where (and how) to display plots. To connect to a GUI -loop, execute the **%matplotlib** magic at your IPython prompt. There's more -detail on exactly what this does at `IPython's documentation on GUI -event loops -`_. - -If you're using IPython Notebook, the same commands are available, but -people commonly use a specific argument to the %matplotlib magic: - -.. sourcecode:: ipython - - In [1]: %matplotlib inline - -This turns on inline plotting, where plot graphics will appear in your -notebook. This has important implications for interactivity. For inline plotting, commands in -cells below the cell that outputs a plot will not affect the plot. For example, -changing the color map is not possible from cells below the cell that creates a plot. -However, for other backends, such as qt4, that open a separate window, -cells below those that create the plot will change the plot - it is a -live object in memory. - -This tutorial will use matplotlib's imperative-style plotting -interface, pyplot. This interface maintains global state, and is very -useful for quickly and easily experimenting with various plot -settings. The alternative is the object-oriented interface, which is also -very powerful, and generally more suitable for large application -development. If you'd like to learn about the object-oriented -interface, a great place to start is our `FAQ on usage -`_. For now, let's get on -with the imperative-style approach: - -.. sourcecode:: ipython - - In [2]: import matplotlib.pyplot as plt - In [3]: import matplotlib.image as mpimg - In [4]: import numpy as np - -.. _importing_data: - -Importing image data into Numpy arrays -=============================================== - -Loading image data is supported by the `Pillow -`_ library. Natively, matplotlib only -supports PNG images. The commands shown below fall back on Pillow if the -native read fails. - -The image used in this example is a PNG file, but keep that Pillow -requirement in mind for your own data. - -Here's the image we're going to play with: - -.. image:: ../_static/stinkbug.png - -It's a 24-bit RGB PNG image (8 bits for each of R, G, B). Depending -on where you get your data, the other kinds of image that you'll most -likely encounter are RGBA images, which allow for transparency, or -single-channel grayscale (luminosity) images. You can right click on -it and choose "Save image as" to download it to your computer for the -rest of this tutorial. - -And here we go... - -.. sourcecode:: ipython - - In [5]: img=mpimg.imread('stinkbug.png') - Out[5]: - array([[[ 0.40784314, 0.40784314, 0.40784314], - [ 0.40784314, 0.40784314, 0.40784314], - [ 0.40784314, 0.40784314, 0.40784314], - ..., - [ 0.42745098, 0.42745098, 0.42745098], - [ 0.42745098, 0.42745098, 0.42745098], - [ 0.42745098, 0.42745098, 0.42745098]], - - ..., - [[ 0.44313726, 0.44313726, 0.44313726], - [ 0.4509804 , 0.4509804 , 0.4509804 ], - [ 0.4509804 , 0.4509804 , 0.4509804 ], - ..., - [ 0.44705883, 0.44705883, 0.44705883], - [ 0.44705883, 0.44705883, 0.44705883], - [ 0.44313726, 0.44313726, 0.44313726]]], dtype=float32) - -Note the dtype there - float32. Matplotlib has rescaled the 8 bit -data from each channel to floating point data between 0.0 and 1.0. As -a side note, the only datatype that Pillow can work with is uint8. -Matplotlib plotting can handle float32 and uint8, but image -reading/writing for any format other than PNG is limited to uint8 -data. Why 8 bits? Most displays can only render 8 bits per channel -worth of color gradation. Why can they only render 8 bits/channel? -Because that's about all the human eye can see. More here (from a -photography standpoint): `Luminous Landscape bit depth tutorial -`_. - -Each inner list represents a pixel. Here, with an RGB image, there -are 3 values. Since it's a black and white image, R, G, and B are all -similar. An RGBA (where A is alpha, or transparency), has 4 values -per inner list, and a simple luminance image just has one value (and -is thus only a 2-D array, not a 3-D array). For RGB and RGBA images, -matplotlib supports float32 and uint8 data types. For grayscale, -matplotlib supports only float32. If your array data does not meet -one of these descriptions, you need to rescale it. - -.. _plotting_data: - -Plotting numpy arrays as images -=================================== - -So, you have your data in a numpy array (either by importing it, or by -generating it). Let's render it. In Matplotlib, this is performed -using the :func:`~matplotlib.pyplot.imshow` function. Here we'll grab -the plot object. This object gives you an easy way to manipulate the -plot from the prompt. - -.. sourcecode:: ipython - - In [6]: imgplot = plt.imshow(img) - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - imgplot = plt.imshow(img) - -You can also plot any numpy array. - -.. _Pseudocolor: - -Applying pseudocolor schemes to image plots -------------------------------------------------- - -Pseudocolor can be a useful tool for enhancing contrast and -visualizing your data more easily. This is especially useful when -making presentations of your data using projectors - their contrast is -typically quite poor. - -Pseudocolor is only relevant to single-channel, grayscale, luminosity -images. We currently have an RGB image. Since R, G, and B are all -similar (see for yourself above or in your data), we can just pick one -channel of our data: - -.. sourcecode:: ipython - - In [7]: lum_img = img[:,:,0] - -This is array slicing. You can read more in the `Numpy tutorial -`_. - -.. sourcecode:: ipython - - In [8]: plt.imshow(lum_img) - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:, :, 0] - plt.imshow(lum_img) - -Now, with a luminosity (2D, no color) image, the default colormap (aka lookup table, -LUT), is applied. The default is called jet. There are plenty of -others to choose from. - -.. sourcecode:: ipython - - In [9]: plt.imshow(lum_img, cmap="hot") - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:,:,0] - imgplot = plt.imshow(lum_img) - imgplot.set_cmap('hot') - -Note that you can also change colormaps on existing plot objects using the -:meth:`~matplotlib.image.Image.set_cmap` method: - -.. sourcecode:: ipython - - In [10]: imgplot = plt.imshow(lum_img) - In [11]: imgplot.set_cmap('spectral') - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:, :, 0] - imgplot = plt.imshow(lum_img) - imgplot.set_cmap('spectral') - -.. note:: - - However, remember that in the IPython notebook with the inline backend, - you can't make changes to plots that have already been rendered. If you - create imgplot here in one cell, you cannot call set_cmap() on it in a later - cell and expect the earlier plot to change. Make sure that you enter these - commands together in one cell. plt commands will not change plots from earlier - cells. - -There are many other colormap schemes available. See the `list and -images of the colormaps -<../examples/color/colormaps_reference.html>`_. - -.. _`Color Bars`: - -Color scale reference ------------------------- - -It's helpful to have an idea of what value a color represents. We can -do that by adding color bars. - -.. sourcecode:: ipython - - In [12]: imgplot = plt.imshow(lum_img) - In [13]: plt.colorbar() - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:, :, 0] - imgplot = plt.imshow(lum_img) - imgplot.set_cmap('spectral') - plt.colorbar() - -This adds a colorbar to your existing figure. This won't -automatically change if you change you switch to a different -colormap - you have to re-create your plot, and add in the colorbar -again. - -.. _`Data ranges`: - -Examining a specific data range ---------------------------------- - -Sometimes you want to enhance the contrast in your image, or expand -the contrast in a particular region while sacrificing the detail in -colors that don't vary much, or don't matter. A good tool to find -interesting regions is the histogram. To create a histogram of our -image data, we use the :func:`~matplotlib.pyplot.hist` function. - -.. sourcecode:: ipython - - In [14]: plt.hist(lum_img.ravel(), bins=256, range=(0.0, 1.0), fc='k', ec='k') - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:,:,0] - plt.hist(lum_img.flatten(), 256, range=(0.0, 1.0), fc='k', ec='k') - -Most often, the "interesting" part of the image is around the peak, -and you can get extra contrast by clipping the regions above and/or -below the peak. In our histogram, it looks like there's not much -useful information in the high end (not many white things in the -image). Let's adjust the upper limit, so that we effectively "zoom in -on" part of the histogram. We do this by passing the clim argument to -imshow. You could also do this by calling the -:meth:`~matplotlib.image.Image.set_clim` method of the image plot -object, but make sure that you do so in the same cell as your plot -command when working with the IPython Notebook - it will not change -plots from earlier cells. - -.. sourcecode:: ipython - - In [15]: imgplot = plt.imshow(lum_img, clim=(0.0, 0.7)) - -.. plot:: - - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - fig = plt.figure() - a=fig.add_subplot(1,2,1) - img = mpimg.imread('../_static/stinkbug.png') - lum_img = img[:,:,0] - imgplot = plt.imshow(lum_img) - a.set_title('Before') - plt.colorbar(ticks=[0.1,0.3,0.5,0.7], orientation ='horizontal') - a=fig.add_subplot(1,2,2) - imgplot = plt.imshow(lum_img) - imgplot.set_clim(0.0,0.7) - a.set_title('After') - plt.colorbar(ticks=[0.1,0.3,0.5,0.7], orientation='horizontal') - -.. _Interpolation: - -Array Interpolation schemes ---------------------------- - -Interpolation calculates what the color or value of a pixel "should" -be, according to different mathematical schemes. One common place -that this happens is when you resize an image. The number of pixels -change, but you want the same information. Since pixels are discrete, -there's missing space. Interpolation is how you fill that space. -This is why your images sometimes come out looking pixelated when you -blow them up. The effect is more pronounced when the difference -between the original image and the expanded image is greater. Let's -take our image and shrink it. We're effectively discarding pixels, -only keeping a select few. Now when we plot it, that data gets blown -up to the size on your screen. The old pixels aren't there anymore, -and the computer has to draw in pixels to fill that space. - -We'll use the Pillow library that we used to load the image also to resize -the image. - -.. sourcecode:: ipython - - In [16]: from PIL import Image - In [17]: img = Image.open('../_static/stinkbug.png') - In [18]: img.thumbnail((64, 64), Image.ANTIALIAS) # resizes image in-place - In [19]: imgplot = plt.imshow(img) - -.. plot:: - - import matplotlib.pyplot as plt - from PIL import Image - img = Image.open('../_static/stinkbug.png') # opens the file using Pillow - it's not an array yet - img.thumbnail((64, 64), Image.ANTIALIAS) # resizes image in-place - imgplot = plt.imshow(img) - -Here we have the default interpolation, bilinear, since we did not -give :func:`~matplotlib.pyplot.imshow` any interpolation argument. - -Let's try some others: - -.. sourcecode:: ipython - - In [20]: imgplot = plt.imshow(img, interpolation="nearest") - -.. plot:: - - import matplotlib.pyplot as plt - from PIL import Image - img = Image.open('../_static/stinkbug.png') # opens the file using Pillow - it's not an array yet - img.thumbnail((64, 64), Image.ANTIALIAS) # resizes image in-place - imgplot = plt.imshow(img, interpolation="nearest") - -.. sourcecode:: ipython - - In [21]: imgplot = plt.imshow(img, interpolation="bicubic") - -.. plot:: - - import matplotlib.pyplot as plt - from PIL import Image - img = Image.open('../_static/stinkbug.png') # opens the file using Pillow - it's not an array yet - img.thumbnail((64, 64), Image.ANTIALIAS) # resizes image in-place - imgplot = plt.imshow(img, interpolation="bicubic") - -Bicubic interpolation is often used when blowing up photos - people -tend to prefer blurry over pixelated. diff --git a/doc/users/index.rst b/doc/users/index.rst index 566205ba8a3a..b98bda824a7e 100644 --- a/doc/users/index.rst +++ b/doc/users/index.rst @@ -1,26 +1,107 @@ + .. _users-guide-index: -############ -User's Guide -############ +.. redirect-from:: /contents +.. redirect-from:: /users/explain + + +Using Matplotlib +================ + +.. grid:: 1 1 2 2 + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/quick_start + + .. toctree:: + :maxdepth: 1 + + faq.rst + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/figure/index + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/axes/index + + + .. grid-item-card:: + :padding: 2 -.. htmlonly:: + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/artists/index + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/configuration + explain/customizing + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/colors/index + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/text/index + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/animations/index + + .. grid-item-card:: + :padding: 2 + + .. toctree:: + :maxdepth: 2 + :includehidden: + + explain/toolkits/index - :Release: |version| - :Date: |today| .. toctree:: - :maxdepth: 2 - - intro.rst - installing.rst - tutorials.rst - index_text.rst - color_index.rst - customizing.rst - interactive.rst - examples_index - whats_new.rst - github_stats.rst - license.rst - credits.rst + :hidden: + + getting_started/index + ../install/index + glossary diff --git a/doc/users/index_text.rst b/doc/users/index_text.rst deleted file mode 100644 index 7904d7e3173e..000000000000 --- a/doc/users/index_text.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _text-guide: - -Working with text -################# - -.. toctree:: - - text_intro.rst - text_props.rst - annotations.rst - mathtext.rst - usetex.rst - pgf.rst diff --git a/doc/users/interactive.rst b/doc/users/interactive.rst deleted file mode 100644 index e8af8df9409e..000000000000 --- a/doc/users/interactive.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _dump-index: - -=================== - Interactive plots -=================== - -.. toctree:: - :maxdepth: 2 - - navigation_toolbar.rst - shell.rst - event_handling.rst diff --git a/doc/users/intro.rst b/doc/users/intro.rst deleted file mode 100644 index 9e1a109fca2a..000000000000 --- a/doc/users/intro.rst +++ /dev/null @@ -1,94 +0,0 @@ -Introduction -============ - -matplotlib is a library for making 2D plots of arrays in `Python -`_. Although it has its origins in emulating -the MATLAB |reg| [#]_ graphics commands, it is -independent of MATLAB, and can be used in a Pythonic, object oriented -way. Although matplotlib is written primarily in pure Python, it -makes heavy use of `NumPy `_ and other extension -code to provide good performance even for large arrays. - -.. |reg| unicode:: 0xAE - :ltrim: - -matplotlib is designed with the philosophy that you should be able to -create simple plots with just a few commands, or just one! If you -want to see a histogram of your data, you shouldn't need to -instantiate objects, call methods, set properties, and so on; it -should just work. - -For years, I used to use MATLAB exclusively for data analysis and -visualization. MATLAB excels at making nice looking plots easy. When -I began working with EEG data, I found that I needed to write -applications to interact with my data, and developed an EEG analysis -application in MATLAB. As the application grew in complexity, -interacting with databases, http servers, manipulating complex data -structures, I began to strain against the limitations of MATLAB as a -programming language, and decided to start over in Python. Python -more than makes up for all of MATLAB's deficiencies as a programming -language, but I was having difficulty finding a 2D plotting package -(for 3D `VTK `_ more than exceeds all of my -needs). - -When I went searching for a Python plotting package, I had several -requirements: - -* Plots should look great - publication quality. One important - requirement for me is that the text looks good (antialiased, etc.) - -* Postscript output for inclusion with TeX documents - -* Embeddable in a graphical user interface for application - development - -* Code should be easy enough that I can understand it and extend - it - -* Making plots should be easy - -Finding no package that suited me just right, I did what any -self-respecting Python programmer would do: rolled up my sleeves and -dived in. Not having any real experience with computer graphics, I -decided to emulate MATLAB's plotting capabilities because that is -something MATLAB does very well. This had the added advantage that -many people have a lot of MATLAB experience, and thus they can -quickly get up to steam plotting in python. From a developer's -perspective, having a fixed user interface (the pylab interface) has -been very useful, because the guts of the code base can be redesigned -without affecting user code. - -The matplotlib code is conceptually divided into three parts: the -*pylab interface* is the set of functions provided by -:mod:`matplotlib.pylab` which allow the user to create plots with code -quite similar to MATLAB figure generating code -(:ref:`pyplot-tutorial`). The *matplotlib frontend* or *matplotlib -API* is the set of classes that do the heavy lifting, creating and -managing figures, text, lines, plots and so on -(:ref:`artist-tutorial`). This is an abstract interface that knows -nothing about output. The *backends* are device-dependent drawing -devices, aka renderers, that transform the frontend representation to -hardcopy or a display device (:ref:`what-is-a-backend`). Example -backends: PS creates `PostScript® -`_ hardcopy, SVG -creates `Scalable Vector Graphics `_ -hardcopy, Agg creates PNG output using the high quality `Anti-Grain -Geometry `_ -library that ships with matplotlib, GTK embeds matplotlib in a -`Gtk+ `_ -application, GTKAgg uses the Anti-Grain renderer to create a figure -and embed it in a Gtk+ application, and so on for `PDF -`_, `WxWidgets -`_, `Tkinter -`_, etc. - -matplotlib is used by many people in many different contexts. Some -people want to automatically generate PostScript files to send -to a printer or publishers. Others deploy matplotlib on a web -application server to generate PNG output for inclusion in -dynamically-generated web pages. Some use matplotlib interactively -from the Python shell in Tkinter on Windows™. My primary use is to -embed matplotlib in a Gtk+ EEG application that runs on Windows, Linux -and Macintosh OS X. - -.. [#] MATLAB is a registered trademark of The MathWorks, Inc. diff --git a/doc/users/legend_guide.rst b/doc/users/legend_guide.rst deleted file mode 100644 index 3c204a640e90..000000000000 --- a/doc/users/legend_guide.rst +++ /dev/null @@ -1,296 +0,0 @@ -.. _plotting-guide-legend: - -************ -Legend guide -************ - -.. currentmodule:: matplotlib.pyplot - - -This legend guide is an extension of the documentation available at -:func:`~matplotlib.pyplot.legend` - please ensure you are familiar with -contents of that documentation before proceeding with this guide. - - -This guide makes use of some common terms, which are documented here for clarity: - -.. glossary:: - - legend entry - A legend is made up of one or more legend entries. An entry is made up of - exactly one key and one label. - - legend key - The colored/patterned marker to the left of each legend label. - - legend label - The text which describes the handle represented by the key. - - legend handle - The original object which is used to generate an appropriate entry in - the legend. - - -Controlling the legend entries -============================== - -Calling :func:`legend` with no arguments automatically fetches the legend -handles and their associated labels. This functionality is equivalent to:: - - handles, labels = ax.get_legend_handles_labels() - ax.legend(handles, labels) - -The :meth:`~matplotlib.axes.Axes.get_legend_handles_labels` function returns -a list of handles/artists which exist on the Axes which can be used to -generate entries for the resulting legend - it is worth noting however that -not all artists can be added to a legend, at which point a "proxy" will have -to be created (see :ref:`proxy_legend_handles` for further details). - -For full control of what is being added to the legend, it is common to pass -the appropriate handles directly to :func:`legend`:: - - line_up, = plt.plot([1,2,3], label='Line 2') - line_down, = plt.plot([3,2,1], label='Line 1') - plt.legend(handles=[line_up, line_down]) - -In some cases, it is not possible to set the label of the handle, so it is -possible to pass through the list of labels to :func:`legend`:: - - line_up, = plt.plot([1,2,3], label='Line 2') - line_down, = plt.plot([3,2,1], label='Line 1') - plt.legend([line_up, line_down], ['Line Up', 'Line Down']) - - -.. _proxy_legend_handles: - -Creating artists specifically for adding to the legend (aka. Proxy artists) -=========================================================================== - -Not all handles can be turned into legend entries automatically, -so it is often necessary to create an artist which *can*. Legend handles -don't have to exists on the Figure or Axes in order to be used. - -Suppose we wanted to create a legend which has an entry for some data which -is represented by a red color: - -.. plot:: - :include-source: - - import matplotlib.patches as mpatches - import matplotlib.pyplot as plt - - red_patch = mpatches.Patch(color='red', label='The red data') - plt.legend(handles=[red_patch]) - - plt.show() - -There are many supported legend handles, instead of creating a patch of color -we could have created a line with a marker: - -.. plot:: - :include-source: - - import matplotlib.lines as mlines - import matplotlib.pyplot as plt - - blue_line = mlines.Line2D([], [], color='blue', marker='*', - markersize=15, label='Blue stars') - plt.legend(handles=[blue_line]) - - plt.show() - - -Legend location -=============== - -The location of the legend can be specified by the keyword argument -*loc*. Please see the documentation at :func:`legend` for more details. - -The ``bbox_to_anchor`` keyword gives a great degree of control for manual -legend placement. For example, if you want your axes legend located at the -figure's top right-hand corner instead of the axes' corner, simply specify -the corner's location, and the coordinate system of that location:: - - plt.legend(bbox_to_anchor=(1, 1), - bbox_transform=plt.gcf().transFigure) - -More examples of custom legend placement: - -.. plot:: users/plotting/examples/simple_legend01.py - :include-source: - - -Multiple legends on the same Axes -================================= - -Sometimes it is more clear to split legend entries across multiple -legends. Whilst the instinctive approach to doing this might be to call -the :func:`legend` function multiple times, you will find that only one -legend ever exists on the Axes. This has been done so that it is possible -to call :func:`legend` repeatedly to update the legend to the latest -handles on the Axes, so to persist old legend instances, we must add them -manually to the Axes: - -.. plot:: users/plotting/examples/simple_legend02.py - :include-source: - -Legend Handlers -=============== - -In order to create legend entries, handles are given as an argument to an -appropriate :class:`~matplotlib.legend_handler.HandlerBase` subclass. -The choice of handler subclass is determined by the following rules: - - 1. Update :func:`~matplotlib.legend.Legend.get_legend_handler_map` - with the value in the ``handler_map`` keyword. - 2. Check if the ``handle`` is in the newly created ``handler_map``. - 3. Check if the type of ``handle`` is in the newly created - ``handler_map``. - 4. Check if any of the types in the ``handle``'s mro is in the newly - created ``handler_map``. - -For completeness, this logic is mostly implemented in -:func:`~matplotlib.legend.Legend.get_legend_handler`. - -All of this flexibility means that we have the necessary hooks to implement -custom handlers for our own type of legend key. - -The simplest example of using custom handlers is to instantiate one of the -existing :class:`~matplotlib.legend_handler.HandlerBase` subclasses. For the -sake of simplicity, let's choose :class:`matplotlib.legend_handler.HandlerLine2D` -which accepts a ``numpoints`` argument (note numpoints is a keyword -on the :func:`legend` function for convenience). We can then pass the mapping -of instance to Handler as a keyword to legend. - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from matplotlib.legend_handler import HandlerLine2D - - line1, = plt.plot([3,2,1], marker='o', label='Line 1') - line2, = plt.plot([1,2,3], marker='o', label='Line 2') - - plt.legend(handler_map={line1: HandlerLine2D(numpoints=4)}) - -As you can see, "Line 1" now has 4 marker points, where "Line 2" has 2 (the -default). Try the above code, only change the map's key from ``line1`` to -``type(line1)``. Notice how now both :class:`~matplotlib.lines.Line2D` instances -get 4 markers. - -Along with handlers for complex plot types such as errorbars, stem plots -and histograms, the default ``handler_map`` has a special ``tuple`` handler -(:class:`~matplotlib.legend_handler.HandlerTuple`) which simply plots -the handles on top of one another for each item in the given tuple. The -following example demonstrates combining two legend keys on top of one another: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from numpy.random import randn - - z = randn(10) - - red_dot, = plt.plot(z, "ro", markersize=15) - # Put a white cross over some of the data. - white_cross, = plt.plot(z[:5], "w+", markeredgewidth=3, markersize=15) - - plt.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"]) - -The :class:`~matplotlib.legend_handler.HandlerTuple` class can also be used to -assign several legend keys to the same entry: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from matplotlib.legend_handler import HandlerLine2D, HandlerTuple - - p1, = plt.plot([1, 2.5, 3], 'r-d') - p2, = plt.plot([3, 2, 1], 'k-o') - - l = plt.legend([(p1, p2)], ['Two keys'], numpoints=1, - handler_map={tuple: HandlerTuple(ndivide=None)}) - - -Implementing a custom legend handler ------------------------------------- - -A custom handler can be implemented to turn any handle into a legend key (handles -don't necessarily need to be matplotlib artists). -The handler must implement a "legend_artist" method which returns a -single artist for the legend to use. Signature details about the "legend_artist" -are documented at :meth:`~matplotlib.legend_handler.HandlerBase.legend_artist`. - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import matplotlib.patches as mpatches - - class AnyObject(object): - pass - - class AnyObjectHandler(object): - def legend_artist(self, legend, orig_handle, fontsize, handlebox): - x0, y0 = handlebox.xdescent, handlebox.ydescent - width, height = handlebox.width, handlebox.height - patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red', - edgecolor='black', hatch='xx', lw=3, - transform=handlebox.get_transform()) - handlebox.add_artist(patch) - return patch - - plt.legend([AnyObject()], ['My first handler'], - handler_map={AnyObject: AnyObjectHandler()}) - -Alternatively, had we wanted to globally accept ``AnyObject`` instances without -needing to manually set the ``handler_map`` keyword all the time, we could have -registered the new handler with:: - - from matplotlib.legend import Legend - Legend.update_default_handler_map({AnyObject: AnyObjectHandler()}) - -Whilst the power here is clear, remember that there are already many handlers -implemented and what you want to achieve may already be easily possible with -existing classes. For example, to produce elliptical legend keys, rather than -rectangular ones: - -.. plot:: - :include-source: - - from matplotlib.legend_handler import HandlerPatch - import matplotlib.pyplot as plt - import matplotlib.patches as mpatches - - - class HandlerEllipse(HandlerPatch): - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent - p = mpatches.Ellipse(xy=center, width=width + xdescent, - height=height + ydescent) - self.update_prop(p, orig_handle, legend) - p.set_transform(trans) - return [p] - - - c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green", - edgecolor="red", linewidth=3) - plt.gca().add_patch(c) - - plt.legend([c], ["An ellipse, not a rectangle"], - handler_map={mpatches.Circle: HandlerEllipse()}) - -Known examples of using legend -============================== - -Here is a non-exhaustive list of the examples available involving legend -being used in various ways: - -* :ref:`lines_bars_and_markers-scatter_with_legend` -* :ref:`api-legend_demo` -* :ref:`pylab_examples-contourf_hatching` -* :ref:`pylab_examples-figlegend_demo` -* :ref:`pylab_examples-scatter_symbol` diff --git a/doc/users/license.rst b/doc/users/license.rst deleted file mode 100644 index 65f9cc78708e..000000000000 --- a/doc/users/license.rst +++ /dev/null @@ -1,141 +0,0 @@ -.. _license: - -*********************************************** -License -*********************************************** - - -Matplotlib only uses BSD compatible code, and its license is based on -the `PSF `_ license. See the Open -Source Initiative `licenses page -`_ for details on individual -licenses. Non-BSD compatible licenses (e.g., LGPL) are acceptable in -matplotlib toolkits. For a discussion of the motivations behind the -licencing choice, see :ref:`license-discussion`. - -Copyright Policy -================ - -John Hunter began matplotlib around 2003. Since shortly before his -passing in 2012, Michael Droettboom has been the lead maintainer of -matplotlib, but, as has always been the case, matplotlib is the work -of many. - -Prior to July of 2013, and the 1.3.0 release, the copyright of the -source code was held by John Hunter. As of July 2013, and the 1.3.0 -release, matplotlib has moved to a shared copyright model. - -matplotlib uses a shared copyright model. Each contributor maintains -copyright over their contributions to matplotlib. But, it is important to -note that these contributions are typically only changes to the -repositories. Thus, the matplotlib source code, in its entirety, is not -the copyright of any single person or institution. Instead, it is the -collective copyright of the entire matplotlib Development Team. If -individual contributors want to maintain a record of what -changes/contributions they have specific copyright on, they should -indicate their copyright in the commit message of the change, when -they commit the change to one of the matplotlib repositories. - -The Matplotlib Development Team is the set of all contributors to the -matplotlib project. A full list can be obtained from the git version -control logs. - -License agreement for matplotlib |version| -============================================== - -1. This LICENSE AGREEMENT is between the Matplotlib Development Team -("MDT"), and the Individual or Organization ("Licensee") accessing and -otherwise using matplotlib software in source or binary form and its -associated documentation. - -2. Subject to the terms and conditions of this License Agreement, MDT -hereby grants Licensee a nonexclusive, royalty-free, world-wide license -to reproduce, analyze, test, perform and/or display publicly, prepare -derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that MDT's -License Agreement and MDT's notice of copyright, i.e., "Copyright (c) -2012-2013 Matplotlib Development Team; All Rights Reserved" are retained in -matplotlib |version| alone or in any derivative version prepared by -Licensee. - -3. In the event Licensee prepares a derivative work that is based on or -incorporates matplotlib |version| or any part thereof, and wants to -make the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to matplotlib |version|. - -4. MDT is making matplotlib |version| available to Licensee on an "AS -IS" basis. MDT MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, MDT MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| -WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. MDT SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB -|version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR -LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING -MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF -THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between MDT and -Licensee. This License Agreement does not grant permission to use MDT -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using matplotlib |version|, -Licensee agrees to be bound by the terms and conditions of this License -Agreement. - -License agreement for matplotlib versions prior to 1.3.0 -======================================================== - -1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the -Individual or Organization ("Licensee") accessing and otherwise using -matplotlib software in source or binary form and its associated -documentation. - -2. Subject to the terms and conditions of this License Agreement, JDH -hereby grants Licensee a nonexclusive, royalty-free, world-wide license -to reproduce, analyze, test, perform and/or display publicly, prepare -derivative works, distribute, and otherwise use matplotlib |version| -alone or in any derivative version, provided, however, that JDH's -License Agreement and JDH's notice of copyright, i.e., "Copyright (c) -2002-2009 John D. Hunter; All Rights Reserved" are retained in -matplotlib |version| alone or in any derivative version prepared by -Licensee. - -3. In the event Licensee prepares a derivative work that is based on or -incorporates matplotlib |version| or any part thereof, and wants to -make the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to matplotlib |version|. - -4. JDH is making matplotlib |version| available to Licensee on an "AS -IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB |version| -WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB -|version| FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR -LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING -MATPLOTLIB |version|, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF -THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between JDH and -Licensee. This License Agreement does not grant permission to use JDH -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using matplotlib |version|, -Licensee agrees to be bound by the terms and conditions of this License -Agreement. diff --git a/doc/users/mathtext.rst b/doc/users/mathtext.rst deleted file mode 100644 index e34f574d9e85..000000000000 --- a/doc/users/mathtext.rst +++ /dev/null @@ -1,344 +0,0 @@ -.. _mathtext-tutorial: - -Writing mathematical expressions -================================ - -You can use a subset TeX markup in any matplotlib text string by -placing it inside a pair of dollar signs ($). - -Note that you do not need to have TeX installed, since matplotlib -ships its own TeX expression parser, layout engine and fonts. The -layout engine is a fairly direct adaptation of the layout algorithms -in Donald Knuth's TeX, so the quality is quite good (matplotlib also -provides a ``usetex`` option for those who do want to call out to TeX -to generate their text (see :ref:`usetex-tutorial`). - -Any text element can use math text. You should use raw strings (precede the -quotes with an ``'r'``), and surround the math text with dollar signs ($), as in -TeX. Regular text and mathtext can be interleaved within the same string. -Mathtext can use DejaVu Sans (default), DejaVu Serif, the Computer Modern fonts -(from (La)TeX), `STIX `_ fonts (with are designed -to blend well with Times), or a Unicode font that you provide. The mathtext -font can be selected with the customization variable ``mathtext.fontset`` (see -:ref:`customizing-matplotlib`) - -.. note:: - On `"narrow" `_ builds - of Python, if you use the STIX fonts you should also set - ``ps.fonttype`` and ``pdf.fonttype`` to 3 (the default), not 42. - Otherwise `some characters will not be visible - `_. - -Here is a simple example:: - - # plain text - plt.title('alpha > beta') - -produces "alpha > beta". - -Whereas this:: - - # math text - plt.title(r'$\alpha > \beta$') - -produces ":math:`\alpha > \beta`". - -.. note:: - Mathtext should be placed between a pair of dollar signs ($). To - make it easy to display monetary values, e.g., "$100.00", if a - single dollar sign is present in the entire string, it will be - displayed verbatim as a dollar sign. This is a small change from - regular TeX, where the dollar sign in non-math text would have to - be escaped ('\\\$'). - -.. note:: - While the syntax inside the pair of dollar signs ($) aims to be - TeX-like, the text outside does not. In particular, characters - such as:: - - # $ % & ~ _ ^ \ { } \( \) \[ \] - - have special meaning outside of math mode in TeX. Therefore, these - characters will behave differently depending on the rcParam - ``text.usetex`` flag. See the :ref:`usetex tutorial - ` for more information. - -Subscripts and superscripts ---------------------------- - -To make subscripts and superscripts, use the ``'_'`` and ``'^'`` symbols:: - - r'$\alpha_i > \beta_i$' - -.. math:: - - \alpha_i > \beta_i - -Some symbols automatically put their sub/superscripts under and over -the operator. For example, to write the sum of :math:`x_i` from :math:`0` to -:math:`\infty`, you could do:: - - r'$\sum_{i=0}^\infty x_i$' - -.. math:: - - \sum_{i=0}^\infty x_i - -Fractions, binomials and stacked numbers ----------------------------------------- - -Fractions, binomials and stacked numbers can be created with the -``\frac{}{}``, ``\binom{}{}`` and ``\stackrel{}{}`` commands, -respectively:: - - r'$\frac{3}{4} \binom{3}{4} \stackrel{3}{4}$' - -produces - -.. math:: - - \frac{3}{4} \binom{3}{4} \stackrel{3}{4} - -Fractions can be arbitrarily nested:: - - r'$\frac{5 - \frac{1}{x}}{4}$' - -produces - -.. math:: - - \frac{5 - \frac{1}{x}}{4} - -Note that special care needs to be taken to place parentheses and brackets around -fractions. Doing things the obvious way produces brackets that are -too small:: - - r'$(\frac{5 - \frac{1}{x}}{4})$' - -.. math :: - - (\frac{5 - \frac{1}{x}}{4}) - -The solution is to precede the bracket with ``\left`` and ``\right`` -to inform the parser that those brackets encompass the entire object:: - - r'$\left(\frac{5 - \frac{1}{x}}{4}\right)$' - -.. math :: - - \left(\frac{5 - \frac{1}{x}}{4}\right) - -Radicals --------- - -Radicals can be produced with the ``\sqrt[]{}`` command. For example:: - - r'$\sqrt{2}$' - -.. math :: - - \sqrt{2} - -Any base can (optionally) be provided inside square brackets. Note -that the base must be a simple expression, and can not contain layout -commands such as fractions or sub/superscripts:: - - r'$\sqrt[3]{x}$' - -.. math :: - - \sqrt[3]{x} - -Fonts ------ - -The default font is *italics* for mathematical symbols. - -.. note:: - - This default can be changed using the ``mathtext.default`` rcParam. - This is useful, for example, to use the same font as regular - non-math text for math text, by setting it to ``regular``. - -To change fonts, e.g., to write "sin" in a Roman font, enclose the text -in a font command:: - - r'$s(t) = \mathcal{A}\mathrm{sin}(2 \omega t)$' - -.. math:: - - s(t) = \mathcal{A}\mathrm{sin}(2 \omega t) - -More conveniently, many commonly used function names that are typeset in a -Roman font have shortcuts. So the expression above could be written -as follows:: - - r'$s(t) = \mathcal{A}\sin(2 \omega t)$' - -.. math:: - - s(t) = \mathcal{A}\sin(2 \omega t) - -Here "s" and "t" are variable in italics font (default), "sin" is in -Roman font, and the amplitude "A" is in calligraphy font. Note in the -example above the caligraphy ``A`` is squished into the ``sin``. You -can use a spacing command to add a little whitespace between them:: - - s(t) = \mathcal{A}\/\sin(2 \omega t) - -.. math:: - - s(t) = \mathcal{A}\/\sin(2 \omega t) - -The choices available with all fonts are: - - ============================ ================================== - Command Result - ============================ ================================== - ``\mathrm{Roman}`` :math:`\mathrm{Roman}` - ``\mathit{Italic}`` :math:`\mathit{Italic}` - ``\mathtt{Typewriter}`` :math:`\mathtt{Typewriter}` - ``\mathcal{CALLIGRAPHY}`` :math:`\mathcal{CALLIGRAPHY}` - ============================ ================================== - -.. role:: math-stix(math) - :fontset: stix - -When using the `STIX `_ fonts, you also have the choice of: - - ====================================== ========================================= - Command Result - ====================================== ========================================= - ``\mathbb{blackboard}`` :math-stix:`\mathbb{blackboard}` - ``\mathrm{\mathbb{blackboard}}`` :math-stix:`\mathrm{\mathbb{blackboard}}` - ``\mathfrak{Fraktur}`` :math-stix:`\mathfrak{Fraktur}` - ``\mathsf{sansserif}`` :math-stix:`\mathsf{sansserif}` - ``\mathrm{\mathsf{sansserif}}`` :math-stix:`\mathrm{\mathsf{sansserif}}` - ====================================== ========================================= - -.. htmlonly:: - - ====================================== ========================================= - ``\mathcircled{circled}`` :math-stix:`\mathcircled{circled}` - ====================================== ========================================= - -There are also three global "font sets" to choose from, which are -selected using the ``mathtext.fontset`` parameter in -:ref:`matplotlibrc `. - -``cm``: **Computer Modern (TeX)** - -.. image:: ../_static/cm_fontset.png - -``stix``: **STIX** (designed to blend well with Times) - -.. image:: ../_static/stix_fontset.png - -``stixsans``: **STIX sans-serif** - -.. image:: ../_static/stixsans_fontset.png - -Additionally, you can use ``\mathdefault{...}`` or its alias -``\mathregular{...}`` to use the font used for regular text outside of -mathtext. There are a number of limitations to this approach, most -notably that far fewer symbols will be available, but it can be useful -to make math expressions blend well with other text in the plot. - -Custom fonts -~~~~~~~~~~~~ - -mathtext also provides a way to use custom fonts for math. This -method is fairly tricky to use, and should be considered an -experimental feature for patient users only. By setting the rcParam -``mathtext.fontset`` to ``custom``, you can then set the following -parameters, which control which font file to use for a particular set -of math characters. - - ============================== ================================= - Parameter Corresponds to - ============================== ================================= - ``mathtext.it`` ``\mathit{}`` or default italic - ``mathtext.rm`` ``\mathrm{}`` Roman (upright) - ``mathtext.tt`` ``\mathtt{}`` Typewriter (monospace) - ``mathtext.bf`` ``\mathbf{}`` bold italic - ``mathtext.cal`` ``\mathcal{}`` calligraphic - ``mathtext.sf`` ``\mathsf{}`` sans-serif - ============================== ================================= - -Each parameter should be set to a fontconfig font descriptor (as -defined in the yet-to-be-written font chapter). - -.. TODO: Link to font chapter - -The fonts used should have a Unicode mapping in order to find any -non-Latin characters, such as Greek. If you want to use a math symbol -that is not contained in your custom fonts, you can set the rcParam -``mathtext.fallback_to_cm`` to ``True`` which will cause the mathtext -system to use characters from the default Computer Modern fonts -whenever a particular character can not be found in the custom font. - -Note that the math glyphs specified in Unicode have evolved over time, -and many fonts may not have glyphs in the correct place for mathtext. - -Accents -------- - -An accent command may precede any symbol to add an accent above it. -There are long and short forms for some of them. - - ============================== ================================= - Command Result - ============================== ================================= - ``\acute a`` or ``\'a`` :math:`\acute a` - ``\bar a`` :math:`\bar a` - ``\breve a`` :math:`\breve a` - ``\ddot a`` or ``\"a`` :math:`\ddot a` - ``\dot a`` or ``\.a`` :math:`\dot a` - ``\grave a`` or ``\`a`` :math:`\grave a` - ``\hat a`` or ``\^a`` :math:`\hat a` - ``\tilde a`` or ``\~a`` :math:`\tilde a` - ``\vec a`` :math:`\vec a` - ``\overline{abc}`` :math:`\overline{abc}` - ============================== ================================= - -In addition, there are two special accents that automatically adjust -to the width of the symbols below: - - ============================== ================================= - Command Result - ============================== ================================= - ``\widehat{xyz}`` :math:`\widehat{xyz}` - ``\widetilde{xyz}`` :math:`\widetilde{xyz}` - ============================== ================================= - -Care should be taken when putting accents on lower-case i's and j's. -Note that in the following ``\imath`` is used to avoid the extra dot -over the i:: - - r"$\hat i\ \ \hat \imath$" - -.. math:: - - \hat i\ \ \hat \imath - -Symbols -------- - -You can also use a large number of the TeX symbols, as in ``\infty``, -``\leftarrow``, ``\sum``, ``\int``. - -.. math_symbol_table:: - -If a particular symbol does not have a name (as is true of many of the -more obscure symbols in the STIX fonts), Unicode characters can -also be used:: - - ur'$\u23ce$' - -Example -------- - -Here is an example illustrating many of these features in context. - -.. plot:: pyplots/pyplot_mathtext.py - :include-source: diff --git a/doc/users/navigation_toolbar.rst b/doc/users/navigation_toolbar.rst deleted file mode 100644 index 3722f2efe615..000000000000 --- a/doc/users/navigation_toolbar.rst +++ /dev/null @@ -1,140 +0,0 @@ -.. _navigation-toolbar: - -Interactive navigation -====================== - -.. image:: ../_static/toolbar.png - -All figure windows come with a navigation toolbar, which can be used -to navigate through the data set. Here is a description of each of -the buttons at the bottom of the toolbar - -.. image:: ../../lib/matplotlib/mpl-data/images/home_large.png - -.. image:: ../../lib/matplotlib/mpl-data/images/back_large.png - -.. image:: ../../lib/matplotlib/mpl-data/images/forward_large.png - -The ``Home``, ``Forward`` and ``Back`` buttons - These are akin to a web browser's home, forward and back controls. - ``Forward`` and ``Back`` are used to navigate back and forth between - previously defined views. They have no meaning unless you have already - navigated somewhere else using the pan and zoom buttons. This is analogous - to trying to click ``Back`` on your web browser before visiting a - new page or ``Forward`` before you have gone back to a page -- - nothing happens. ``Home`` always takes you to the - first, default view of your data. Again, all of these buttons should - feel very familiar to any user of a web browser. - -.. image:: ../../lib/matplotlib/mpl-data/images/move_large.png - -The ``Pan/Zoom`` button - This button has two modes: pan and zoom. Click the toolbar button - to activate panning and zooming, then put your mouse somewhere - over an axes. Press the left mouse button and hold it to pan the - figure, dragging it to a new position. When you release it, the - data under the point where you pressed will be moved to the point - where you released. If you press 'x' or 'y' while panning the - motion will be constrained to the x or y axis, respectively. Press - the right mouse button to zoom, dragging it to a new position. - The x axis will be zoomed in proportionately to the rightward - movement and zoomed out proportionately to the leftward movement. - The same is true for the y axis and up/down motions. The point under your - mouse when you begin the zoom remains stationary, allowing you to - zoom in or out around that point as much as you wish. You can use the - modifier keys 'x', 'y' or 'CONTROL' to constrain the zoom to the x - axis, the y axis, or aspect ratio preserve, respectively. - - With polar plots, the pan and zoom functionality behaves - differently. The radius axis labels can be dragged using the left - mouse button. The radius scale can be zoomed in and out using the - right mouse button. - -.. image:: ../../lib/matplotlib/mpl-data/images/zoom_to_rect_large.png - -The ``Zoom-to-rectangle`` button - Click this toolbar button to activate this mode. Put your mouse - somewhere over an axes and press the left mouse button. Drag the - mouse while holding the button to a new location and release. The - axes view limits will be zoomed to the rectangle you have defined. - There is also an experimental 'zoom out to rectangle' in this mode - with the right button, which will place your entire axes in the - region defined by the zoom out rectangle. - -.. image:: ../../lib/matplotlib/mpl-data/images/subplots_large.png - -The ``Subplot-configuration`` button - Use this tool to configure the appearance of the subplot: - you can stretch or compress the left, right, top, or bottom - side of the subplot, or the space between the rows or - space between the columns. - -.. image:: ../../lib/matplotlib/mpl-data/images/filesave_large.png - -The ``Save`` button - Click this button to launch a file save dialog. You can save - files with the following extensions: ``png``, ``ps``, ``eps``, - ``svg`` and ``pdf``. - - -.. _key-event-handling: - -Navigation Keyboard Shortcuts ------------------------------ - -The following table holds all the default keys, which can be overwritten by use of your matplotlibrc (#keymap.\*). - -================================== ================================================= -Command Keyboard Shortcut(s) -================================== ================================================= -Home/Reset **h** or **r** or **home** -Back **c** or **left arrow** or **backspace** -Forward **v** or **right arrow** -Pan/Zoom **p** -Zoom-to-rect **o** -Save **ctrl** + **s** -Toggle fullscreen **ctrl** + **f** -Close plot **ctrl** + **w** -Close all plots **shift** + **w** -Constrain pan/zoom to x axis hold **x** when panning/zooming with mouse -Constrain pan/zoom to y axis hold **y** when panning/zooming with mouse -Preserve aspect ratio hold **CONTROL** when panning/zooming with mouse -Toggle major grids **g** when mouse is over an axes -Toggle minor grids **G** when mouse is over an axes -Toggle x axis scale (log/linear) **L** or **k** when mouse is over an axes -Toggle y axis scale (log/linear) **l** when mouse is over an axes -================================== ================================================= - -If you are using :mod:`matplotlib.pyplot` the toolbar will be created -automatically for every figure. If you are writing your own user -interface code, you can add the toolbar as a widget. The exact syntax -depends on your UI, but we have examples for every supported UI in the -``matplotlib/examples/user_interfaces`` directory. Here is some -example code for GTK:: - - - import gtk - - from matplotlib.figure import Figure - from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas - from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar - - win = gtk.Window() - win.connect("destroy", lambda x: gtk.main_quit()) - win.set_default_size(400,300) - win.set_title("Embedding in GTK") - - vbox = gtk.VBox() - win.add(vbox) - - fig = Figure(figsize=(5,4), dpi=100) - ax = fig.add_subplot(111) - ax.plot([1,2,3]) - - canvas = FigureCanvas(fig) # a gtk.DrawingArea - vbox.pack_start(canvas) - toolbar = NavigationToolbar(canvas, win) - vbox.pack_start(toolbar, False, False) - - win.show_all() - gtk.main() diff --git a/doc/users/path_tutorial.rst b/doc/users/path_tutorial.rst deleted file mode 100644 index 4e1c1b708318..000000000000 --- a/doc/users/path_tutorial.rst +++ /dev/null @@ -1,187 +0,0 @@ -.. _path_tutorial: - -************* -Path Tutorial -************* - -The object underlying all of the :mod:`matplotlib.patch` objects is -the :class:`~matplotlib.path.Path`, which supports the standard set of -moveto, lineto, curveto commands to draw simple and compound outlines -consisting of line segments and splines. The ``Path`` is instantiated -with a (N,2) array of (x,y) vertices, and a N-length array of path -codes. For example to draw the unit rectangle from (0,0) to (1,1), we -could use this code - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from matplotlib.path import Path - import matplotlib.patches as patches - - verts = [ - (0., 0.), # left, bottom - (0., 1.), # left, top - (1., 1.), # right, top - (1., 0.), # right, bottom - (0., 0.), # ignored - ] - - codes = [Path.MOVETO, - Path.LINETO, - Path.LINETO, - Path.LINETO, - Path.CLOSEPOLY, - ] - - path = Path(verts, codes) - - fig = plt.figure() - ax = fig.add_subplot(111) - patch = patches.PathPatch(path, facecolor='orange', lw=2) - ax.add_patch(patch) - ax.set_xlim(-2,2) - ax.set_ylim(-2,2) - plt.show() - - -The following path codes are recognized - -============== ================================= ==================================================================================================================== -Code Vertices Description -============== ================================= ==================================================================================================================== -``STOP`` 1 (ignored) A marker for the end of the entire path (currently not required and ignored) -``MOVETO`` 1 Pick up the pen and move to the given vertex. -``LINETO`` 1 Draw a line from the current position to the given vertex. -``CURVE3`` 2 (1 control point, 1 endpoint) Draw a quadratic Bézier curve from the current position, with the given control point, to the given end point. -``CURVE4`` 3 (2 control points, 1 endpoint) Draw a cubic Bézier curve from the current position, with the given control points, to the given end point. -``CLOSEPOLY`` 1 (point itself is ignored) Draw a line segment to the start point of the current polyline. -============== ================================= ==================================================================================================================== - - -.. path-curves: - - -Bézier example -============== - -Some of the path components require multiple vertices to specify them: -for example CURVE 3 is a `bézier -`_ curve with one -control point and one end point, and CURVE4 has three vertices for the -two control points and the end point. The example below shows a -CURVE4 Bézier spline -- the bézier curve will be contained in the -convex hull of the start point, the two control points, and the end -point - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from matplotlib.path import Path - import matplotlib.patches as patches - - verts = [ - (0., 0.), # P0 - (0.2, 1.), # P1 - (1., 0.8), # P2 - (0.8, 0.), # P3 - ] - - codes = [Path.MOVETO, - Path.CURVE4, - Path.CURVE4, - Path.CURVE4, - ] - - path = Path(verts, codes) - - fig = plt.figure() - ax = fig.add_subplot(111) - patch = patches.PathPatch(path, facecolor='none', lw=2) - ax.add_patch(patch) - - xs, ys = zip(*verts) - ax.plot(xs, ys, 'x--', lw=2, color='black', ms=10) - - ax.text(-0.05, -0.05, 'P0') - ax.text(0.15, 1.05, 'P1') - ax.text(1.05, 0.85, 'P2') - ax.text(0.85, -0.05, 'P3') - - ax.set_xlim(-0.1, 1.1) - ax.set_ylim(-0.1, 1.1) - plt.show() - -.. compound_paths: - -Compound paths -============== - -All of the simple patch primitives in matplotlib, Rectangle, Circle, -Polygon, etc, are implemented with simple path. Plotting functions -like :meth:`~matplotlib.axes.Axes.hist` and -:meth:`~matplotlib.axes.Axes.bar`, which create a number of -primitives, e.g., a bunch of Rectangles, can usually be implemented more -efficiently using a compound path. The reason ``bar`` creates a list -of rectangles and not a compound path is largely historical: the -:class:`~matplotlib.path.Path` code is comparatively new and ``bar`` -predates it. While we could change it now, it would break old code, -so here we will cover how to create compound paths, replacing the -functionality in bar, in case you need to do so in your own code for -efficiency reasons, e.g., you are creating an animated bar plot. - -We will make the histogram chart by creating a series of rectangles -for each histogram bar: the rectangle width is the bin width and the -rectangle height is the number of datapoints in that bin. First we'll -create some random normally distributed data and compute the -histogram. Because numpy returns the bin edges and not centers, the -length of ``bins`` is 1 greater than the length of ``n`` in the -example below:: - - # histogram our data with numpy - data = np.random.randn(1000) - n, bins = np.histogram(data, 100) - -We'll now extract the corners of the rectangles. Each of the -``left``, ``bottom``, etc, arrays below is ``len(n)``, where ``n`` is -the array of counts for each histogram bar:: - - # get the corners of the rectangles for the histogram - left = np.array(bins[:-1]) - right = np.array(bins[1:]) - bottom = np.zeros(len(left)) - top = bottom + n - -Now we have to construct our compound path, which will consist of a -series of ``MOVETO``, ``LINETO`` and ``CLOSEPOLY`` for each rectangle. -For each rectangle, we need 5 vertices: 1 for the ``MOVETO``, 3 for -the ``LINETO``, and 1 for the ``CLOSEPOLY``. As indicated in the -table above, the vertex for the closepoly is ignored but we still need -it to keep the codes aligned with the vertices:: - - nverts = nrects*(1+3+1) - verts = np.zeros((nverts, 2)) - codes = np.ones(nverts, int) * path.Path.LINETO - codes[0::5] = path.Path.MOVETO - codes[4::5] = path.Path.CLOSEPOLY - verts[0::5,0] = left - verts[0::5,1] = bottom - verts[1::5,0] = left - verts[1::5,1] = top - verts[2::5,0] = right - verts[2::5,1] = top - verts[3::5,0] = right - verts[3::5,1] = bottom - -All that remains is to create the path, attach it to a -:class:`~matplotlib.patch.PathPatch`, and add it to our axes:: - - barpath = path.Path(verts, codes) - patch = patches.PathPatch(barpath, facecolor='green', - edgecolor='yellow', alpha=0.5) - ax.add_patch(patch) - -Here is the result - -.. plot:: pyplots/compound_path_demo.py diff --git a/doc/users/patheffects_guide.rst b/doc/users/patheffects_guide.rst deleted file mode 100644 index e674f4be4bac..000000000000 --- a/doc/users/patheffects_guide.rst +++ /dev/null @@ -1,134 +0,0 @@ -.. _patheffects-guide: - -****************** -Path effects guide -****************** - -.. py:module:: matplotlib.patheffects - - -Matplotlib's :mod:`~matplotlib.patheffects` module provides functionality to -apply a multiple draw stage to any Artist which can be rendered via a -:class:`~matplotlib.path.Path`. - -Artists which can have a path effect applied to them include :class:`~matplotlib.patches.Patch`, -:class:`~matplotlib.lines.Line2D`, :class:`~matplotlib.collections.Collection` and even -:class:`~matplotlib.text.Text`. Each artist's path effects can be controlled via the -``set_path_effects`` method (:class:`~matplotlib.artist.Artist.set_path_effects`), which takes -an iterable of :class:`AbstractPathEffect` instances. - -The simplest path effect is the :class:`Normal` effect, which simply -draws the artist without any effect: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import matplotlib.patheffects as path_effects - - fig = plt.figure(figsize=(5, 1.5)) - text = fig.text(0.5, 0.5, 'Hello path effects world!\nThis is the normal ' - 'path effect.\nPretty dull, huh?', - ha='center', va='center', size=20) - text.set_path_effects([path_effects.Normal()]) - plt.show() - -Whilst the plot doesn't look any different to what you would expect without any path -effects, the drawing of the text now been changed to use the path effects -framework, opening up the possibilities for more interesting examples. - -Adding a shadow ---------------- - -A far more interesting path effect than :class:`Normal` is the -drop-shadow, which we can apply to any of our path based artists. The classes -:class:`SimplePatchShadow` and -:class:`SimpleLineShadow` do precisely this by drawing either a filled -patch or a line patch below the original artist: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import matplotlib.patheffects as path_effects - - text = plt.text(0.5, 0.5, 'Hello path effects world!', - path_effects=[path_effects.withSimplePatchShadow()]) - - plt.plot([0, 3, 2, 5], linewidth=5, color='blue', - path_effects=[path_effects.SimpleLineShadow(), - path_effects.Normal()]) - plt.show() - - -Notice the two approaches to setting the path effects in this example. The -first uses the ``with*`` classes to include the desired functionality automatically -followed with the "normal" effect, whereas the latter explicitly defines the two path -effects to draw. - -Making an artist stand out --------------------------- - -One nice way of making artists visually stand out is to draw an outline in a bold -color below the actual artist. The :class:`Stroke` path effect -makes this a relatively simple task: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import matplotlib.patheffects as path_effects - - fig = plt.figure(figsize=(7, 1)) - text = fig.text(0.5, 0.5, 'This text stands out because of\n' - 'its black border.', color='white', - ha='center', va='center', size=30) - text.set_path_effects([path_effects.Stroke(linewidth=3, foreground='black'), - path_effects.Normal()]) - plt.show() - -It is important to note that this effect only works because we have drawn the text -path twice; once with a thick black line, and then once with the original text -path on top. - -You may have noticed that the keywords to :class:`Stroke` and -:class:`SimplePatchShadow` and :class:`SimpleLineShadow` are not the usual Artist -keywords (such as ``facecolor`` and ``edgecolor`` etc.). This is because with these -path effects we are operating at lower level of matplotlib. In fact, the keywords -which are accepted are those for a :class:`matplotlib.backend_bases.GraphicsContextBase` -instance, which have been designed for making it easy to create new backends - and not -for its user interface. - - -Greater control of the path effect artist ------------------------------------------ - -As already mentioned, some of the path effects operate at a lower level than most users -will be used to, meaning that setting keywords such as ``facecolor`` and ``edgecolor`` -raise an AttributeError. Luckily there is a generic :class:`PathPatchEffect` path effect -which creates a :class:`~matplotlib.patches.PathPatch` class with the original path. -The keywords to this effect are identical to those of :class:`~matplotlib.patches.PathPatch`: - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import matplotlib.patheffects as path_effects - - fig = plt.figure(figsize=(8, 1)) - t = fig.text(0.02, 0.5, 'Hatch shadow', fontsize=75, weight=1000, va='center') - t.set_path_effects([path_effects.PathPatchEffect(offset=(4, -4), hatch='xxxx', - facecolor='gray'), - path_effects.PathPatchEffect(edgecolor='white', linewidth=1.1, - facecolor='black')]) - plt.show() - - -.. - Headings for future consideration: - - Implementing a custom path effect - --------------------------------- - - What is going on under the hood - -------------------------------- diff --git a/doc/users/pgf.rst b/doc/users/pgf.rst deleted file mode 100644 index 88eabf802ca2..000000000000 --- a/doc/users/pgf.rst +++ /dev/null @@ -1,177 +0,0 @@ -.. _pgf-tutorial: - -********************************* -Typesetting With XeLaTeX/LuaLaTeX -********************************* - -Using the ``pgf`` backend, matplotlib can export figures as pgf drawing commands -that can be processed with pdflatex, xelatex or lualatex. XeLaTeX and LuaLaTeX -have full unicode support and can use any font that is installed in the operating -system, making use of advanced typographic features of OpenType, AAT and -Graphite. Pgf pictures created by ``plt.savefig('figure.pgf')`` can be -embedded as raw commands in LaTeX documents. Figures can also be directly -compiled and saved to PDF with ``plt.savefig('figure.pdf')`` by either -switching to the backend - -.. code-block:: python - - matplotlib.use('pgf') - -or registering it for handling pdf output - -.. code-block:: python - - from matplotlib.backends.backend_pgf import FigureCanvasPgf - matplotlib.backend_bases.register_backend('pdf', FigureCanvasPgf) - -The second method allows you to keep using regular interactive backends and to -save xelatex, lualatex or pdflatex compiled PDF files from the graphical user interface. - -Matplotlib's pgf support requires a recent LaTeX_ installation that includes -the TikZ/PGF packages (such as TeXLive_), preferably with XeLaTeX or LuaLaTeX -installed. If either pdftocairo or ghostscript is present on your system, -figures can optionally be saved to PNG images as well. The executables -for all applications must be located on your :envvar:`PATH`. - -Rc parameters that control the behavior of the pgf backend: - - ================= ===================================================== - Parameter Documentation - ================= ===================================================== - pgf.preamble Lines to be included in the LaTeX preamble - pgf.rcfonts Setup fonts from rc params using the fontspec package - pgf.texsystem Either "xelatex" (default), "lualatex" or "pdflatex" - ================= ===================================================== - -.. note:: - - TeX defines a set of special characters, such as:: - - # $ % & ~ _ ^ \ { } - - Generally, these characters must be escaped correctly. For convenience, - some characters (_,^,%) are automatically escaped outside of math - environments. - -.. _pgf-rcfonts: - -Font specification -================== - -The fonts used for obtaining the size of text elements or when compiling -figures to PDF are usually defined in the matplotlib rc parameters. You can -also use the LaTeX default Computer Modern fonts by clearing the lists for -``font.serif``, ``font.sans-serif`` or ``font.monospace``. Please note that -the glyph coverage of these fonts is very limited. If you want to keep the -Computer Modern font face but require extended unicode support, consider -installing the `Computer Modern Unicode `_ -fonts *CMU Serif*, *CMU Sans Serif*, etc. - -When saving to ``.pgf``, the font configuration matplotlib used for the -layout of the figure is included in the header of the text file. - -.. literalinclude:: plotting/examples/pgf_fonts.py - :end-before: plt.savefig - -.. image:: /_static/pgf_fonts.* - - -.. _pgf-preamble: - -Custom preamble -=============== - -Full customization is possible by adding your own commands to the preamble. -Use the ``pgf.preamble`` parameter if you want to configure the math fonts, -using ``unicode-math`` for example, or for loading additional packages. Also, -if you want to do the font configuration yourself instead of using the fonts -specified in the rc parameters, make sure to disable ``pgf.rcfonts``. - -.. htmlonly:: - - .. literalinclude:: plotting/examples/pgf_preamble.py - :end-before: plt.savefig - -.. latexonly:: - - .. literalinclude:: plotting/examples/pgf_preamble.py - :end-before: import matplotlib.pyplot as plt - -.. image:: /_static/pgf_preamble.* - - -.. _pgf-texsystem: - -Choosing the TeX system -======================= - -The TeX system to be used by matplotlib is chosen by the ``pgf.texsystem`` -parameter. Possible values are ``'xelatex'`` (default), ``'lualatex'`` and -``'pdflatex'``. Please note that when selecting pdflatex the fonts and -unicode handling must be configured in the preamble. - -.. literalinclude:: plotting/examples/pgf_texsystem.py - :end-before: plt.savefig - -.. image:: /_static/pgf_texsystem.* - - -.. _pgf-troubleshooting: - -Troubleshooting -=============== - -* Please note that the TeX packages found in some Linux distributions and - MiKTeX installations are dramatically outdated. Make sure to update your - package catalog and upgrade or install a recent TeX distribution. - -* On Windows, the :envvar:`PATH` environment variable may need to be modified - to include the directories containing the latex, dvipng and ghostscript - executables. See :ref:`environment-variables` and - :ref:`setting-windows-environment-variables` for details. - -* A limitation on Windows causes the backend to keep file handles that have - been opened by your application open. As a result, it may not be possible - to delete the corresponding files until the application closes (see - `#1324 `_). - -* Sometimes the font rendering in figures that are saved to png images is - very bad. This happens when the pdftocairo tool is not available and - ghostscript is used for the pdf to png conversion. - -* Make sure what you are trying to do is possible in a LaTeX document, - that your LaTeX syntax is valid and that you are using raw strings - if necessary to avoid unintended escape sequences. - -* The ``pgf.preamble`` rc setting provides lots of flexibility, and lots of - ways to cause problems. When experiencing problems, try to minimalize or - disable the custom preamble. - -* Configuring an ``unicode-math`` environment can be a bit tricky. The - TeXLive distribution for example provides a set of math fonts which are - usually not installed system-wide. XeTeX, unlike LuaLatex, cannot find - these fonts by their name, which is why you might have to specify - ``\setmathfont{xits-math.otf}`` instead of ``\setmathfont{XITS Math}`` or - alternatively make the fonts available to your OS. See this - `tex.stackexchange.com question `_ - for more details. - -* If the font configuration used by matplotlib differs from the font setting - in yout LaTeX document, the alignment of text elements in imported figures - may be off. Check the header of your ``.pgf`` file if you are unsure about - the fonts matplotlib used for the layout. - -* Vector images and hence ``.pgf`` files can become bloated if there are a lot - of objects in the graph. This can be the case for image processing or very - big scatter graphs. In an extreme case this can cause TeX to run out of - memory: "TeX capacity exceeded, sorry" You can configure latex to increase - the amount of memory available to generate the ``.pdf`` image as discussed on - `tex.stackexchange.com `_. - Another way would be to "rasterize" parts of the graph causing problems - using either the ``rasterized=True`` keyword, or ``.set_rasterized(True)`` as per - `this example `_. - -* If you still need help, please see :ref:`reporting-problems` - -.. _LaTeX: http://www.tug.org -.. _TeXLive: http://www.tug.org/texlive/ diff --git a/doc/users/plotting/colormaps/colormaps.py b/doc/users/plotting/colormaps/colormaps.py deleted file mode 100644 index a7dbf27e38a9..000000000000 --- a/doc/users/plotting/colormaps/colormaps.py +++ /dev/null @@ -1,22 +0,0 @@ -# Have colormaps separated into categories: -# http://matplotlib.org/examples/color/colormaps_reference.html - -cmaps = [('Perceptually Uniform Sequential', - ['viridis', 'inferno', 'plasma', 'magma']), - ('Sequential', ['Blues', 'BuGn', 'BuPu', - 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', - 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', - 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']), - ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', - 'copper', 'gist_heat', 'gray', 'hot', - 'pink', 'spring', 'summer', 'winter']), - ('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', - 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', - 'seismic']), - ('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', - 'Pastel2', 'Set1', 'Set2', 'Set3']), - ('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern', - 'brg', 'CMRmap', 'cubehelix', - 'gnuplot', 'gnuplot2', 'gist_ncar', - 'nipy_spectral', 'jet', 'rainbow', - 'gist_rainbow', 'hsv', 'flag', 'prism'])] diff --git a/doc/users/plotting/colormaps/grayscale.py b/doc/users/plotting/colormaps/grayscale.py deleted file mode 100644 index 7d3e9f38aeac..000000000000 --- a/doc/users/plotting/colormaps/grayscale.py +++ /dev/null @@ -1,60 +0,0 @@ -''' -Show what matplotlib colormaps look like in grayscale. -Uses lightness L* as a proxy for grayscale value. -''' - -from colormaps import cmaps - -#from skimage import color -# we are using a local copy of colorconv from scikit-image to reduce dependencies. -# You should probably use the one from scikit-image in most cases. -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import cm -import matplotlib as mpl -from colorspacious import cspace_converter - -mpl.rcParams.update({'font.size': 14}) - - -# indices to step through colormap -x = np.linspace(0.0, 1.0, 100) - -# nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps) -gradient = np.linspace(0, 1, 256) -gradient = np.vstack((gradient, gradient)) - -def plot_color_gradients(cmap_category, cmap_list): - nrows = len(cmap_list) - fig, axes = plt.subplots(nrows=nrows, ncols=2) - fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99, - wspace=0.05) - fig.suptitle(cmap_category + ' colormaps', fontsize=14, y=1.0, x=0.6) - - for ax, name in zip(axes, cmap_list): - - # Get rgb values for colormap - rgb = cm.get_cmap(plt.get_cmap(name))(x)[np.newaxis,:,:3] - - # Get colormap in CAM02-UCS colorspace. We want the lightness. - lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb) - L = lab[0,:,0] - L = np.float32(np.vstack((L, L, L))) - - ax[0].imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) - ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.) - pos = list(ax[0].get_position().bounds) - x_text = pos[0] - 0.01 - y_text = pos[1] + pos[3]/2. - fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10) - - # Turn off *all* ticks & spines, not just the ones with colormaps. - for ax in axes: - ax[0].set_axis_off() - ax[1].set_axis_off() - plt.show() - - -for cmap_category, cmap_list in cmaps: - - plot_color_gradients(cmap_category, cmap_list) diff --git a/doc/users/plotting/colormaps/lightness.py b/doc/users/plotting/colormaps/lightness.py deleted file mode 100644 index cd9950fd4d82..000000000000 --- a/doc/users/plotting/colormaps/lightness.py +++ /dev/null @@ -1,120 +0,0 @@ -''' -For each colormap, plot the lightness parameter L* from CIELAB colorspace -along the y axis vs index through the colormap. Colormaps are examined in -categories as in the original matplotlib gallery of colormaps. -''' - -from colormaps import cmaps -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import cm -import matplotlib as mpl -from colorspacious import cspace_converter - -mpl.rcParams.update({'font.size': 12}) - -# indices to step through colormap -x = np.linspace(0.0, 1.0, 100) - -# Do plot -for cmap_category, cmap_list in cmaps: - - # Do subplots so that colormaps have enough space. 5 per subplot? - dsub = 5 # number of colormaps per subplot - if cmap_category == 'Diverging': # because has 12 colormaps - dsub = 6 - elif cmap_category == 'Sequential (2)': - dsub = 6 - elif cmap_category == 'Sequential': - dsub = 7 - nsubplots = int(np.ceil(len(cmap_list)/float(dsub))) - - fig = plt.figure(figsize=(7,2.6*nsubplots)) - - for i, subplot in enumerate(range(nsubplots)): - - locs = [] # locations for text labels - - ax = fig.add_subplot(nsubplots, 1, i+1) - - for j, cmap in enumerate(cmap_list[i*dsub:(i+1)*dsub]): - - # Get rgb values for colormap - rgb = cm.get_cmap(cmap)(x)[np.newaxis,:,:3] - - # Get colormap in CAM02-UCS colorspace. We want the lightness. - lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb) - - # Plot colormap L values - # Do separately for each category so each plot can be pretty - # to make scatter markers change color along plot: - # http://stackoverflow.com/questions/8202605/matplotlib-scatterplot-colour-as-a-function-of-a-third-variable - if cmap_category=='Perceptually Uniform Sequential': - dc = 1.15 # spacing between colormaps - ax.scatter(x+j*dc, lab[0,:,0], c=x, cmap=cmap, - s=300, linewidths=0.) - if i==2: - ax.axis([-0.1,4.1,0,100]) - else: - ax.axis([-0.1,4.7,0,100]) - locs.append(x[-1]+j*dc) # store locations for colormap labels - - elif cmap_category=='Sequential': - dc = 0.6 # spacing between colormaps - # These colormaps all start at high lightness but we want them - # reversed to look nice in the plot, so reverse the order. - ax.scatter(x+j*dc, lab[0,::-1,0], c=x[::-1], cmap=cmap, - s=300, linewidths=0.) - if i==2: - ax.axis([-0.1,4.1,0,100]) - else: - ax.axis([-0.1,4.7,0,100]) - locs.append(x[-1]+j*dc) # store locations for colormap labels - - elif cmap_category=='Sequential (2)': - dc = 1.15 - ax.scatter(x+j*dc, lab[0,:,0], c=x, cmap=cmap, - s=300, linewidths=0.) - ax.axis([-0.1,7.0,0,100]) - # store locations for colormap labels - locs.append(x[-1]+j*dc) - - elif cmap_category=='Diverging': - dc = 1.2 - ax.scatter(x+j*dc, lab[0,:,0], c=x, cmap=cmap, - s=300, linewidths=0.) - ax.axis([-0.1,7.1,0,100]) - # store locations for colormap labels - locs.append(x[int(x.size/2.)]+j*dc) - elif cmap_category=='Qualitative': - dc = 1.3 - ax.scatter(x+j*dc, lab[0,:,0], c=x, cmap=cmap, - s=300, linewidths=0.) - ax.axis([-0.1,6.3,0,100]) - # store locations for colormap labels - locs.append(x[int(x.size/2.)]+j*dc) - - elif cmap_category=='Miscellaneous': - dc = 1.25 - ax.scatter(x+j*dc, lab[0,:,0], c=x, cmap=cmap, - s=300, linewidths=0.) - ax.axis([-0.1,6.1,0,100]) - # store locations for colormap labels - locs.append(x[int(x.size/2.)]+j*dc) - - # Set up labels for colormaps - ax.xaxis.set_ticks_position('top') - ticker = mpl.ticker.FixedLocator(locs) - ax.xaxis.set_major_locator(ticker) - formatter = mpl.ticker.FixedFormatter(cmap_list[i*dsub:(i+1)*dsub]) - ax.xaxis.set_major_formatter(formatter) - labels = ax.get_xticklabels() - for label in labels: - label.set_rotation(60) - - ax.set_xlabel(cmap_category + ' colormaps', fontsize=14) - fig.text(0.0, 0.55, 'Lightness $L^*$', fontsize=12, - transform=fig.transFigure, rotation=90) - - fig.tight_layout(h_pad=0.05, pad=1.5) - plt.show() diff --git a/doc/users/plotting/examples/anchored_box01.py b/doc/users/plotting/examples/anchored_box01.py deleted file mode 100644 index 509e6acf0962..000000000000 --- a/doc/users/plotting/examples/anchored_box01.py +++ /dev/null @@ -1,14 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib.offsetbox import AnchoredText - -fig=plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -at = AnchoredText("Figure 1a", - prop=dict(size=15), frameon=True, - loc=2, - ) -at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") -ax.add_artist(at) - -plt.show() diff --git a/doc/users/plotting/examples/anchored_box02.py b/doc/users/plotting/examples/anchored_box02.py deleted file mode 100644 index d2a2ed7b3e5c..000000000000 --- a/doc/users/plotting/examples/anchored_box02.py +++ /dev/null @@ -1,18 +0,0 @@ -from matplotlib.patches import Circle -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea - -fig=plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - - -ada = AnchoredDrawingArea(40, 20, 0, 0, - loc=1, pad=0., frameon=False) -p1 = Circle((10, 10), 10) -ada.drawing_area.add_artist(p1) -p2 = Circle((30, 10), 5, fc="r") -ada.drawing_area.add_artist(p2) - -ax.add_artist(ada) - -plt.show() diff --git a/doc/users/plotting/examples/anchored_box03.py b/doc/users/plotting/examples/anchored_box03.py deleted file mode 100644 index 371ec9f68052..000000000000 --- a/doc/users/plotting/examples/anchored_box03.py +++ /dev/null @@ -1,14 +0,0 @@ -from matplotlib.patches import Ellipse -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.anchored_artists import AnchoredAuxTransformBox - -fig=plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -box = AnchoredAuxTransformBox(ax.transData, loc=2) -el = Ellipse((0,0), width=0.1, height=0.4, angle=30) # in data coordinates! -box.drawing_area.add_artist(el) - -ax.add_artist(box) - -plt.show() diff --git a/doc/users/plotting/examples/anchored_box04.py b/doc/users/plotting/examples/anchored_box04.py deleted file mode 100644 index 570c73162141..000000000000 --- a/doc/users/plotting/examples/anchored_box04.py +++ /dev/null @@ -1,35 +0,0 @@ -from matplotlib.patches import Ellipse -import matplotlib.pyplot as plt -from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, DrawingArea, HPacker - -fig=plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -box1 = TextArea(" Test : ", textprops=dict(color="k")) - -box2 = DrawingArea(60, 20, 0, 0) -el1 = Ellipse((10, 10), width=16, height=5, angle=30, fc="r") -el2 = Ellipse((30, 10), width=16, height=5, angle=170, fc="g") -el3 = Ellipse((50, 10), width=16, height=5, angle=230, fc="b") -box2.add_artist(el1) -box2.add_artist(el2) -box2.add_artist(el3) - - -box = HPacker(children=[box1, box2], - align="center", - pad=0, sep=5) - -anchored_box = AnchoredOffsetbox(loc=3, - child=box, pad=0., - frameon=True, - bbox_to_anchor=(0., 1.02), - bbox_transform=ax.transAxes, - borderpad=0., - ) - - -ax.add_artist(anchored_box) - -fig.subplots_adjust(top=0.8) -plt.show() diff --git a/doc/users/plotting/examples/annotate_explain.py b/doc/users/plotting/examples/annotate_explain.py deleted file mode 100644 index 12cc94e2aa70..000000000000 --- a/doc/users/plotting/examples/annotate_explain.py +++ /dev/null @@ -1,103 +0,0 @@ - -import matplotlib.pyplot as plt -import matplotlib.patches as mpatches - -x1, y1 = 0.3, 0.3 -x2, y2 = 0.7, 0.7 - -fig = plt.figure(1, figsize=(8,3)) -fig.clf() -from mpl_toolkits.axes_grid1.axes_grid import AxesGrid -from matplotlib.offsetbox import AnchoredText - -#from matplotlib.font_manager import FontProperties - -def add_at(ax, t, loc=2): - fp = dict(size=10) - _at = AnchoredText(t, loc=loc, prop=fp) - ax.add_artist(_at) - return _at - - -grid = AxesGrid(fig, 111, (1, 4), label_mode="1", share_all=True) - -grid[0].set_autoscale_on(False) - -ax = grid[0] -ax.plot([x1, x2], [y1, y2], ".") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="-", #linestyle="dashed", - color="0.5", - patchB=None, - shrinkB=0, - connectionstyle="arc3,rad=0.3", - ), - ) - -add_at(ax, "connect", loc=2) - -ax = grid[1] -ax.plot([x1, x2], [y1, y2], ".") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="-", #linestyle="dashed", - color="0.5", - patchB=el, - shrinkB=0, - connectionstyle="arc3,rad=0.3", - ), - ) - -add_at(ax, "clip", loc=2) - - -ax = grid[2] -ax.plot([x1, x2], [y1, y2], ".") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="-", #linestyle="dashed", - color="0.5", - patchB=el, - shrinkB=5, - connectionstyle="arc3,rad=0.3", - ), - ) - -add_at(ax, "shrink", loc=2) - - -ax = grid[3] -ax.plot([x1, x2], [y1, y2], ".") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.2) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="fancy", #linestyle="dashed", - color="0.5", - patchB=el, - shrinkB=5, - connectionstyle="arc3,rad=0.3", - ), - ) - -add_at(ax, "mutate", loc=2) - -grid[0].set_xlim(0, 1) -grid[0].set_ylim(0, 1) -grid[0].axis["bottom"].toggle(ticklabels=False) -grid[0].axis["left"].toggle(ticklabels=False) -fig.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95) - -plt.draw() -plt.show() diff --git a/doc/users/plotting/examples/annotate_simple01.py b/doc/users/plotting/examples/annotate_simple01.py deleted file mode 100644 index 1a376b66f5b0..000000000000 --- a/doc/users/plotting/examples/annotate_simple01.py +++ /dev/null @@ -1,14 +0,0 @@ -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -ax.annotate("", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3"), - ) - -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple02.py b/doc/users/plotting/examples/annotate_simple02.py deleted file mode 100644 index 25bb0002de5f..000000000000 --- a/doc/users/plotting/examples/annotate_simple02.py +++ /dev/null @@ -1,15 +0,0 @@ -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -ax.annotate("Test", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - size=20, va="center", ha="center", - arrowprops=dict(arrowstyle="simple", - connectionstyle="arc3,rad=-0.2"), - ) - -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple03.py b/doc/users/plotting/examples/annotate_simple03.py deleted file mode 100644 index 61a885afd2a5..000000000000 --- a/doc/users/plotting/examples/annotate_simple03.py +++ /dev/null @@ -1,17 +0,0 @@ -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -ann = ax.annotate("Test", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - size=20, va="center", ha="center", - bbox=dict(boxstyle="round4", fc="w"), - arrowprops=dict(arrowstyle="-|>", - connectionstyle="arc3,rad=-0.2", - fc="w"), - ) - -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple04.py b/doc/users/plotting/examples/annotate_simple04.py deleted file mode 100644 index cdbb1d804175..000000000000 --- a/doc/users/plotting/examples/annotate_simple04.py +++ /dev/null @@ -1,29 +0,0 @@ -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) - -ann = ax.annotate("Test", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - size=20, va="center", ha="center", - bbox=dict(boxstyle="round4", fc="w"), - arrowprops=dict(arrowstyle="-|>", - connectionstyle="arc3,rad=0.2", - relpos=(0., 0.), - fc="w"), - ) - -ann = ax.annotate("Test", - xy=(0.2, 0.2), xycoords='data', - xytext=(0.8, 0.8), textcoords='data', - size=20, va="center", ha="center", - bbox=dict(boxstyle="round4", fc="w"), - arrowprops=dict(arrowstyle="-|>", - connectionstyle="arc3,rad=-0.2", - relpos=(1., 0.), - fc="w"), - ) - -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple_coord01.py b/doc/users/plotting/examples/annotate_simple_coord01.py deleted file mode 100644 index 7b53d0c22973..000000000000 --- a/doc/users/plotting/examples/annotate_simple_coord01.py +++ /dev/null @@ -1,15 +0,0 @@ - -import matplotlib.pyplot as plt - -plt.figure(figsize=(3,2)) -ax=plt.subplot(111) -an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data", - va="center", ha="center", - bbox=dict(boxstyle="round", fc="w")) -an2 = ax.annotate("Test 2", xy=(1, 0.5), xycoords=an1, - xytext=(30,0), textcoords="offset points", - va="center", ha="left", - bbox=dict(boxstyle="round", fc="w"), - arrowprops=dict(arrowstyle="->")) -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple_coord02.py b/doc/users/plotting/examples/annotate_simple_coord02.py deleted file mode 100644 index d2ce74dc6cf9..000000000000 --- a/doc/users/plotting/examples/annotate_simple_coord02.py +++ /dev/null @@ -1,16 +0,0 @@ - -import matplotlib.pyplot as plt - -plt.figure(figsize=(3,2)) -ax=plt.axes([0.1, 0.1, 0.8, 0.7]) -an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data", - va="center", ha="center", - bbox=dict(boxstyle="round", fc="w")) - -an2 = ax.annotate("Test 2", xy=(0.5, 1.), xycoords=an1, - xytext=(0.5,1.1), textcoords=(an1, "axes fraction"), - va="bottom", ha="center", - bbox=dict(boxstyle="round", fc="w"), - arrowprops=dict(arrowstyle="->")) -plt.show() - diff --git a/doc/users/plotting/examples/annotate_simple_coord03.py b/doc/users/plotting/examples/annotate_simple_coord03.py deleted file mode 100644 index b448f7513caf..000000000000 --- a/doc/users/plotting/examples/annotate_simple_coord03.py +++ /dev/null @@ -1,19 +0,0 @@ - -import matplotlib.pyplot as plt - -plt.figure(figsize=(3,2)) -ax=plt.axes([0.1, 0.1, 0.8, 0.7]) -an1 = ax.annotate("Test 1", xy=(0.5, 0.5), xycoords="data", - va="center", ha="center", - bbox=dict(boxstyle="round", fc="w")) - -from matplotlib.text import OffsetFrom -offset_from = OffsetFrom(an1, (0.5, 0)) -an2 = ax.annotate("Test 2", xy=(0.1, 0.1), xycoords="data", - xytext=(0, -10), textcoords=offset_from, - # xytext is offset points from "xy=(0.5, 0), xycoords=an1" - va="top", ha="center", - bbox=dict(boxstyle="round", fc="w"), - arrowprops=dict(arrowstyle="->")) -plt.show() - diff --git a/doc/users/plotting/examples/annotate_text_arrow.py b/doc/users/plotting/examples/annotate_text_arrow.py deleted file mode 100644 index 4ed10f99670e..000000000000 --- a/doc/users/plotting/examples/annotate_text_arrow.py +++ /dev/null @@ -1,38 +0,0 @@ - -import numpy.random -import matplotlib.pyplot as plt - -fig = plt.figure(1, figsize=(5,5)) -fig.clf() - -ax = fig.add_subplot(111) -ax.set_aspect(1) - -x1 = -1 + numpy.random.randn(100) -y1 = -1 + numpy.random.randn(100) -x2 = 1. + numpy.random.randn(100) -y2 = 1. + numpy.random.randn(100) - -ax.scatter(x1, y1, color="r") -ax.scatter(x2, y2, color="g") - -bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9) -ax.text(-2, -2, "Sample A", ha="center", va="center", size=20, - bbox=bbox_props) -ax.text(2, 2, "Sample B", ha="center", va="center", size=20, - bbox=bbox_props) - - -bbox_props = dict(boxstyle="rarrow", fc=(0.8,0.9,0.9), ec="b", lw=2) -t = ax.text(0, 0, "Direction", ha="center", va="center", rotation=45, - size=15, - bbox=bbox_props) - -bb = t.get_bbox_patch() -bb.set_boxstyle("rarrow", pad=0.6) - -ax.set_xlim(-4, 4) -ax.set_ylim(-4, 4) - -plt.draw() -plt.show() diff --git a/doc/users/plotting/examples/colormap_normalizations.py b/doc/users/plotting/examples/colormap_normalizations.py deleted file mode 100644 index 15d38c9fc899..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations.py +++ /dev/null @@ -1,140 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -''' -Lognorm: Instead of pcolor log10(Z1) you can have colorbars that have -the exponential labels using a norm. -''' -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] - -# A low hump with a spike coming out of the top right. Needs to have -# z/colour axis on a log scale so we see both hump and spike. linear -# scale only shows the spike. -Z1 = bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) + \ - 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolor(X, Y, Z1, - norm=colors.LogNorm(vmin=Z1.min(), vmax=Z1.max()), - cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[0], extend='max') - -pcm = ax[1].pcolor(X, Y, Z1, cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[1], extend='max') -fig.show() - - -''' -PowerNorm: Here a power-law trend in X partially obscures a rectified -sine wave in Y. We can remove the power law using a PowerNorm. -''' -X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)] -Z1 = (1 + np.sin(Y * 10.)) * X**(2.) - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, norm=colors.PowerNorm(gamma=1./2.), - cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[0], extend='max') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[1], extend='max') -fig.show() - -''' -SymLogNorm: two humps, one negative and one positive, The positive -with 5-times the amplitude. Linearly, you cannot see detail in the -negative hump. Here we logarithmically scale the positive and -negative data separately. - -Note that colorbar labels do not come out looking very good. -''' - -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = (bivariate_normal(X, Y, 1., 1., 1.0, 1.0))**2 \ - - 0.4 * (bivariate_normal(X, Y, 1.0, 1.0, -1.0, 0.0))**2 -Z1 = Z1/0.03 - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03, - vmin=-1.0, vmax=1.0), - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[1], extend='both') -fig.show() - - -''' -Custom Norm: An example with a customized normalization. This one -uses the example above, and normalizes the negative data differently -from the positive. -''' -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = (bivariate_normal(X, Y, 1., 1., 1.0, 1.0))**2 \ - - 0.4 * (bivariate_normal(X, Y, 1.0, 1.0, -1.0, 0.0))**2 -Z1 = Z1/0.03 - -# Example of making your own norm. Also see matplotlib.colors. -# From Joe Kington: This one gives two different linear ramps: - - -class MidpointNormalize(colors.Normalize): - def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): - self.midpoint = midpoint - colors.Normalize.__init__(self, vmin, vmax, clip) - - def __call__(self, value, clip=None): - # I'm ignoring masked values and all kinds of edge cases to make a - # simple example... - x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1] - return np.ma.masked_array(np.interp(value, x, y)) -##### -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=MidpointNormalize(midpoint=0.), - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[1], extend='both') -fig.show() - -''' -BoundaryNorm: For this one you provide the boundaries for your colors, -and the Norm puts the first color in between the first pair, the -second color between the second pair, etc. -''' - -fig, ax = plt.subplots(3, 1, figsize=(8, 8)) -ax = ax.flatten() -# even bounds gives a contour-like effect -bounds = np.linspace(-1, 1, 10) -norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=norm, - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical') - -# uneven bounds changes the colormapping: -bounds = np.array([-0.25, -0.125, 0, 0.5, 1]) -norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[1].pcolormesh(X, Y, Z1, norm=norm, cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical') - -pcm = ax[2].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical') -fig.show() - diff --git a/doc/users/plotting/examples/colormap_normalizations_bounds.py b/doc/users/plotting/examples/colormap_normalizations_bounds.py deleted file mode 100644 index fb84e1d2efe2..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations_bounds.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = (bivariate_normal(X, Y, 1., 1., 1.0, 1.0))**2 \ - - 0.4 * (bivariate_normal(X, Y, 1.0, 1.0, -1.0, 0.0))**2 -Z1 = Z1/0.03 - -''' -BoundaryNorm: For this one you provide the boundaries for your colors, -and the Norm puts the first color in between the first pair, the -second color between the second pair, etc. -''' - -fig, ax = plt.subplots(3, 1, figsize=(8, 8)) -ax = ax.flatten() -# even bounds gives a contour-like effect -bounds = np.linspace(-1, 1, 10) -norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=norm, - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical') - -# uneven bounds changes the colormapping: -bounds = np.array([-0.25, -0.125, 0, 0.5, 1]) -norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256) -pcm = ax[1].pcolormesh(X, Y, Z1, norm=norm, cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical') - -pcm = ax[2].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical') -fig.show() - diff --git a/doc/users/plotting/examples/colormap_normalizations_custom.py b/doc/users/plotting/examples/colormap_normalizations_custom.py deleted file mode 100644 index 89f6d7423e1e..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations_custom.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -N = 100 -''' -Custom Norm: An example with a customized normalization. This one -uses the example above, and normalizes the negative data differently -from the positive. -''' -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = (bivariate_normal(X, Y, 1., 1., 1.0, 1.0))**2 \ - - 0.4 * (bivariate_normal(X, Y, 1.0, 1.0, -1.0, 0.0))**2 -Z1 = Z1/0.03 - -# Example of making your own norm. Also see matplotlib.colors. -# From Joe Kington: This one gives two different linear ramps: - - -class MidpointNormalize(colors.Normalize): - def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False): - self.midpoint = midpoint - colors.Normalize.__init__(self, vmin, vmax, clip) - - def __call__(self, value, clip=None): - # I'm ignoring masked values and all kinds of edge cases to make a - # simple example... - x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1] - return np.ma.masked_array(np.interp(value, x, y)) -##### -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=MidpointNormalize(midpoint=0.), - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[1], extend='both') -fig.show() - diff --git a/doc/users/plotting/examples/colormap_normalizations_lognorm.py b/doc/users/plotting/examples/colormap_normalizations_lognorm.py deleted file mode 100644 index 82cd2a31bc66..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations_lognorm.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -''' -Lognorm: Instead of pcolor log10(Z1) you can have colorbars that have -the exponential labels using a norm. -''' -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] - -# A low hump with a spike coming out of the top right. Needs to have -# z/colour axis on a log scale so we see both hump and spike. linear -# scale only shows the spike. -Z1 = bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) + \ - 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolor(X, Y, Z1, - norm=colors.LogNorm(vmin=Z1.min(), vmax=Z1.max()), - cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[0], extend='max') - -pcm = ax[1].pcolor(X, Y, Z1, cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[1], extend='max') -fig.show() diff --git a/doc/users/plotting/examples/colormap_normalizations_power.py b/doc/users/plotting/examples/colormap_normalizations_power.py deleted file mode 100644 index 2b5b737074cb..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations_power.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] - -''' -PowerNorm: Here a power-law trend in X partially obscures a rectified -sine wave in Y. We can remove the power law using a PowerNorm. -''' -X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)] -Z1 = (1 + np.sin(Y * 10.)) * X**(2.) - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, norm=colors.PowerNorm(gamma=1./2.), - cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[0], extend='max') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r') -fig.colorbar(pcm, ax=ax[1], extend='max') -fig.show() - - diff --git a/doc/users/plotting/examples/colormap_normalizations_symlognorm.py b/doc/users/plotting/examples/colormap_normalizations_symlognorm.py deleted file mode 100644 index 691b922946a1..000000000000 --- a/doc/users/plotting/examples/colormap_normalizations_symlognorm.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Demonstration of using norm to map colormaps onto data in non-linear ways. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as colors -from matplotlib.mlab import bivariate_normal - -""" -SymLogNorm: two humps, one negative and one positive, The positive -with 5-times the amplitude. Linearly, you cannot see detail in the -negative hump. Here we logarithmically scale the positive and -negative data separately. - -Note that colorbar labels do not come out looking very good. -""" -N=100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] -Z1 = (bivariate_normal(X, Y, 1., 1., 1.0, 1.0))**2 \ - - 0.4 * (bivariate_normal(X, Y, 1.0, 1.0, -1.0, 0.0))**2 -Z1 = Z1/0.03 - -fig, ax = plt.subplots(2, 1) - -pcm = ax[0].pcolormesh(X, Y, Z1, - norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03, - vmin=-1.0, vmax=1.0), - cmap='RdBu_r') -fig.colorbar(pcm, ax=ax[0], extend='both') - -pcm = ax[1].pcolormesh(X, Y, Z1, cmap='RdBu_r', vmin=-np.max(Z1)) -fig.colorbar(pcm, ax=ax[1], extend='both') -fig.show() - diff --git a/doc/users/plotting/examples/connect_simple01.py b/doc/users/plotting/examples/connect_simple01.py deleted file mode 100644 index 7e251ca6bc28..000000000000 --- a/doc/users/plotting/examples/connect_simple01.py +++ /dev/null @@ -1,31 +0,0 @@ -from matplotlib.patches import ConnectionPatch -import matplotlib.pyplot as plt - -fig = plt.figure(1, figsize=(6,3)) -ax1 = plt.subplot(121) -xyA=(0.2, 0.2) -xyB=(0.8, 0.8) -coordsA="data" -coordsB="data" -con = ConnectionPatch(xyA, xyB, coordsA, coordsB, - arrowstyle="-|>", shrinkA=5, shrinkB=5, - mutation_scale=20, fc="w") -ax1.plot([xyA[0], xyB[0]], [xyA[1], xyB[1]], "o") -ax1.add_artist(con) - -ax2 = plt.subplot(122) -#xyA=(0.7, 0.7) -xy=(0.3, 0.2) -coordsA="data" -coordsB="data" -con = ConnectionPatch(xyA=xy, xyB=xy, coordsA=coordsA, coordsB=coordsB, - axesA=ax2, axesB=ax1, - arrowstyle="->", shrinkB=5) -ax2.add_artist(con) - -ax1.set_xlim(0, 1) -ax1.set_ylim(0, 1) -ax2.set_xlim(0, .5) -ax2.set_ylim(0, .5) -plt.draw() -plt.show() diff --git a/doc/users/plotting/examples/connectionstyle_demo.py b/doc/users/plotting/examples/connectionstyle_demo.py deleted file mode 100644 index 86757f2104e6..000000000000 --- a/doc/users/plotting/examples/connectionstyle_demo.py +++ /dev/null @@ -1,109 +0,0 @@ - -import matplotlib.pyplot as plt -import matplotlib.patches as mpatches - -fig = plt.figure(1, figsize=(8,5)) -fig.clf() -from mpl_toolkits.axes_grid1.axes_grid import AxesGrid -from matplotlib.offsetbox import AnchoredText - -#from matplotlib.font_manager import FontProperties - -def add_at(ax, t, loc=2): - fp = dict(size=8) - _at = AnchoredText(t, loc=loc, prop=fp) - ax.add_artist(_at) - return _at - - -grid = AxesGrid(fig, 111, (3, 5), label_mode="1", share_all=True) - -grid[0].set_autoscale_on(False) - - -x1, y1 = 0.3, 0.3 -x2, y2 = 0.7, 0.7 - - -def demo_con_style(ax, connectionstyle, label=None): - - if label is None: - label = connectionstyle - - x1, y1 = 0.3, 0.2 - x2, y2 = 0.8, 0.6 - - ax.plot([x1, x2], [y1, y2], ".") - ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->", #linestyle="dashed", - color="0.5", - shrinkA=5, shrinkB=5, - patchA=None, - patchB=None, - connectionstyle=connectionstyle, - ), - ) - - add_at(ax, label, loc=2) - -column = grid.axes_column[0] - -demo_con_style(column[0], "angle3,angleA=90,angleB=0", - label="angle3,\nangleA=90,\nangleB=0") -demo_con_style(column[1], "angle3,angleA=0,angleB=90", - label="angle3,\nangleA=0,\nangleB=90") - - - -column = grid.axes_column[1] - -demo_con_style(column[0], "arc3,rad=0.") -demo_con_style(column[1], "arc3,rad=0.3") -demo_con_style(column[2], "arc3,rad=-0.3") - - - -column = grid.axes_column[2] - -demo_con_style(column[0], "angle,angleA=-90,angleB=180,rad=0", - label="angle,\nangleA=-90,\nangleB=180,\nrad=0") -demo_con_style(column[1], "angle,angleA=-90,angleB=180,rad=5", - label="angle,\nangleA=-90,\nangleB=180,\nrad=5") -demo_con_style(column[2], "angle,angleA=-90,angleB=10,rad=5", - label="angle,\nangleA=-90,\nangleB=10,\nrad=0") - - -column = grid.axes_column[3] - -demo_con_style(column[0], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=0", - label="arc,\nangleA=-90,\nangleB=0,\narmA=30,\narmB=30,\nrad=0") -demo_con_style(column[1], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=5", - label="arc,\nangleA=-90,\nangleB=0,\narmA=30,\narmB=30,\nrad=5") -demo_con_style(column[2], "arc,angleA=-90,angleB=0,armA=0,armB=40,rad=0", - label="arc,\nangleA=-90,\nangleB=0,\narmA=0,\narmB=40,\nrad=0") - - -column = grid.axes_column[4] - -demo_con_style(column[0], "bar,fraction=0.3", - label="bar,\nfraction=0.3") -demo_con_style(column[1], "bar,fraction=-0.3", - label="bar,\nfraction=-0.3") -demo_con_style(column[2], "bar,angle=180,fraction=-0.2", - label="bar,\nangle=180,\nfraction=-0.2") - - -#demo_con_style(column[1], "arc3,rad=0.3") -#demo_con_style(column[2], "arc3,rad=-0.3") - - -grid[0].set_xlim(0, 1) -grid[0].set_ylim(0, 1) -grid.axes_llc.axis["bottom"].toggle(ticklabels=False) -grid.axes_llc.axis["left"].toggle(ticklabels=False) -fig.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95) - -plt.draw() -plt.show() diff --git a/doc/users/plotting/examples/custom_boxstyle01.py b/doc/users/plotting/examples/custom_boxstyle01.py deleted file mode 100644 index f53f135d38e4..000000000000 --- a/doc/users/plotting/examples/custom_boxstyle01.py +++ /dev/null @@ -1,47 +0,0 @@ -from matplotlib.path import Path - -def custom_box_style(x0, y0, width, height, mutation_size, mutation_aspect=1): - """ - Given the location and size of the box, return the path of - the box around it. - - - *x0*, *y0*, *width*, *height* : location and size of the box - - *mutation_size* : a reference scale for the mutation. - - *aspect_ratio* : aspect-ration for the mutation. - """ - - # note that we are ignoring mutation_aspect. This is okay in general. - - # padding - mypad = 0.3 - pad = mutation_size * mypad - - # width and height with padding added. - width, height = width + 2.*pad, \ - height + 2.*pad, - - # boundary of the padded box - x0, y0 = x0-pad, y0-pad, - x1, y1 = x0+width, y0 + height - - cp = [(x0, y0), - (x1, y0), (x1, y1), (x0, y1), - (x0-pad, (y0+y1)/2.), (x0, y0), - (x0, y0)] - - com = [Path.MOVETO, - Path.LINETO, Path.LINETO, Path.LINETO, - Path.LINETO, Path.LINETO, - Path.CLOSEPOLY] - - path = Path(cp, com) - - return path - - -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) -ax.text(0.5, 0.5, "Test", size=30, va="center", ha="center", - bbox=dict(boxstyle=custom_box_style, alpha=0.2)) diff --git a/doc/users/plotting/examples/custom_boxstyle02.py b/doc/users/plotting/examples/custom_boxstyle02.py deleted file mode 100644 index 96933cb06897..000000000000 --- a/doc/users/plotting/examples/custom_boxstyle02.py +++ /dev/null @@ -1,74 +0,0 @@ -from matplotlib.path import Path -from matplotlib.patches import BoxStyle -import matplotlib.pyplot as plt - -# we may derive from matplotlib.patches.BoxStyle._Base class. -# You need to overide transmute method in this case. - -class MyStyle(BoxStyle._Base): - """ - A simple box. - """ - - def __init__(self, pad=0.3): - """ - The arguments need to be floating numbers and need to have - default values. - - *pad* - amount of padding - """ - - self.pad = pad - super(MyStyle, self).__init__() - - def transmute(self, x0, y0, width, height, mutation_size): - """ - Given the location and size of the box, return the path of - the box around it. - - - *x0*, *y0*, *width*, *height* : location and size of the box - - *mutation_size* : a reference scale for the mutation. - - Often, the *mutation_size* is the font size of the text. - You don't need to worry about the rotation as it is - automatically taken care of. - """ - - # padding - pad = mutation_size * self.pad - - # width and height with padding added. - width, height = width + 2.*pad, \ - height + 2.*pad, - - # boundary of the padded box - x0, y0 = x0-pad, y0-pad, - x1, y1 = x0+width, y0 + height - - cp = [(x0, y0), - (x1, y0), (x1, y1), (x0, y1), - (x0-pad, (y0+y1)/2.), (x0, y0), - (x0, y0)] - - com = [Path.MOVETO, - Path.LINETO, Path.LINETO, Path.LINETO, - Path.LINETO, Path.LINETO, - Path.CLOSEPOLY] - - path = Path(cp, com) - - return path - - -# register the custom style -BoxStyle._style_list["angled"] = MyStyle - -plt.figure(1, figsize=(3,3)) -ax = plt.subplot(111) -ax.text(0.5, 0.5, "Test", size=30, va="center", ha="center", rotation=30, - bbox=dict(boxstyle="angled,pad=0.5", alpha=0.2)) - -del BoxStyle._style_list["angled"] - -plt.show() diff --git a/doc/users/plotting/examples/demo_gridspec01.py b/doc/users/plotting/examples/demo_gridspec01.py deleted file mode 100644 index 55c76a7f0f4d..000000000000 --- a/doc/users/plotting/examples/demo_gridspec01.py +++ /dev/null @@ -1,20 +0,0 @@ -import matplotlib.pyplot as plt - -def make_ticklabels_invisible(fig): - for i, ax in enumerate(fig.axes): - ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center") - for tl in ax.get_xticklabels() + ax.get_yticklabels(): - tl.set_visible(False) - - -plt.figure(0) -ax1 = plt.subplot2grid((3,3), (0,0), colspan=3) -ax2 = plt.subplot2grid((3,3), (1,0), colspan=2) -ax3 = plt.subplot2grid((3,3), (1, 2), rowspan=2) -ax4 = plt.subplot2grid((3,3), (2, 0)) -ax5 = plt.subplot2grid((3,3), (2, 1)) - -plt.suptitle("subplot2grid") -make_ticklabels_invisible(plt.gcf()) -plt.show() - diff --git a/doc/users/plotting/examples/demo_gridspec02.py b/doc/users/plotting/examples/demo_gridspec02.py deleted file mode 100644 index 43a7f0899403..000000000000 --- a/doc/users/plotting/examples/demo_gridspec02.py +++ /dev/null @@ -1,26 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib.gridspec import GridSpec - - -def make_ticklabels_invisible(fig): - for i, ax in enumerate(fig.axes): - ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center") - for tl in ax.get_xticklabels() + ax.get_yticklabels(): - tl.set_visible(False) - - -plt.figure() - -gs = GridSpec(3, 3) -ax1 = plt.subplot(gs[0, :]) -# identical to ax1 = plt.subplot(gs.new_subplotspec((0,0), colspan=3)) -ax2 = plt.subplot(gs[1,:-1]) -ax3 = plt.subplot(gs[1:, -1]) -ax4 = plt.subplot(gs[-1,0]) -ax5 = plt.subplot(gs[-1,-2]) - -plt.suptitle("GridSpec") -make_ticklabels_invisible(plt.gcf()) - -plt.show() - diff --git a/doc/users/plotting/examples/demo_gridspec03.py b/doc/users/plotting/examples/demo_gridspec03.py deleted file mode 100644 index da7c801566c1..000000000000 --- a/doc/users/plotting/examples/demo_gridspec03.py +++ /dev/null @@ -1,34 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib.gridspec import GridSpec - - -def make_ticklabels_invisible(fig): - for i, ax in enumerate(fig.axes): - ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center") - for tl in ax.get_xticklabels() + ax.get_yticklabels(): - tl.set_visible(False) - - - -# demo 3 : gridspec with subplotpars set. - -f = plt.figure() - -plt.suptitle("GridSpec w/ different subplotpars") - -gs1 = GridSpec(3, 3) -gs1.update(left=0.05, right=0.48, wspace=0.05) -ax1 = plt.subplot(gs1[:-1, :]) -ax2 = plt.subplot(gs1[-1, :-1]) -ax3 = plt.subplot(gs1[-1, -1]) - -gs2 = GridSpec(3, 3) -gs2.update(left=0.55, right=0.98, hspace=0.05) -ax4 = plt.subplot(gs2[:, :-1]) -ax5 = plt.subplot(gs2[:-1, -1]) -ax6 = plt.subplot(gs2[-1, -1]) - -make_ticklabels_invisible(plt.gcf()) - -plt.show() - diff --git a/doc/users/plotting/examples/demo_gridspec04.py b/doc/users/plotting/examples/demo_gridspec04.py deleted file mode 100644 index f948b13d91e7..000000000000 --- a/doc/users/plotting/examples/demo_gridspec04.py +++ /dev/null @@ -1,41 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec - -def make_ticklabels_invisible(fig): - for i, ax in enumerate(fig.axes): - ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center") - for tl in ax.get_xticklabels() + ax.get_yticklabels(): - tl.set_visible(False) - - - -# gridspec inside gridspec - -f = plt.figure() - -gs0 = gridspec.GridSpec(1, 2) - -gs00 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[0]) - -ax1 = plt.Subplot(f, gs00[:-1, :]) -f.add_subplot(ax1) -ax2 = plt.Subplot(f, gs00[-1, :-1]) -f.add_subplot(ax2) -ax3 = plt.Subplot(f, gs00[-1, -1]) -f.add_subplot(ax3) - - -gs01 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[1]) - -ax4 = plt.Subplot(f, gs01[:, :-1]) -f.add_subplot(ax4) -ax5 = plt.Subplot(f, gs01[:-1, -1]) -f.add_subplot(ax5) -ax6 = plt.Subplot(f, gs01[-1, -1]) -f.add_subplot(ax6) - -plt.suptitle("GirdSpec Inside GridSpec") -make_ticklabels_invisible(plt.gcf()) - -plt.show() - diff --git a/doc/users/plotting/examples/demo_gridspec05.py b/doc/users/plotting/examples/demo_gridspec05.py deleted file mode 100644 index 6bc263a89331..000000000000 --- a/doc/users/plotting/examples/demo_gridspec05.py +++ /dev/null @@ -1,26 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec - -def make_ticklabels_invisible(fig): - for i, ax in enumerate(fig.axes): - ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center") - for tl in ax.get_xticklabels() + ax.get_yticklabels(): - tl.set_visible(False) - - - -f = plt.figure() - -gs = gridspec.GridSpec(2, 2, - width_ratios=[1,2], - height_ratios=[4,1] - ) - -ax1 = plt.subplot(gs[0]) -ax2 = plt.subplot(gs[1]) -ax3 = plt.subplot(gs[2]) -ax4 = plt.subplot(gs[3]) - -make_ticklabels_invisible(f) -plt.show() - diff --git a/doc/users/plotting/examples/demo_gridspec06.py b/doc/users/plotting/examples/demo_gridspec06.py deleted file mode 100644 index 32084dfa960b..000000000000 --- a/doc/users/plotting/examples/demo_gridspec06.py +++ /dev/null @@ -1,40 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec -import numpy as np -from itertools import product - -def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)): - return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d) - -fig = plt.figure(figsize=(8, 8)) - -# gridspec inside gridspec -outer_grid = gridspec.GridSpec(4, 4, wspace=0.0, hspace=0.0) - -for i in range(16): - inner_grid = gridspec.GridSpecFromSubplotSpec(3, 3, - subplot_spec=outer_grid[i], wspace=0.0, hspace=0.0) - a, b = int(i/4)+1,i%4+1 - for j, (c, d) in enumerate(product(range(1, 4), repeat=2)): - ax = plt.Subplot(fig, inner_grid[j]) - ax.plot(*squiggle_xy(a, b, c, d)) - ax.set_xticks([]) - ax.set_yticks([]) - fig.add_subplot(ax) - -all_axes = fig.get_axes() - -#show only the outside spines -for ax in all_axes: - for sp in ax.spines.values(): - sp.set_visible(False) - if ax.is_first_row(): - ax.spines['top'].set_visible(True) - if ax.is_last_row(): - ax.spines['bottom'].set_visible(True) - if ax.is_first_col(): - ax.spines['left'].set_visible(True) - if ax.is_last_col(): - ax.spines['right'].set_visible(True) - -plt.show() diff --git a/doc/users/plotting/examples/pgf_fonts.py b/doc/users/plotting/examples/pgf_fonts.py deleted file mode 100644 index ae260b151406..000000000000 --- a/doc/users/plotting/examples/pgf_fonts.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- - -import matplotlib as mpl -mpl.use("pgf") -pgf_with_rc_fonts = { - "font.family": "serif", - "font.serif": [], # use latex default serif font - "font.sans-serif": ["DejaVu Sans"], # use a specific sans-serif font -} -mpl.rcParams.update(pgf_with_rc_fonts) - -import matplotlib.pyplot as plt -plt.figure(figsize=(4.5,2.5)) -plt.plot(range(5)) -plt.text(0.5, 3., "serif") -plt.text(0.5, 2., "monospace", family="monospace") -plt.text(2.5, 2., "sans-serif", family="sans-serif") -plt.text(2.5, 1., "comic sans", family="Comic Sans MS") -plt.xlabel(u"µ is not $\\mu$") -plt.tight_layout(.5) - -plt.savefig("pgf_fonts.pdf") -plt.savefig("pgf_fonts.png") diff --git a/doc/users/plotting/examples/pgf_preamble.py b/doc/users/plotting/examples/pgf_preamble.py deleted file mode 100644 index f233afbd1db7..000000000000 --- a/doc/users/plotting/examples/pgf_preamble.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import six - -import matplotlib as mpl -mpl.use("pgf") -pgf_with_custom_preamble = { - "font.family": "serif", # use serif/main font for text elements - "text.usetex": True, # use inline math for ticks - "pgf.rcfonts": False, # don't setup fonts from rc parameters - "pgf.preamble": [ - "\\usepackage{units}", # load additional packages - "\\usepackage{metalogo}", - "\\usepackage{unicode-math}", # unicode math setup - r"\setmathfont{xits-math.otf}", - r"\setmainfont{DejaVu Serif}", # serif font via preamble - ] -} -mpl.rcParams.update(pgf_with_custom_preamble) - -import matplotlib.pyplot as plt -plt.figure(figsize=(4.5,2.5)) -plt.plot(range(5)) -plt.xlabel("unicode text: я, ψ, €, ü, \\unitfrac[10]{°}{µm}") -plt.ylabel("\\XeLaTeX") -plt.legend(["unicode math: $λ=∑_i^∞ μ_i^2$"]) -plt.tight_layout(.5) - -plt.savefig("pgf_preamble.pdf") -plt.savefig("pgf_preamble.png") diff --git a/doc/users/plotting/examples/pgf_texsystem.py b/doc/users/plotting/examples/pgf_texsystem.py deleted file mode 100644 index 88231348b5e2..000000000000 --- a/doc/users/plotting/examples/pgf_texsystem.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- - -import matplotlib as mpl -mpl.use("pgf") -pgf_with_pdflatex = { - "pgf.texsystem": "pdflatex", - "pgf.preamble": [ - r"\usepackage[utf8x]{inputenc}", - r"\usepackage[T1]{fontenc}", - r"\usepackage{cmbright}", - ] -} -mpl.rcParams.update(pgf_with_pdflatex) - -import matplotlib.pyplot as plt -plt.figure(figsize=(4.5,2.5)) -plt.plot(range(5)) -plt.text(0.5, 3., "serif", family="serif") -plt.text(0.5, 2., "monospace", family="monospace") -plt.text(2.5, 2., "sans-serif", family="sans-serif") -plt.xlabel(u"µ is not $\\mu$") -plt.tight_layout(.5) - -plt.savefig("pgf_texsystem.pdf") -plt.savefig("pgf_texsystem.png") diff --git a/doc/users/plotting/examples/simple_annotate01.py b/doc/users/plotting/examples/simple_annotate01.py deleted file mode 100644 index eb99af25babd..000000000000 --- a/doc/users/plotting/examples/simple_annotate01.py +++ /dev/null @@ -1,132 +0,0 @@ - -import matplotlib.pyplot as plt -import matplotlib.patches as mpatches - -x1, y1 = 0.3, 0.3 -x2, y2 = 0.7, 0.7 - -fig = plt.figure(1) -fig.clf() -from mpl_toolkits.axes_grid1.axes_grid import Grid -from matplotlib.offsetbox import AnchoredText - -from matplotlib.font_manager import FontProperties - -def add_at(ax, t, loc=2): - fp = dict(size=10) - _at = AnchoredText(t, loc=loc, prop=fp) - ax.add_artist(_at) - return _at - - -grid = Grid(fig, 111, (4, 4), label_mode="1", share_all=True) - -grid[0].set_autoscale_on(False) - -ax = grid[0] -ax.plot([x1, x2], [y1, y2], "o") -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->")) - -add_at(ax, "A $->$ B", loc=2) - -ax = grid[1] -ax.plot([x1, x2], [y1, y2], "o") -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3,rad=0.3")) - -add_at(ax, "connectionstyle=arc3", loc=2) - - -ax = grid[2] -ax.plot([x1, x2], [y1, y2], "o") -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3,rad=0.3", - shrinkB=5, - ) - ) - -add_at(ax, "shrinkB=5", loc=2) - - -ax = grid[3] -ax.plot([x1, x2], [y1, y2], "o") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.5) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3,rad=0.2", - ) - ) - - -ax = grid[4] -ax.plot([x1, x2], [y1, y2], "o") -el = mpatches.Ellipse((x1, y1), 0.3, 0.4, angle=30, alpha=0.5) -ax.add_artist(el) -ax.annotate("", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3,rad=0.2", - patchB=el, - ) - ) - - -add_at(ax, "patchB", loc=2) - - - -ax = grid[5] -ax.plot([x1], [y1], "o") -ax.annotate("Test", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - ha="center", va="center", - bbox=dict(boxstyle="round", - fc="w", - ), - arrowprops=dict(arrowstyle="->", - #connectionstyle="arc3,rad=0.2", - ) - ) - - -add_at(ax, "annotate", loc=2) - - -ax = grid[6] -ax.plot([x1], [y1], "o") -ax.annotate("Test", - xy=(x1, y1), xycoords='data', - xytext=(x2, y2), textcoords='data', - ha="center", va="center", - bbox=dict(boxstyle="round", - fc="w", - ), - arrowprops=dict(arrowstyle="->", - #connectionstyle="arc3,rad=0.2", - relpos=(0., 0.) - ) - ) - - -add_at(ax, "relpos=(0,0)", loc=2) - - - -#ax.set_xlim(0, 1) -#ax.set_ylim(0, 1) -plt.draw() -plt.show() diff --git a/doc/users/plotting/examples/simple_legend01.py b/doc/users/plotting/examples/simple_legend01.py deleted file mode 100644 index 8e0ede331670..000000000000 --- a/doc/users/plotting/examples/simple_legend01.py +++ /dev/null @@ -1,18 +0,0 @@ -import matplotlib.pyplot as plt - - -plt.subplot(211) -plt.plot([1,2,3], label="test1") -plt.plot([3,2,1], label="test2") -# Place a legend above this subplot, expanding itself to -# fully use the given bounding box. -plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, - ncol=2, mode="expand", borderaxespad=0.) - -plt.subplot(223) -plt.plot([1,2,3], label="test1") -plt.plot([3,2,1], label="test2") -# Place a legend to the right of this smaller subplot. -plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) - -plt.show() diff --git a/doc/users/plotting/examples/simple_legend02.py b/doc/users/plotting/examples/simple_legend02.py deleted file mode 100644 index dabd2f072e72..000000000000 --- a/doc/users/plotting/examples/simple_legend02.py +++ /dev/null @@ -1,15 +0,0 @@ -import matplotlib.pyplot as plt - -line1, = plt.plot([1,2,3], label="Line 1", linestyle='--') -line2, = plt.plot([3,2,1], label="Line 2", linewidth=4) - -# Create a legend for the first line. -first_legend = plt.legend(handles=[line1], loc=1) - -# Add the legend manually to the current Axes. -ax = plt.gca().add_artist(first_legend) - -# Create another legend for the second line. -plt.legend(handles=[line2], loc=4) - -plt.show() diff --git a/doc/users/prev_whats_new/whats_new_0.99.rst b/doc/users/prev_whats_new/whats_new_0.99.rst deleted file mode 100644 index d7ddf6d1ff90..000000000000 --- a/doc/users/prev_whats_new/whats_new_0.99.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _whats-new-0-99: - -New in matplotlib 0.99 -====================== - -.. contents:: Table of Contents - :depth: 2 - - - -New documentation ------------------ - -Jae-Joon Lee has written two new guides :ref:`plotting-guide-legend` -and :ref:`plotting-guide-annotation`. Michael Sarahan has written -:ref:`image_tutorial`. John Hunter has written two new tutorials on -working with paths and transformations: :ref:`path_tutorial` and -:ref:`transforms_tutorial`. - -.. _whats-new-mplot3d: - -mplot3d --------- - - -Reinier Heeres has ported John Porter's mplot3d over to the new -matplotlib transformations framework, and it is now available as a -toolkit mpl_toolkits.mplot3d (which now comes standard with all mpl -installs). See :ref:`mplot3d-examples-index` and -:ref:`toolkit_mplot3d-tutorial` - -.. plot:: pyplots/whats_new_99_mplot3d.py - -.. _whats-new-axes-grid: - -axes grid toolkit ------------------ - -Jae-Joon Lee has added a new toolkit to ease displaying multiple images in -matplotlib, as well as some support for curvilinear grids to support -the world coordinate system. The toolkit is included standard with all -new mpl installs. See :ref:`axes_grid1-examples-index`, -:ref:`axisartist-examples-index`, :ref:`axes_grid1_users-guide-index` and -:ref:`axisartist_users-guide-index` - -.. plot:: pyplots/whats_new_99_axes_grid.py - -.. _whats-new-spine: - -Axis spine placement --------------------- - -Andrew Straw has added the ability to place "axis spines" -- the lines -that denote the data limits -- in various arbitrary locations. No -longer are your axis lines constrained to be a simple rectangle around -the figure -- you can turn on or off left, bottom, right and top, as -well as "detach" the spine to offset it away from the data. See -:ref:`pylab_examples-spine_placement_demo` and -:class:`matplotlib.spines.Spine`. - -.. plot:: pyplots/whats_new_99_spines.py diff --git a/doc/users/prev_whats_new/whats_new_1.0.rst b/doc/users/prev_whats_new/whats_new_1.0.rst deleted file mode 100644 index 5af33dd4fcbe..000000000000 --- a/doc/users/prev_whats_new/whats_new_1.0.rst +++ /dev/null @@ -1,134 +0,0 @@ -.. _whats-new-1-0: - -New in matplotlib 1.0 -====================== - -.. contents:: Table of Contents - :depth: 2 - -.. _whats-new-html5: - -HTML5/Canvas backend ---------------------- - -Simon Ratcliffe and Ludwig Schwardt have released an `HTML5/Canvas -`__ backend for matplotlib. The -backend is almost feature complete, and they have done a lot of work -comparing their html5 rendered images with our core renderer Agg. The -backend features client/server interactive navigation of matplotlib -figures in an html5 compliant browser. - -Sophisticated subplot grid layout ---------------------------------- - -Jae-Joon Lee has written :mod:`~matplotlib.gridspec`, a new module for -doing complex subplot layouts, featuring row and column spans and -more. See :ref:`gridspec-guide` for a tutorial overview. - -.. plot:: users/plotting/examples/demo_gridspec01.py - -Easy pythonic subplots ------------------------ - -Fernando Perez got tired of all the boilerplate code needed to create a -figure and multiple subplots when using the matplotlib API, and wrote -a :func:`~matplotlib.pyplot.subplots` helper function. Basic usage -allows you to create the figure and an array of subplots with numpy -indexing (starts with 0). e.g.:: - - fig, axarr = plt.subplots(2, 2) - axarr[0,0].plot([1,2,3]) # upper, left - -See :ref:`pylab_examples-subplots_demo` for several code examples. - -Contour fixes and and triplot ---------------------------------- - -Ian Thomas has fixed a long-standing bug that has vexed our most -talented developers for years. :func:`~matplotlib.pyplot.contourf` -now handles interior masked regions, and the boundaries of line and -filled contours coincide. - -Additionally, he has contributed a new module :mod:`~matplotlib.tri` and -helper function :func:`~matplotlib.pyplot.triplot` for creating and -plotting unstructured triangular grids. - -.. plot:: mpl_examples/pylab_examples/triplot_demo.py - -multiple calls to show supported ---------------------------------- - -A long standing request is to support multiple calls to -:func:`~matplotlib.pyplot.show`. This has been difficult because it -is hard to get consistent behavior across operating systems, user -interface toolkits and versions. Eric Firing has done a lot of work -on rationalizing show across backends, with the desired behavior to -make show raise all newly created figures and block execution until -they are closed. Repeated calls to show should raise newly created -figures since the last call. Eric has done a lot of testing on the -user interface toolkits and versions and platforms he has access to, -but it is not possible to test them all, so please report problems to -the `mailing list -`__ -and `bug tracker -`__. - - -mplot3d graphs can be embedded in arbitrary axes -------------------------------------------------- - -You can now place an mplot3d graph into an arbitrary axes location, -supporting mixing of 2D and 3D graphs in the same figure, and/or -multiple 3D graphs in a single figure, using the "projection" keyword -argument to add_axes or add_subplot. Thanks Ben Root. - -.. plot:: pyplots/whats_new_1_subplot3d.py - -tick_params ------------- - -Eric Firing wrote tick_params, a convenience method for changing the -appearance of ticks and tick labels. See pyplot function -:func:`~matplotlib.pyplot.tick_params` and associated Axes method -:meth:`~matplotlib.axes.Axes.tick_params`. - -Lots of performance and feature enhancements ---------------------------------------------- - - -* Faster magnification of large images, and the ability to zoom in to - a single pixel - -* Local installs of documentation work better - -* Improved "widgets" -- mouse grabbing is supported - -* More accurate snapping of lines to pixel boundaries - -* More consistent handling of color, particularly the alpha channel, - throughout the API - -Much improved software carpentry ---------------------------------- - -The matplotlib trunk is probably in as good a shape as it has ever -been, thanks to improved `software carpentry -`__. We now have a `buildbot -`__ which runs a suite of `nose -`__ regression tests on every -svn commit, auto-generating a set of images and comparing them against -a set of known-goods, sending emails to developers on failures with a -pixel-by-pixel image comparison. Releases and release -bugfixes happen in branches, allowing active new feature development -to happen in the trunk while keeping the release branches stable. -Thanks to Andrew Straw, Michael Droettboom and other matplotlib -developers for the heavy lifting. - -Bugfix marathon ----------------- - -Eric Firing went on a bug fixing and closing marathon, closing over -100 bugs on the `bug tracker -`__ with -help from Jae-Joon Lee, Michael Droettboom, Christoph Gohlke and -Michiel de Hoon. diff --git a/doc/users/prev_whats_new/whats_new_1.5.rst b/doc/users/prev_whats_new/whats_new_1.5.rst deleted file mode 100644 index a9fd1e83f4e7..000000000000 --- a/doc/users/prev_whats_new/whats_new_1.5.rst +++ /dev/null @@ -1,731 +0,0 @@ -.. _whats-new-1-5: - -New in matplotlib 1.5 -===================== - -.. contents:: Table of Contents - :depth: 2 - - -.. note:: - - matplotlib 1.5 supports Python 2.7, 3.4, and 3.5 - - - - - - -Interactive OO usage --------------------- - -All `Artists` now keep track of if their internal state has been -changed but not reflected in the display ('stale') by a call to -``draw``. It is thus possible to pragmatically determine if a given -`Figure` needs to be re-drawn in an interactive session. - -To facilitate interactive usage a ``draw_all`` method has been added -to ``pyplot`` which will redraw all of the figures which are 'stale'. - -To make this convenient for interactive use matplotlib now registers -a function either with IPython's 'post_execute' event or with the -displayhook in the standard python REPL to automatically call -``plt.draw_all`` just before control is returned to the REPL. This ensures -that the draw command is deferred and only called once. - -The upshot of this is that for interactive backends (including -``%matplotlib notebook``) in interactive mode (with ``plt.ion()``) - -.. ipython :: python - - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - - ln, = ax.plot([0, 1, 4, 9, 16]) - - plt.show() - - ln.set_color('g') - - -will automatically update the plot to be green. Any subsequent -modifications to the ``Artist`` objects will do likewise. - -This is the first step of a larger consolidation and simplification of -the pyplot internals. - - -Working with labeled data like pandas DataFrames ------------------------------------------------- -Plot methods which take arrays as inputs can now also work with labeled data -and unpack such data. - -This means that the following two examples produce the same plot: - -Example :: - - df = pandas.DataFrame({"var1":[1,2,3,4,5,6], "var2":[1,2,3,4,5,6]}) - plt.plot(df["var1"], df["var2"]) - - -Example :: - - plt.plot("var1", "var2", data=df) - -This works for most plotting methods, which expect arrays/sequences as -inputs. ``data`` can be anything which supports ``__getitem__`` -(``dict``, ``pandas.DataFrame``, ``h5py``, ...) to access ``array`` like -values with string keys. - -In addition to this, some other changes were made, which makes working with -labeled data (ex ``pandas.Series``) easier: - -* For plotting methods with ``label`` keyword argument, one of the - data inputs is designated as the label source. If the user does not - supply a ``label`` that value object will be introspected for a - label, currently by looking for a ``name`` attribute. If the value - object does not have a ``name`` attribute but was specified by as a - key into the ``data`` kwarg, then the key is used. In the above - examples, this results in an implicit ``label="var2"`` for both - cases. - -* ``plot()`` now uses the index of a ``Series`` instead of - ``np.arange(len(y))``, if no ``x`` argument is supplied. - - -Added ``axes.prop_cycle`` key to rcParams ------------------------------------------ - -This is a more generic form of the now-deprecated ``axes.color_cycle`` param. -Now, we can cycle more than just colors, but also linestyles, hatches, -and just about any other artist property. Cycler notation is used for -defining property cycles. Adding cyclers together will be like you are -`zip()`-ing together two or more property cycles together:: - - axes.prop_cycle: cycler('color', 'rgb') + cycler('lw', [1, 2, 3]) - -You can even multiply cyclers, which is like using `itertools.product()` -on two or more property cycles. Remember to use parentheses if writing -a multi-line `prop_cycle` parameter. - -.. plot:: mpl_examples/color/color_cycle_demo.py - - -New Colormaps --------------- - -All four of the colormaps proposed as the new default are available -as ``'viridis'`` (the new default in 2.0), ``'magma'``, ``'plasma'``, and -``'inferno'`` - -.. plot:: - - import numpy as np - from cycler import cycler - cmap = cycler('cmap', ['viridis', 'magma','plasma', 'inferno']) - x_mode = cycler('x', [1, 2]) - y_mode = cycler('y', x_mode) - - cy = (x_mode * y_mode) + cmap - - def demo(ax, x, y, cmap): - X, Y = np.ogrid[0:2*np.pi:200j, 0:2*np.pi:200j] - data = np.sin(X*x) * np.cos(Y*y) - ax.imshow(data, interpolation='none', cmap=cmap) - ax.set_title(cmap) - - fig, axes = plt.subplots(2, 2) - for ax, sty in zip(axes.ravel(), cy): - demo(ax, **sty) - - fig.tight_layout() - - -Styles ------- - -Several new styles have been added, including many styles from the -Seaborn project. Additionally, in order to prep for the upcoming 2.0 -style-change release, a 'classic' and 'default' style has been added. -For this release, the 'default' and 'classic' styles are identical. -By using them now in your scripts, you can help ensure a smooth -transition during future upgrades of matplotlib, so that you can -upgrade to the snazzy new defaults when you are ready! :: - - import matplotlib.style - matplotlib.style.use('classic') - -The 'default' style will give you matplotlib's latest plotting styles:: - - matplotlib.style.use('default') - -Backends --------- - -New backend selection -````````````````````` - -The environment variable :envvar:`MPLBACKEND` can now be used to set the -matplotlib backend. - - -wx backend has been updated -``````````````````````````` - -The wx backend can now be used with both wxPython classic and -`Phoenix `__. - -wxPython classic has to be at least version 2.8.12 and works on Python 2.x. As -of May 2015 no official release of wxPython Phoenix is available but a -current snapshot will work on Python 2.7+ and 3.4+. - -If you have multiple versions of wxPython installed, then the user code is -responsible setting the wxPython version. How to do this is -explained in the comment at the beginning of the example -`examples\user_interfaces\embedding_in_wx2.py`. - -Configuration (rcParams) ------------------------- - -Some parameters have been added, others have been improved. - -+-------------------------+--------------------------------------------------+ -| Parameter | Description | -+=========================+==================================================+ -|`{x,y}axis.labelpad` | mplot3d now respects these parameters | -+-------------------------+--------------------------------------------------+ -|`axes.labelpad` | Default space between the axis and the label | -+-------------------------+--------------------------------------------------+ -|`errorbar.capsize` | Default length of end caps on error bars | -+-------------------------+--------------------------------------------------+ -|`{x,y}tick.minor.visible`| Default visibility of minor x/y ticks | -+-------------------------+--------------------------------------------------+ -|`legend.framealpha` | Default transparency of the legend frame box | -+-------------------------+--------------------------------------------------+ -|`legend.facecolor` | Default facecolor of legend frame box (or | -| | ``'inherit'`` from `axes.facecolor`) | -+-------------------------+--------------------------------------------------+ -|`legend.edgecolor` | Default edgecolor of legend frame box (or | -| | ``'inherit'`` from `axes.edgecolor`) | -+-------------------------+--------------------------------------------------+ -|`figure.titlesize` | Default font size for figure suptitles | -+-------------------------+--------------------------------------------------+ -|`figure.titleweight` | Default font weight for figure suptitles | -+-------------------------+--------------------------------------------------+ -|`image.composite_image` | Whether a vector graphics backend should | -| | composite several images into a single image or | -| | not when saving. Useful when needing to edit the | -| | files further in Inkscape or other programs. | -+-------------------------+--------------------------------------------------+ -|`markers.fillstyle` | Default fillstyle of markers. Possible values | -| | are ``'full'`` (the default), ``'left'``, | -| | ``'right'``, ``'bottom'``, ``'top'`` and | -| | ``'none'`` | -+-------------------------+--------------------------------------------------+ -|`toolbar` | Added ``'toolmanager'`` as a valid value, | -| | enabling the experimental ``ToolManager`` | -| | feature. | -+-------------------------+--------------------------------------------------+ - - -Widgets -------- - -Active state of Selectors -````````````````````````` - -All selectors now implement ``set_active`` and ``get_active`` methods (also -called when accessing the ``active`` property) to properly update and query -whether they are active. - - -Moved ``ignore``, ``set_active``, and ``get_active`` methods to base class ``Widget`` -````````````````````````````````````````````````````````````````````````````````````` - -Pushes up duplicate methods in child class to parent class to avoid duplication of code. - - -Adds enable/disable feature to MultiCursor -`````````````````````````````````````````` - -A MultiCursor object can be disabled (and enabled) after it has been created without destroying the object. -Example:: - - multi_cursor.active = False - - -Improved RectangleSelector and new EllipseSelector Widget -````````````````````````````````````````````````````````` - -Adds an `interactive` keyword which enables visible handles for manipulating the shape after it has been drawn. - -Adds keyboard modifiers for: - -- Moving the existing shape (default key = 'space') -- Making the shape square (default 'shift') -- Make the initial point the center of the shape (default 'control') -- Square and center can be combined - -Allow Artists to Display Pixel Data in Cursor -````````````````````````````````````````````` - -Adds `get_pixel_data` and `format_pixel_data` methods to artists -which can be used to add zdata to the cursor display -in the status bar. Also adds an implementation for Images. - - -New plotting features ---------------------- - - -Auto-wrapping Text -`````````````````` - -Added the keyword argument "wrap" to Text, which automatically breaks -long lines of text when being drawn. Works for any rotated text, -different modes of alignment, and for text that are either labels or -titles. This breaks at the ``Figure``, not ``Axes`` edge. - -.. plot:: - - fig, ax = plt.subplots() - fig.patch.set_color('.9') - ax.text(.5, .75, - "This is a really long string that should be wrapped so that " - "it does not go outside the figure.", wrap=True) - -Contour plot corner masking -``````````````````````````` - -Ian Thomas rewrote the C++ code that calculates contours to add support for -corner masking. This is controlled by a new keyword argument -``corner_mask`` in the functions :func:`~matplotlib.pyplot.contour` and -:func:`~matplotlib.pyplot.contourf`. The previous behaviour, which is now -obtained using ``corner_mask=False``, was for a single masked point to -completely mask out all four quads touching that point. The new behaviour, -obtained using ``corner_mask=True``, only masks the corners of those -quads touching the point; any triangular corners comprising three unmasked -points are contoured as usual. If the ``corner_mask`` keyword argument is not -specified, the default value is taken from rcParams. - -.. plot:: mpl_examples/pylab_examples/contour_corner_mask.py - - -Mostly unified linestyles for `Line2D`, `Patch` and `Collection` -````````````````````````````````````````````````````````````````` - -The handling of linestyles for Lines, Patches and Collections has been -unified. Now they all support defining linestyles with short symbols, -like `"--"`, as well as with full names, like ``"dashed"``. Also the -definition using a dash pattern (``(0., [3., 3.])``) is supported for all -methods using `Line2D`, `Patch` or ``Collection``. - - -Legend marker order -``````````````````` - -Added ability to place the label before the marker in a legend box with -``markerfirst`` keyword - - -Support for legend for PolyCollection and stackplot -``````````````````````````````````````````````````` - -Added a `legend_handler` for :class:`~matplotlib.collections.PolyCollection` as well as a `labels` argument to -:func:`~matplotlib.axes.Axes.stackplot`. - - -Support for alternate pivots in mplot3d quiver plot -``````````````````````````````````````````````````` - -Added a :code:`pivot` kwarg to :func:`~mpl_toolkits.mplot3d.Axes3D.quiver` -that controls the pivot point around which the quiver line rotates. This also -determines the placement of the arrow head along the quiver line. - - -Logit Scale -``````````` - -Added support for the 'logit' axis scale, a nonlinear transformation - -.. math:: - - x -> \log10(x / (1-x)) - -for data between 0 and 1 excluded. - - -Add step kwargs to fill_between -``````````````````````````````` - -Added ``step`` kwarg to `Axes.fill_between` to allow to fill between -lines drawn using the 'step' draw style. The values of ``step`` match -those of the ``where`` kwarg of `Axes.step`. The asymmetry of of the -kwargs names is not ideal, but `Axes.fill_between` already has a -``where`` kwarg. - -This is particularly useful for plotting pre-binned histograms. - -.. plot:: mpl_examples/api/filled_step.py - - -Square Plot -``````````` - -Implemented square plots feature as a new parameter in the axis -function. When argument 'square' is specified, equal scaling is set, -and the limits are set such that ``xmax-xmin == ymax-ymin``. - -.. plot:: - - fig, ax = plt.subplots() - ax.axis('square') - - -Updated figimage to take optional resize parameter -`````````````````````````````````````````````````` - -Added the ability to plot simple 2D-Array using ``plt.figimage(X, resize=True)``. -This is useful for plotting simple 2D-Array without the Axes or whitespacing -around the image. - -.. plot:: - - data = np.random.random([500, 500]) - plt.figimage(data, resize=True) - -Updated Figure.savefig() can now use figure's dpi -````````````````````````````````````````````````` - -Added support to save the figure with the same dpi as the figure on the -screen using `dpi='figure'`. - -Example:: - - f = plt.figure(dpi=25) # dpi set to 25 - S = plt.scatter([1,2,3],[4,5,6]) - f.savefig('output.png', dpi='figure') # output savefig dpi set to 25 (same as figure) - - -Updated Table to control edge visibility -```````````````````````````````````````` - -Added the ability to toggle the visibility of lines in Tables. -Functionality added to the :func:`pyplot.table` factory function under -the keyword argument "edges". Values can be the strings "open", "closed", -"horizontal", "vertical" or combinations of the letters "L", "R", "T", -"B" which represent left, right, top, and bottom respectively. - -Example:: - - table(..., edges="open") # No line visible - table(..., edges="closed") # All lines visible - table(..., edges="horizontal") # Only top and bottom lines visible - table(..., edges="LT") # Only left and top lines visible. - -Zero r/cstride support in plot_wireframe -```````````````````````````````````````` - -Adam Hughes added support to mplot3d's plot_wireframe to draw only row or -column line plots. - - -.. plot:: - - from mpl_toolkits.mplot3d import Axes3D, axes3d - fig = plt.figure() - ax = fig.add_subplot(111, projection='3d') - X, Y, Z = axes3d.get_test_data(0.05) - ax.plot_wireframe(X, Y, Z, rstride=10, cstride=0) - - -Plot bar and barh with labels -````````````````````````````` - -Added kwarg ``"tick_label"`` to `bar` and `barh` to support plotting bar graphs with a -text label for each bar. - -.. plot:: - - plt.bar([1, 2], [.5, .75], tick_label=['bar1', 'bar2'], - align='center') - -Added center and frame kwargs to pie -```````````````````````````````````` - -These control where the center of the pie graph are and if -the Axes frame is shown. - -Fixed 3D filled contour plot polygon rendering -`````````````````````````````````````````````` - -Certain cases of 3D filled contour plots that produce polygons with multiple -holes produced improper rendering due to a loss of path information between -:class:`~matplotlib.collections.PolyCollection` and -:class:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection`. A function -:func:`~matplotlib.collections.PolyCollection.set_verts_and_codes` was -added to allow path information to be retained for proper rendering. - -Dense colorbars are rasterized -`````````````````````````````` - -Vector file formats (pdf, ps, svg) are efficient for -many types of plot element, but for some they can yield -excessive file size and even rendering artifacts, depending -on the renderer used for screen display. This is a problem -for colorbars that show a large number of shades, as is -most commonly the case. Now, if a colorbar is showing -50 or more colors, it will be rasterized in vector -backends. - - -DateFormatter strftime -`````````````````````` -:class:`~matplotlib.dates.DateFormatter`s' -:meth:`~matplotlib.dates.DateFormatter.strftime` method will format -a :class:`datetime.datetime` object with the format string passed to -the formatter's constructor. This method accepts datetimes with years -before 1900, unlike :meth:`datetime.datetime.strftime`. - - -Artist-level {get,set}_usetex for text -`````````````````````````````````````` - -Add ``{get,set}_usetex`` methods to :class:`~matplotlib.text.Text` objects -which allow artist-level control of LaTeX rendering vs the internal mathtex -rendering. - - -`ax.remove()` works as expected -``````````````````````````````` - -As with artists added to an :class:`~matplotlib.axes.Axes`, -`Axes` objects can be removed from their figure via -:meth:`~matplotlib.axes.Axes.remove()`. - - -API Consistency fix within Locators set_params() function -````````````````````````````````````````````````````````` - -:meth:`~matplotlib.ticker.Locator.set_params` function, which sets parameters -within a :class:`~matplotlib.ticker.Locator` type -instance, is now available to all `Locator` types. The implementation -also prevents unsafe usage by strictly defining the parameters that a -user can set. - -To use, call ``set_params()`` on a `Locator` instance with desired arguments: -:: - - loc = matplotlib.ticker.LogLocator() - # Set given attributes for loc. - loc.set_params(numticks=8, numdecs=8, subs=[2.0], base=8) - # The below will error, as there is no such parameter for LogLocator - # named foo - # loc.set_params(foo='bar') - - -Date Locators -````````````` - -Date Locators (derived from :class:`~matplotlib.dates.DateLocator`) now -implement the :meth:`~matplotlib.tickers.Locator.tick_values` method. -This is expected of all Locators derived from :class:`~matplotlib.tickers.Locator`. - -The Date Locators can now be used easily without creating axes :: - - from datetime import datetime - from matplotlib.dates import YearLocator - t0 = datetime(2002, 10, 9, 12, 10) - tf = datetime(2005, 10, 9, 12, 15) - loc = YearLocator() - values = loc.tick_values(t0, tf) - -OffsetBoxes now support clipping -```````````````````````````````` - -`Artists` draw onto objects of type :class:`~OffsetBox` -through :class:`~OffsetBox.DrawingArea` and :class:`~OffsetBox.TextArea`. -The `TextArea` calculates the required space for the text and so the -text is always within the bounds, for this nothing has changed. - -However, `DrawingArea` acts as a parent for zero or more `Artists` that -draw on it and may do so beyond the bounds. Now child `Artists` can be -clipped to the bounds of the `DrawingArea`. - - -OffsetBoxes now considered by tight_layout -`````````````````````````````````````````` - -When `~matplotlib.pyplot.tight_layout()` or `Figure.tight_layout()` -or `GridSpec.tight_layout()` is called, `OffsetBoxes` that are -anchored outside the axes will not get chopped out. The `OffsetBoxes` will -also not get overlapped by other axes in case of multiple subplots. - -Per-page pdf notes in multi-page pdfs (PdfPages) -```````````````````````````````````````````````` - -Add a new method :meth:`~matplotlib.backends.backend_pdf.PdfPages.attach_note` -to the PdfPages class, allowing the -attachment of simple text notes to pages in a multi-page pdf of -figures. The new note is visible in the list of pdf annotations in a -viewer that has this facility (Adobe Reader, OSX Preview, Skim, -etc.). Per default the note itself is kept off-page to prevent it to -appear in print-outs. - -`PdfPages.attach_note` needs to be called before `savefig()` in order to be -added to the correct figure. - -Updated fignum_exists to take figure name -````````````````````````````````````````` - -Added the ability to check the existence of a figure using its name -instead of just the figure number. -Example:: - - figure('figure') - fignum_exists('figure') #true - - -ToolManager ------------ - -Federico Ariza wrote the new `~matplotlib.backend_managers.ToolManager` -that comes as replacement for `NavigationToolbar2` - -`ToolManager` offers a new way of looking at the user interactions -with the figures. Before we had the `NavigationToolbar2` with its own -tools like `zoom/pan/home/save/...` and also we had the shortcuts like -`yscale/grid/quit/....` `Toolmanager` relocate all those actions as -`Tools` (located in `~matplotlib.backend_tools`), and defines a way to -`access/trigger/reconfigure` them. - -The `Toolbars` are replaced for `ToolContainers` that are just GUI -interfaces to `trigger` the tools. But don't worry the default -backends include a `ToolContainer` called `toolbar` - - -.. note:: - At the moment, we release this primarily for feedback purposes and should - be treated as experimental until further notice as API changes will occur. - For the moment the `ToolManager` works only with the `GTK3` and `Tk` backends. - Make sure you use one of those. - Port for the rest of the backends is comming soon. - - To activate the `ToolManager` include the following at the top of your file :: - - >>> matplotlib.rcParams['toolbar'] = 'toolmanager' - - -Interact with the ToolContainer -``````````````````````````````` - -The most important feature is the ability to easily reconfigure the ToolContainer (aka toolbar). -For example, if we want to remove the "forward" button we would just do. :: - - >>> fig.canvas.manager.toolmanager.remove_tool('forward') - -Now if you want to programmatically trigger the "home" button :: - - >>> fig.canvas.manager.toolmanager.trigger_tool('home') - - -New Tools for ToolManager -````````````````````````` - -It is possible to add new tools to the ToolManager - -A very simple tool that prints "You're awesome" would be:: - - from matplotlib.backend_tools import ToolBase - class AwesomeTool(ToolBase): - def trigger(self, *args, **kwargs): - print("You're awesome") - - -To add this tool to `ToolManager` - - >>> fig.canvas.manager.toolmanager.add_tool('Awesome', AwesomeTool) - -If we want to add a shortcut ("d") for the tool - - >>> fig.canvas.manager.toolmanager.update_keymap('Awesome', 'd') - - -To add it to the toolbar inside the group 'foo' - - >>> fig.canvas.manager.toolbar.add_tool('Awesome', 'foo') - - -There is a second class of tools, "Toggleable Tools", this are almost -the same as our basic tools, just that belong to a group, and are -mutually exclusive inside that group. For tools derived from -`ToolToggleBase` there are two basic methods `enable` and `disable` -that are called automatically whenever it is toggled. - - -A full example is located in :ref:`user_interfaces-toolmanager` - - -cbook.is_sequence_of_strings recognizes string objects ------------------------------------------------------- - -This is primarily how pandas stores a sequence of strings :: - - import pandas as pd - import matplotlib.cbook as cbook - - a = np.array(['a', 'b', 'c']) - print(cbook.is_sequence_of_strings(a)) # True - - a = np.array(['a', 'b', 'c'], dtype=object) - print(cbook.is_sequence_of_strings(a)) # True - - s = pd.Series(['a', 'b', 'c']) - print(cbook.is_sequence_of_strings(s)) # True - -Previously, the last two prints returned false. - - -New ``close-figs`` argument for plot directive ----------------------------------------------- - -Matplotlib has a sphinx extension ``plot_directive`` that creates plots for -inclusion in sphinx documents. Matplotlib 1.5 adds a new option to the plot -directive - ``close-figs`` - that closes any previous figure windows before -creating the plots. This can help avoid some surprising duplicates of plots -when using ``plot_directive``. - -Support for URL string arguments to ``imread`` ----------------------------------------------- - -The :func:`~matplotlib.pyplot.imread` function now accepts URL strings that -point to remote PNG files. This circumvents the generation of a -HTTPResponse object directly. - -Display hook for animations in the IPython notebook ---------------------------------------------------- - -`~matplotlib.animation.Animation` instances gained a ``_repr_html_`` method -to support inline display of animations in the notebook. The method used -to display is controlled by the ``animation.html`` rc parameter, which -currently supports values of ``none`` and ``html5``. ``none`` is the -default, performing no display. ``html5`` converts the animation to an -h264 encoded video, which is embedded directly in the notebook. - -Users not wishing to use the ``_repr_html_`` display hook can also manually -call the `to_html5_video` method to get the HTML and display using -IPython's ``HTML`` display class:: - - from IPython.display import HTML - HTML(anim.to_html5_video()) - -Prefixed pkg-config for building --------------------------------- - -Handling of `pkg-config` has been fixed in so far as it is now -possible to set it using the environment variable `PKG_CONFIG`. This -is important if your toolchain is prefixed. This is done in a simpilar -way as setting `CC` or `CXX` before building. An example follows. - - export PKG_CONFIG=x86_64-pc-linux-gnu-pkg-config diff --git a/doc/users/pyplot_tutorial.rst b/doc/users/pyplot_tutorial.rst deleted file mode 100644 index 5cdf8f9941b0..000000000000 --- a/doc/users/pyplot_tutorial.rst +++ /dev/null @@ -1,307 +0,0 @@ -.. _pyplot-tutorial: - -*************** -Pyplot tutorial -*************** - -:mod:`matplotlib.pyplot` is a collection of command style functions -that make matplotlib work like MATLAB. -Each ``pyplot`` function makes -some change to a figure: e.g., creates a figure, creates a plotting area -in a figure, plots some lines in a plotting area, decorates the plot -with labels, etc. In :mod:`matplotlib.pyplot` various states are preserved -across function calls, so that it keeps track of things like -the current figure and plotting area, and the plotting -functions are directed to the current axes (please note that "axes" here -and in most places in the documentation refers to the *axes* -`part of a figure `__ -and not the strict mathematical term for more than one axis). - -.. plot:: pyplots/pyplot_simple.py - :include-source: - -You may be wondering why the x-axis ranges from 0-3 and the y-axis -from 1-4. If you provide a single list or array to the -:func:`~matplotlib.pyplot.plot` command, matplotlib assumes it is a -sequence of y values, and automatically generates the x values for -you. Since python ranges start with 0, the default x vector has the -same length as y but starts with 0. Hence the x data are -``[0,1,2,3]``. - -:func:`~matplotlib.pyplot.plot` is a versatile command, and will take -an arbitrary number of arguments. For example, to plot x versus y, -you can issue the command:: - - plt.plot([1, 2, 3, 4], [1, 4, 9, 16]) - -For every x, y pair of arguments, there is an optional third argument -which is the format string that indicates the color and line type of -the plot. The letters and symbols of the format string are from -MATLAB, and you concatenate a color string with a line style string. -The default format string is 'b-', which is a solid blue line. For -example, to plot the above with red circles, you would issue - -.. plot:: pyplots/pyplot_formatstr.py - :include-source: - -See the :func:`~matplotlib.pyplot.plot` documentation for a complete -list of line styles and format strings. The -:func:`~matplotlib.pyplot.axis` command in the example above takes a -list of ``[xmin, xmax, ymin, ymax]`` and specifies the viewport of the -axes. - -If matplotlib were limited to working with lists, it would be fairly -useless for numeric processing. Generally, you will use `numpy -`_ arrays. In fact, all sequences are -converted to numpy arrays internally. The example below illustrates a -plotting several lines with different format styles in one command -using arrays. - -.. plot:: pyplots/pyplot_three.py - :include-source: - -.. _controlling-line-properties: - -Controlling line properties -=========================== - -Lines have many attributes that you can set: linewidth, dash style, -antialiased, etc; see :class:`matplotlib.lines.Line2D`. There are -several ways to set line properties - -* Use keyword args:: - - plt.plot(x, y, linewidth=2.0) - - -* Use the setter methods of a ``Line2D`` instance. ``plot`` returns a list - of ``Line2D`` objects; e.g., ``line1, line2 = plot(x1, y1, x2, y2)``. In the code - below we will suppose that we have only - one line so that the list returned is of length 1. We use tuple unpacking with - ``line,`` to get the first element of that list:: - - line, = plt.plot(x, y, '-') - line.set_antialiased(False) # turn off antialising - -* Use the :func:`~matplotlib.pyplot.setp` command. The example below - uses a MATLAB-style command to set multiple properties - on a list of lines. ``setp`` works transparently with a list of objects - or a single object. You can either use python keyword arguments or - MATLAB-style string/value pairs:: - - lines = plt.plot(x1, y1, x2, y2) - # use keyword args - plt.setp(lines, color='r', linewidth=2.0) - # or MATLAB style string value pairs - plt.setp(lines, 'color', 'r', 'linewidth', 2.0) - - -Here are the available :class:`~matplotlib.lines.Line2D` properties. - -====================== ================================================== -Property Value Type -====================== ================================================== -alpha float -animated [True | False] -antialiased or aa [True | False] -clip_box a matplotlib.transform.Bbox instance -clip_on [True | False] -clip_path a Path instance and a Transform instance, a Patch -color or c any matplotlib color -contains the hit testing function -dash_capstyle [``'butt'`` | ``'round'`` | ``'projecting'``] -dash_joinstyle [``'miter'`` | ``'round'`` | ``'bevel'``] -dashes sequence of on/off ink in points -data (np.array xdata, np.array ydata) -figure a matplotlib.figure.Figure instance -label any string -linestyle or ls [ ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'steps'`` | ...] -linewidth or lw float value in points -lod [True | False] -marker [ ``'+'`` | ``','`` | ``'.'`` | ``'1'`` | ``'2'`` | ``'3'`` | ``'4'`` ] -markeredgecolor or mec any matplotlib color -markeredgewidth or mew float value in points -markerfacecolor or mfc any matplotlib color -markersize or ms float -markevery [ None | integer | (startind, stride) ] -picker used in interactive line selection -pickradius the line pick selection radius -solid_capstyle [``'butt'`` | ``'round'`` | ``'projecting'``] -solid_joinstyle [``'miter'`` | ``'round'`` | ``'bevel'``] -transform a matplotlib.transforms.Transform instance -visible [True | False] -xdata np.array -ydata np.array -zorder any number -====================== ================================================== - -To get a list of settable line properties, call the -:func:`~matplotlib.pyplot.setp` function with a line or lines -as argument - -.. sourcecode:: ipython - - In [69]: lines = plt.plot([1, 2, 3]) - - In [70]: plt.setp(lines) - alpha: float - animated: [True | False] - antialiased or aa: [True | False] - ...snip - -.. _multiple-figs-axes: - -Working with multiple figures and axes -====================================== - - -MATLAB, and :mod:`~matplotlib.pyplot`, have the concept of the current -figure and the current axes. All plotting commands apply to the -current axes. The function :func:`~matplotlib.pyplot.gca` returns the -current axes (a :class:`matplotlib.axes.Axes` instance), and -:func:`~matplotlib.pyplot.gcf` returns the current figure -(:class:`matplotlib.figure.Figure` instance). Normally, you don't have -to worry about this, because it is all taken care of behind the -scenes. Below is a script to create two subplots. - -.. plot:: pyplots/pyplot_two_subplots.py - :include-source: - -The :func:`~matplotlib.pyplot.figure` command here is optional because -``figure(1)`` will be created by default, just as a ``subplot(111)`` -will be created by default if you don't manually specify any axes. The -:func:`~matplotlib.pyplot.subplot` command specifies ``numrows, -numcols, fignum`` where ``fignum`` ranges from 1 to -``numrows*numcols``. The commas in the ``subplot`` command are -optional if ``numrows*numcols<10``. So ``subplot(211)`` is identical -to ``subplot(2, 1, 1)``. You can create an arbitrary number of subplots -and axes. If you want to place an axes manually, i.e., not on a -rectangular grid, use the :func:`~matplotlib.pyplot.axes` command, -which allows you to specify the location as ``axes([left, bottom, -width, height])`` where all values are in fractional (0 to 1) -coordinates. See :ref:`pylab_examples-axes_demo` for an example of -placing axes manually and :ref:`pylab_examples-subplots_demo` for an -example with lots of subplots. - - -You can create multiple figures by using multiple -:func:`~matplotlib.pyplot.figure` calls with an increasing figure -number. Of course, each figure can contain as many axes and subplots -as your heart desires:: - - import matplotlib.pyplot as plt - plt.figure(1) # the first figure - plt.subplot(211) # the first subplot in the first figure - plt.plot([1, 2, 3]) - plt.subplot(212) # the second subplot in the first figure - plt.plot([4, 5, 6]) - - - plt.figure(2) # a second figure - plt.plot([4, 5, 6]) # creates a subplot(111) by default - - plt.figure(1) # figure 1 current; subplot(212) still current - plt.subplot(211) # make subplot(211) in figure1 current - plt.title('Easy as 1, 2, 3') # subplot 211 title - -You can clear the current figure with :func:`~matplotlib.pyplot.clf` -and the current axes with :func:`~matplotlib.pyplot.cla`. If you find -it annoying that states (specifically the current image, figure and axes) -are being maintained for you behind the scenes, don't despair: this is just a thin -stateful wrapper around an object oriented API, which you can use -instead (see :ref:`artist-tutorial`) - -If you are making lots of figures, you need to be aware of one -more thing: the memory required for a figure is not completely -released until the figure is explicitly closed with -:func:`~matplotlib.pyplot.close`. Deleting all references to the -figure, and/or using the window manager to kill the window in which -the figure appears on the screen, is not enough, because pyplot -maintains internal references until :func:`~matplotlib.pyplot.close` -is called. - -.. _working-with-text: - -Working with text -================= - -The :func:`~matplotlib.pyplot.text` command can be used to add text in -an arbitrary location, and the :func:`~matplotlib.pyplot.xlabel`, -:func:`~matplotlib.pyplot.ylabel` and :func:`~matplotlib.pyplot.title` -are used to add text in the indicated locations (see :ref:`text-intro` -for a more detailed example) - -.. plot:: pyplots/pyplot_text.py - :include-source: - - -All of the :func:`~matplotlib.pyplot.text` commands return an -:class:`matplotlib.text.Text` instance. Just as with with lines -above, you can customize the properties by passing keyword arguments -into the text functions or using :func:`~matplotlib.pyplot.setp`:: - - t = plt.xlabel('my data', fontsize=14, color='red') - -These properties are covered in more detail in :ref:`text-properties`. - - -Using mathematical expressions in text --------------------------------------- - -matplotlib accepts TeX equation expressions in any text expression. -For example to write the expression :math:`\sigma_i=15` in the title, -you can write a TeX expression surrounded by dollar signs:: - - plt.title(r'$\sigma_i=15$') - -The ``r`` preceding the title string is important -- it signifies -that the string is a *raw* string and not to treat backslashes as -python escapes. matplotlib has a built-in TeX expression parser and -layout engine, and ships its own math fonts -- for details see -:ref:`mathtext-tutorial`. Thus you can use mathematical text across platforms -without requiring a TeX installation. For those who have LaTeX and -dvipng installed, you can also use LaTeX to format your text and -incorporate the output directly into your display figures or saved -postscript -- see :ref:`usetex-tutorial`. - - -Annotating text ---------------- - -The uses of the basic :func:`~matplotlib.pyplot.text` command above -place text at an arbitrary position on the Axes. A common use for -text is to annotate some feature of the plot, and the -:func:`~matplotlib.pyplot.annotate` method provides helper -functionality to make annotations easy. In an annotation, there are -two points to consider: the location being annotated represented by -the argument ``xy`` and the location of the text ``xytext``. Both of -these arguments are ``(x,y)`` tuples. - -.. plot:: pyplots/pyplot_annotate.py - :include-source: - -In this basic example, both the ``xy`` (arrow tip) and ``xytext`` -locations (text location) are in data coordinates. There are a -variety of other coordinate systems one can choose -- see -:ref:`annotations-tutorial` and :ref:`plotting-guide-annotation` for -details. More examples can be found in -:ref:`pylab_examples-annotation_demo`. - - -Logarithmic and other nonlinear axis -==================================== - -:mod:`matplotlib.pyplot` supports not only linear axis scales, but also -logarithmic and logit scales. This is commonly used if data spans many orders -of magnitude. Changing the scale of an axis is easy: - - plt.xscale('log') - -An example of four plots with the same data and different scales for the y axis -is shown below. - -.. plot:: pyplots/pyplot_scales.py - :include-source: - -It is also possible to add your own scale, see :ref:`adding-new-scales` for -details. diff --git a/doc/users/recipes.rst b/doc/users/recipes.rst deleted file mode 100644 index 64e2f2cf34d0..000000000000 --- a/doc/users/recipes.rst +++ /dev/null @@ -1,379 +0,0 @@ -.. _recipes: - -******************** -Our Favorite Recipes -******************** - -Here is a collection of short tutorials, examples and code snippets -that illustrate some of the useful idioms and tricks to make snazzier -figures and overcome some matplotlib warts. - - -Sharing axis limits and views -============================= - -It's common to make two or more plots which share an axis, e.g., two -subplots with time as a common axis. When you pan and zoom around on -one, you want the other to move around with you. To facilitate this, -matplotlib Axes support a ``sharex`` and ``sharey`` attribute. When -you create a :func:`~matplotlib.pyplot.subplot` or -:func:`~matplotlib.pyplot.axes` instance, you can pass in a keyword -indicating what axes you want to share with - -.. sourcecode:: ipython - - In [96]: t = np.arange(0, 10, 0.01) - - In [97]: ax1 = plt.subplot(211) - - In [98]: ax1.plot(t, np.sin(2*np.pi*t)) - Out[98]: [] - - In [99]: ax2 = plt.subplot(212, sharex=ax1) - - In [100]: ax2.plot(t, np.sin(4*np.pi*t)) - Out[100]: [] - -Easily creating subplots -======================== - -In early versions of matplotlib, if you wanted to use the pythonic API -and create a figure instance and from that create a grid of subplots, -possibly with shared axes, it involved a fair amount of boilerplate -code. e.g. - -.. sourcecode:: python - - # old style - fig = plt.figure() - ax1 = fig.add_subplot(221) - ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1) - ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1) - ax3 = fig.add_subplot(224, sharex=ax1, sharey=ax1) - -Fernando Perez has provided a nice top level method to create in -:func:`~matplotlib.pyplots.subplots` (note the "s" at the end) -everything at once, and turn on x and y sharing for the whole bunch. -You can either unpack the axes individually:: - - # new style method 1; unpack the axes - fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex=True, sharey=True) - ax1.plot(x) - -or get them back as a numrows x numcolumns object array which supports -numpy indexing:: - - # new style method 2; use an axes array - fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) - axs[0,0].plot(x) - - - -Fixing common date annoyances -============================= - - -.. plot:: - :nofigs: - :context: - - # clear the state for context use below - plt.close('all') - -matplotlib allows you to natively plots python datetime instances, and -for the most part does a good job picking tick locations and string -formats. There are a couple of things it does not handle so -gracefully, and here are some tricks to help you work around them. -We'll load up some sample date data which contains datetime.date -objects in a numpy record array:: - - In [63]: datafile = cbook.get_sample_data('goog.npy') - - In [64]: r = np.load(datafile).view(np.recarray) - - In [65]: r.dtype - Out[65]: dtype([('date', '|O4'), ('', '|V4'), ('open', '] - -you will see that the x tick labels are all squashed together. - -.. plot:: - :context: - - import matplotlib.cbook as cbook - datafile = cbook.get_sample_data('goog.npy') - try: - # Python3 cannot load python2 .npy files with datetime(object) arrays - # unless the encoding is set to bytes. Hovever this option was - # not added until numpy 1.10 so this example will only work with - # python 2 or with numpy 1.10 and later. - r = np.load(datafile, encoding='bytes').view(np.recarray) - except TypeError: - # Old Numpy - r = np.load(datafile).view(np.recarray) - plt.figure() - plt.plot(r.date, r.close) - plt.title('Default date handling can cause overlapping labels') - -Another annoyance is that if you hover the mouse over the window and -look in the lower right corner of the matplotlib toolbar -(:ref:`navigation-toolbar`) at the x and y coordinates, you see that -the x locations are formatted the same way the tick labels are, e.g., -"Dec 2004". What we'd like is for the location in the toolbar to have -a higher degree of precision, e.g., giving us the exact date out mouse is -hovering over. To fix the first problem, we can use -:func:`matplotlib.figure.Figure.autofmt_xdate` and to fix the second -problem we can use the ``ax.fmt_xdata`` attribute which can be set to -any function that takes a scalar and returns a string. matplotlib has -a number of date formatters built in, so we'll use one of those. - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig, ax = plt.subplots(1) - ax.plot(r.date, r.close) - - # rotate and align the tick labels so they look better - fig.autofmt_xdate() - - # use a more precise date string for the x axis locations in the - # toolbar - import matplotlib.dates as mdates - ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d') - plt.title('fig.autofmt_xdate fixes the labels') - -Now when you hover your mouse over the plotted data, you'll see date -format strings like 2004-12-01 in the toolbar. - -Fill Between and Alpha -====================== - -The :meth:`~matplotlib.axes.Axes.fill_between` function generates a -shaded region between a min and max boundary that is useful for -illustrating ranges. It has a very handy ``where`` argument to -combine filling with logical ranges, e.g., to just fill in a curve over -some threshold value. - -At its most basic level, ``fill_between`` can be use to enhance a -graphs visual appearance. Let's compare two graphs of a financial -times with a simple line plot on the left and a filled line on the -right. - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import numpy as np - - import matplotlib.cbook as cbook - - # load up some sample financial data - datafile = cbook.get_sample_data('goog.npy') - try: - # Python3 cannot load python2 .npy files with datetime(object) arrays - # unless the encoding is set to bytes. Hovever this option was - # not added until numpy 1.10 so this example will only work with - # python 2 or with numpy 1.10 and later. - r = np.load(datafile, encoding='bytes').view(np.recarray) - except TypeError: - r = np.load(datafile).view(np.recarray) - # create two subplots with the shared x and y axes - fig, (ax1, ax2) = plt.subplots(1,2, sharex=True, sharey=True) - - pricemin = r.close.min() - - ax1.plot(r.date, r.close, lw=2) - ax2.fill_between(r.date, pricemin, r.close, facecolor='blue', alpha=0.5) - - for ax in ax1, ax2: - ax.grid(True) - - ax1.set_ylabel('price') - for label in ax2.get_yticklabels(): - label.set_visible(False) - - fig.suptitle('Google (GOOG) daily closing price') - fig.autofmt_xdate() - -The alpha channel is not necessary here, but it can be used to soften -colors for more visually appealing plots. In other examples, as we'll -see below, the alpha channel is functionally useful as the shaded -regions can overlap and alpha allows you to see both. Note that the -postscript format does not support alpha (this is a postscript -limitation, not a matplotlib limitation), so when using alpha save -your figures in PNG, PDF or SVG. - -Our next example computes two populations of random walkers with a -different mean and standard deviation of the normal distributions from -which the steps are drawn. We use shared regions to plot +/- one -standard deviation of the mean position of the population. Here the -alpha channel is useful, not just aesthetic. - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - import numpy as np - - Nsteps, Nwalkers = 100, 250 - t = np.arange(Nsteps) - - # an (Nsteps x Nwalkers) array of random walk steps - S1 = 0.002 + 0.01*np.random.randn(Nsteps, Nwalkers) - S2 = 0.004 + 0.02*np.random.randn(Nsteps, Nwalkers) - - # an (Nsteps x Nwalkers) array of random walker positions - X1 = S1.cumsum(axis=0) - X2 = S2.cumsum(axis=0) - - - # Nsteps length arrays empirical means and standard deviations of both - # populations over time - mu1 = X1.mean(axis=1) - sigma1 = X1.std(axis=1) - mu2 = X2.mean(axis=1) - sigma2 = X2.std(axis=1) - - # plot it! - fig, ax = plt.subplots(1) - ax.plot(t, mu1, lw=2, label='mean population 1', color='blue') - ax.plot(t, mu2, lw=2, label='mean population 2', color='yellow') - ax.fill_between(t, mu1+sigma1, mu1-sigma1, facecolor='blue', alpha=0.5) - ax.fill_between(t, mu2+sigma2, mu2-sigma2, facecolor='yellow', alpha=0.5) - ax.set_title('random walkers empirical $\mu$ and $\pm \sigma$ interval') - ax.legend(loc='upper left') - ax.set_xlabel('num steps') - ax.set_ylabel('position') - ax.grid() - - -The ``where`` keyword argument is very handy for highlighting certain -regions of the graph. ``where`` takes a boolean mask the same length -as the x, ymin and ymax arguments, and only fills in the region where -the boolean mask is True. In the example below, we simulate a single -random walker and compute the analytic mean and standard deviation of -the population positions. The population mean is shown as the black -dashed line, and the plus/minus one sigma deviation from the mean is -shown as the yellow filled region. We use the where mask -``X>upper_bound`` to find the region where the walker is above the one -sigma boundary, and shade that region blue. - -.. plot:: - :include-source: - - np.random.seed(1234) - - Nsteps = 500 - t = np.arange(Nsteps) - - mu = 0.002 - sigma = 0.01 - - # the steps and position - S = mu + sigma*np.random.randn(Nsteps) - X = S.cumsum() - - # the 1 sigma upper and lower analytic population bounds - lower_bound = mu*t - sigma*np.sqrt(t) - upper_bound = mu*t + sigma*np.sqrt(t) - - fig, ax = plt.subplots(1) - ax.plot(t, X, lw=2, label='walker position', color='blue') - ax.plot(t, mu*t, lw=1, label='population mean', color='black', ls='--') - ax.fill_between(t, lower_bound, upper_bound, facecolor='yellow', alpha=0.5, - label='1 sigma range') - ax.legend(loc='upper left') - - # here we use the where argument to only fill the region where the - # walker is above the population 1 sigma boundary - ax.fill_between(t, upper_bound, X, where=X>upper_bound, facecolor='blue', alpha=0.5) - ax.set_xlabel('num steps') - ax.set_ylabel('position') - ax.grid() - - -Another handy use of filled regions is to highlight horizontal or -vertical spans of an axes -- for that matplotlib has some helper -functions :meth:`~matplotlib.axes.Axes.axhspan` and -:meth:`~matplotlib.axes.Axes.axvspan` and example -:ref:`pylab_examples-axhspan_demo`. - - -Transparent, fancy legends -========================== - -Sometimes you know what your data looks like before you plot it, and -may know for instance that there won't be much data in the upper right -hand corner. Then you can safely create a legend that doesn't overlay -your data:: - - ax.legend(loc='upper right') - -Other times you don't know where your data is, and loc='best' will try -and place the legend:: - - ax.legend(loc='best') - -but still, your legend may overlap your data, and in these cases it's -nice to make the legend frame transparent. - - -.. plot:: - :include-source: - - np.random.seed(1234) - fig, ax = plt.subplots(1) - ax.plot(np.random.randn(300), 'o-', label='normal distribution') - ax.plot(np.random.rand(300), 's-', label='uniform distribution') - ax.set_ylim(-3, 3) - ax.legend(loc='best', fancybox=True, framealpha=0.5) - - ax.set_title('fancy, transparent legends') - - -Placing text boxes -================== - -When decorating axes with text boxes, two useful tricks are to place -the text in axes coordinates (see :ref:`transforms_tutorial`), so the -text doesn't move around with changes in x or y limits. You can also -use the ``bbox`` property of text to surround the text with a -:class:`~matplotlib.patches.Patch` instance -- the ``bbox`` keyword -argument takes a dictionary with keys that are Patch properties. - -.. plot:: - :include-source: - - np.random.seed(1234) - fig, ax = plt.subplots(1) - x = 30*np.random.randn(10000) - mu = x.mean() - median = np.median(x) - sigma = x.std() - textstr = '$\mu=%.2f$\n$\mathrm{median}=%.2f$\n$\sigma=%.2f$'%(mu, median, sigma) - - ax.hist(x, 50) - # these are matplotlib.patch.Patch properties - props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) - - # place a text box in upper left in axes coords - ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14, - verticalalignment='top', bbox=props) diff --git a/doc/users/resources/index.rst b/doc/users/resources/index.rst new file mode 100644 index 000000000000..a31dbc83aa9d --- /dev/null +++ b/doc/users/resources/index.rst @@ -0,0 +1,97 @@ +.. _resources-index: + +.. redirect-from:: /resources/index + +****************** +External resources +****************** + + +============================ +Books, chapters and articles +============================ + +* `Scientific Visualization: Python + Matplotlib (2021) + `_ + by Nicolas P. Rougier + +* `Mastering matplotlib + `_ + by Duncan M. McGreggor + +* `Interactive Applications Using Matplotlib + `_ + by Benjamin Root + +* `Matplotlib for Python Developers + `_ + by Sandro Tosi + +* `Matplotlib chapter `_ + by John Hunter and Michael Droettboom in The Architecture of Open Source + Applications + +* `Ten Simple Rules for Better Figures + `_ + by Nicolas P. Rougier, Michael Droettboom and Philip E. Bourne + +* `Learning Scientific Programming with Python chapter 7 + `_ + by Christian Hill + +* `Hands-On Data Analysis with Pandas, chapters 5 and 6 + `_ + by Stefanie Molin + +====== +Videos +====== + +* `Plotting with matplotlib `_ + by Mike Müller + +* `Introduction to NumPy and Matplotlib + `_ by Eric Jones + +* `Anatomy of Matplotlib + `_ + by Benjamin Root and Hannah Aizenman + +* `Data Visualization Basics with Python (O'Reilly) + `_ + by Randal S. Olson +* `Matplotlib Introduction + `_ + by codebasics +* `Matplotlib + `_ + by Derek Banas + +========= +Tutorials +========= + +* `Matplotlib tutorial `_ + by Nicolas P. Rougier + +* `Anatomy of Matplotlib - IPython Notebooks + `_ + by Benjamin Root + +* `Beyond the Basics: Data Visualization in Python + `_ + by Stefanie Molin + +* `Matplotlib Journey: Interactive Online Course + `_ + by Yan Holtz and Joseph Barbier + +========= +Galleries +========= + +* `Past winners for JDH plotting contest `_ + by Nelle Varoquaux + +* `The Python Graph Gallery `_ + by Yan Holtz diff --git a/doc/users/screenshots.rst b/doc/users/screenshots.rst deleted file mode 100644 index 8d3c2ee2e887..000000000000 --- a/doc/users/screenshots.rst +++ /dev/null @@ -1,281 +0,0 @@ -.. _matplotlibscreenshots: - -********************** -Screenshots -********************** - -Here you'll find a host of example plots with the code that -generated them. - -Simple Plot -=========== - -Here's a very basic :func:`~matplotlib.pyplot.plot` with text labels: - -.. plot:: mpl_examples/pylab_examples/simple_plot.py - -.. _screenshots_subplot_demo: - -Subplot demo -============ - -Multiple axes (i.e. subplots) are created with the -:func:`~matplotlib.pyplot.subplot` command: - -.. plot:: mpl_examples/subplots_axes_and_figures/subplot_demo.py - -.. _screenshots_histogram_demo: - -Histograms -========== - -The :func:`~matplotlib.pyplot.hist` command automatically generates -histograms and returns the bin counts or probabilities: - -.. plot:: mpl_examples/statistics/histogram_demo_features.py - - -.. _screenshots_path_demo: - -Path demo -========= - -You can add arbitrary paths in matplotlib using the -:mod:`matplotlib.path` module: - -.. plot:: mpl_examples/shapes_and_collections/path_patch_demo.py - -.. _screenshots_mplot3d_surface: - -mplot3d -========= - -The mplot3d toolkit (see :ref:`toolkit_mplot3d-tutorial` and -:ref:`mplot3d-examples-index`) has support for simple 3d graphs -including surface, wireframe, scatter, and bar charts. - -.. plot:: mpl_examples/mplot3d/surface3d_demo.py - -Thanks to John Porter, Jonathon Taylor, Reinier Heeres, and Ben Root for -the `mplot3d` toolkit. This toolkit is included with all standard matplotlib -installs. - -.. _screenshots_ellipse_demo: - - -Streamplot -========== - -The :meth:`~matplotlib.pyplot.streamplot` function plots the streamlines of -a vector field. In addition to simply plotting the streamlines, it allows you -to map the colors and/or line widths of streamlines to a separate parameter, -such as the speed or local intensity of the vector field. - -.. plot:: mpl_examples/images_contours_and_fields/streamplot_demo_features.py - -This feature complements the :meth:`~matplotlib.pyplot.quiver` function for -plotting vector fields. Thanks to Tom Flannaghan and Tony Yu for adding the -streamplot function. - - -Ellipses -======== - -In support of the -`Phoenix `_ mission to -Mars (which used matplotlib to display ground tracking of spacecraft), -Michael Droettboom built on work by Charlie Moad to provide an extremely -accurate 8-spline approximation to elliptical arcs (see -:class:`~matplotlib.patches.Arc`), which are insensitive to zoom level. - -.. plot:: mpl_examples/pylab_examples/ellipse_demo.py - -.. _screenshots_barchart_demo: - -Bar charts -========== - -Bar charts are simple to create using the :func:`~matplotlib.pyplot.bar` -command, which includes customizations such as error bars: - -.. plot:: mpl_examples/pylab_examples/barchart_demo.py - -It's also simple to create stacked bars -(`bar_stacked.py <../examples/pylab_examples/bar_stacked.html>`_), -or horizontal bar charts -(`barh_demo.py <../examples/lines_bars_and_markers/barh_demo.html>`_). - -.. _screenshots_pie_demo: - - -Pie charts -========== - -The :func:`~matplotlib.pyplot.pie` command allows you to easily create pie -charts. Optional features include auto-labeling the percentage of area, -exploding one or more wedges from the center of the pie, and a shadow effect. -Take a close look at the attached code, which generates this figure in just -a few lines of code. - -.. plot:: mpl_examples/pie_and_polar_charts/pie_demo_features.py - -.. _screenshots_table_demo: - -Table demo -========== - -The :func:`~matplotlib.pyplot.table` command adds a text table -to an axes. - -.. plot:: mpl_examples/pylab_examples/table_demo.py - - -.. _screenshots_scatter_demo: - - -Scatter demo -============ - -The :func:`~matplotlib.pyplot.scatter` command makes a scatter plot -with (optional) size and color arguments. This example plots changes -in Google's stock price, with marker sizes reflecting the -trading volume and colors varying with time. Here, the -alpha attribute is used to make semitransparent circle markers. - -.. plot:: mpl_examples/pylab_examples/scatter_demo2.py - - -.. _screenshots_slider_demo: - -Slider demo -=========== - -Matplotlib has basic GUI widgets that are independent of the graphical -user interface you are using, allowing you to write cross GUI figures -and widgets. See :mod:`matplotlib.widgets` and the -`widget examples <../examples/widgets/index.html>`_. - -.. plot:: mpl_examples/widgets/slider_demo.py - - -.. _screenshots_fill_demo: - -Fill demo -========= - -The :func:`~matplotlib.pyplot.fill` command lets you -plot filled curves and polygons: - -.. plot:: mpl_examples/lines_bars_and_markers/fill_demo.py - -Thanks to Andrew Straw for adding this function. - -.. _screenshots_date_demo: - -Date demo -========= - -You can plot date data with major and minor ticks and custom tick formatters -for both. - -.. plot:: mpl_examples/api/date_demo.py - -See :mod:`matplotlib.ticker` and :mod:`matplotlib.dates` for details and usage. - - -.. _screenshots_log_demo: - -Log plots -========= - -The :func:`~matplotlib.pyplot.semilogx`, -:func:`~matplotlib.pyplot.semilogy` and -:func:`~matplotlib.pyplot.loglog` functions simplify the creation of -logarithmic plots. - -.. plot:: mpl_examples/pylab_examples/log_demo.py - -Thanks to Andrew Straw, Darren Dale and Gregory Lielens for contributions -log-scaling infrastructure. - -.. _screenshots_polar_demo: - -Polar plots -=========== - -The :func:`~matplotlib.pyplot.polar` command generates polar plots. - -.. plot:: mpl_examples/pylab_examples/polar_demo.py - -.. _screenshots_legend_demo: - - -Legends -======= - -The :func:`~matplotlib.pyplot.legend` command automatically -generates figure legends, with MATLAB-compatible legend placement -commands. - -.. plot:: mpl_examples/api/legend_demo.py - -Thanks to Charles Twardy for input on the legend command. - -.. _screenshots_mathtext_examples_demo: - -Mathtext_examples -================= - -Below is a sampling of the many TeX expressions now supported by matplotlib's -internal mathtext engine. The mathtext module provides TeX style mathematical -expressions using `FreeType `_ -and the DejaVu, BaKoMa computer modern, or `STIX `_ -fonts. See the :mod:`matplotlib.mathtext` module for additional details. - -.. plot:: mpl_examples/pylab_examples/mathtext_examples.py - -Matplotlib's mathtext infrastructure is an independent implementation and -does not require TeX or any external packages installed on your computer. See -the tutorial at :ref:`mathtext-tutorial`. - - -.. _screenshots_tex_demo: - -Native TeX rendering -==================== - -Although matplotlib's internal math rendering engine is quite -powerful, sometimes you need TeX. Matplotlib supports external TeX -rendering of strings with the *usetex* option. - -.. plot:: pyplots/tex_demo.py - -.. _screenshots_eeg_demo: - -EEG demo -========= - -You can embed matplotlib into pygtk, wx, Tk, or Qt applications. -Here is a screenshot of an EEG viewer called `pbrain -`__. - -.. image:: ../_static/eeg_small.png - -The lower axes uses :func:`~matplotlib.pyplot.specgram` -to plot the spectrogram of one of the EEG channels. - -For examples of how to embed matplotlib in different toolkits, see: - - * :ref:`user_interfaces-embedding_in_gtk2` - * :ref:`user_interfaces-embedding_in_wx2` - * :ref:`user_interfaces-mpl_with_glade` - * :ref:`user_interfaces-embedding_in_qt4` - * :ref:`user_interfaces-embedding_in_tk` - -XKCD-style sketch plots -======================= - -matplotlib supports plotting in the style of `xkcd -`. - -.. plot:: mpl_examples/showcase/xkcd.py diff --git a/doc/users/shell.rst b/doc/users/shell.rst deleted file mode 100644 index 3581fcccbe1e..000000000000 --- a/doc/users/shell.rst +++ /dev/null @@ -1,160 +0,0 @@ -.. _mpl-shell: - -********************************** -Using matplotlib in a python shell -********************************** - -.. warning:: - - This page is significantly out of date - -By default, matplotlib defers drawing until the end of the script -because drawing can be an expensive operation, and you may not want -to update the plot every time a single property is changed, only once -after all the properties have changed. - -But when working from the python shell, you usually do want to update -the plot with every command, e.g., after changing the -:func:`~matplotlib.pyplot.xlabel`, or the marker style of a line. -While this is simple in concept, in practice it can be tricky, because -matplotlib is a graphical user interface application under the hood, -and there are some tricks to make the applications work right in a -python shell. - - -.. _ipython-pylab: - -IPython to the rescue -===================== - -.. note:: - - The mode described here still exists for historical reasons, but it is - highly advised not to use. It pollutes namespaces with functions that will - shadow python built-in and can lead to hard to track bugs. To get IPython - integration without imports the use of the `%matplotlib` magic is - preferred. See - `ipython documentation `_ - . - -Fortunately, `ipython `_, an enhanced -interactive python shell, has figured out all of these tricks, and is -matplotlib aware, so when you start ipython in the *pylab* mode. - -.. sourcecode:: ipython - - johnh@flag:~> ipython - Python 2.4.5 (#4, Apr 12 2008, 09:09:16) - IPython 0.9.0 -- An enhanced Interactive Python. - - In [1]: %pylab - - Welcome to pylab, a matplotlib-based Python environment. - For more information, type 'help(pylab)'. - - In [2]: x = randn(10000) - - In [3]: hist(x, 100) - -it sets everything up for you so interactive plotting works as you -would expect it to. Call :func:`~matplotlib.pyplot.figure` and a -figure window pops up, call :func:`~matplotlib.pyplot.plot` and your -data appears in the figure window. - -Note in the example above that we did not import any matplotlib names -because in pylab mode, ipython will import them automatically. -ipython also turns on *interactive* mode for you, which causes every -pyplot command to trigger a figure update, and also provides a -matplotlib aware ``run`` command to run matplotlib scripts -efficiently. ipython will turn off interactive mode during a ``run`` -command, and then restore the interactive state at the end of the -run so you can continue tweaking the figure manually. - -There has been a lot of recent work to embed ipython, with pylab -support, into various GUI applications, so check on the ipython -mailing `list -`_ for the -latest status. - -.. _other-shells: - -Other python interpreters -========================= - -If you can't use ipython, and still want to use matplotlib/pylab from -an interactive python shell, e.g., the plain-ole standard python -interactive interpreter, you -are going to need to understand what a matplotlib backend is -:ref:`what-is-a-backend`. - - - -With the TkAgg backend, which uses the Tkinter user interface toolkit, -you can use matplotlib from an arbitrary non-gui python shell. Just set your -``backend : TkAgg`` and ``interactive : True`` in your -:file:`matplotlibrc` file (see :ref:`customizing-matplotlib`) and fire -up python. Then:: - - >>> from pylab import * - >>> plot([1,2,3]) - >>> xlabel('hi mom') - -should work out of the box. This is also likely to work with recent -versions of the qt4agg and gtkagg backends, and with the macosx backend -on the Macintosh. Note, in batch mode, -i.e. when making -figures from scripts, interactive mode can be slow since it redraws -the figure with each command. So you may want to think carefully -before making this the default behavior via the :file:`matplotlibrc` -file instead of using the functions listed in the next section. - -Gui shells are at best problematic, because they have to run a -mainloop, but interactive plotting also involves a mainloop. Ipython -has sorted all this out for the primary matplotlib backends. There -may be other shells and IDEs that also work with matplotlib in interactive -mode, but one obvious candidate does not: -the python IDLE IDE is a Tkinter gui app that does -not support pylab interactive mode, regardless of backend. - -.. _controlling-interactive: - -Controlling interactive updating -================================ - -The *interactive* property of the pyplot interface controls whether a -figure canvas is drawn on every pyplot command. If *interactive* is -*False*, then the figure state is updated on every plot command, but -will only be drawn on explicit calls to -:func:`~matplotlib.pyplot.draw`. When *interactive* is -*True*, then every pyplot command triggers a draw. - - -The pyplot interface provides 4 commands that are useful for -interactive control. - -:func:`~matplotlib.pyplot.isinteractive` - returns the interactive setting *True|False* - -:func:`~matplotlib.pyplot.ion` - turns interactive mode on - -:func:`~matplotlib.pyplot.ioff` - turns interactive mode off - -:func:`~matplotlib.pyplot.draw` - forces a figure redraw - -When working with a big figure in which drawing is expensive, you may -want to turn matplotlib's interactive setting off temporarily to avoid -the performance hit:: - - - >>> #create big-expensive-figure - >>> ioff() # turn updates off - >>> title('now how much would you pay?') - >>> xticklabels(fontsize=20, color='green') - >>> draw() # force a draw - >>> savefig('alldone', dpi=300) - >>> close() - >>> ion() # turn updating back on - >>> plot(rand(20), mfc='g', mec='r', ms=40, mew=4, ls='--', lw=3) diff --git a/doc/users/text_intro.rst b/doc/users/text_intro.rst deleted file mode 100644 index 824cc8d2af8f..000000000000 --- a/doc/users/text_intro.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. _text-intro: - -Text introduction -================= - -matplotlib has extensive text support, including support for -mathematical expressions, truetype support for raster and -vector outputs, newline separated text with arbitrary -rotations, and unicode support. Because it embeds -fonts directly in output documents, e.g., for postscript -or PDF, what you see on the screen is what you get in the hardcopy. -`FreeType `_ support -produces very nice, antialiased fonts, that look good even at small -raster sizes. matplotlib includes its own -:mod:`matplotlib.font_manager` (thanks to Paul Barrett), which -implements a cross platform, `W3C ` -compliant font finding algorithm. - -The user has a great deal of control over text properties (font size, font -weight, text location and color, etc.) with sensible defaults set in -the `rc file `. -And significantly, for those interested in mathematical -or scientific figures, matplotlib implements a large number of TeX -math symbols and commands, supporting :ref:`mathematical expressions -` anywhere in your figure. - - -Basic text commands -=================== - -The following commands are used to create text in the pyplot -interface - -* :func:`~matplotlib.pyplot.text` - add text at an arbitrary location to the ``Axes``; - :meth:`matplotlib.axes.Axes.text` in the API. - -* :func:`~matplotlib.pyplot.xlabel` - add a label to the x-axis; - :meth:`matplotlib.axes.Axes.set_xlabel` in the API. - -* :func:`~matplotlib.pyplot.ylabel` - add a label to the y-axis; - :meth:`matplotlib.axes.Axes.set_ylabel` in the API. - -* :func:`~matplotlib.pyplot.title` - add a title to the ``Axes``; - :meth:`matplotlib.axes.Axes.set_title` in the API. - -* :func:`~matplotlib.pyplot.figtext` - add text at an arbitrary location to the ``Figure``; - :meth:`matplotlib.figure.Figure.text` in the API. - -* :func:`~matplotlib.pyplot.suptitle` - add a title to the ``Figure``; - :meth:`matplotlib.figure.Figure.suptitle` in the API. - -* :func:`~matplotlib.pyplot.annotate` - add an annotation, with - optional arrow, to the ``Axes`` ; :meth:`matplotlib.axes.Axes.annotate` - in the API. - -All of these functions create and return a -:func:`matplotlib.text.Text` instance, which can be configured with a -variety of font and other properties. The example below shows all of -these commands in action. - -.. plot:: pyplots/text_commands.py - :include-source: diff --git a/doc/users/text_props.rst b/doc/users/text_props.rst deleted file mode 100644 index a418ecc29356..000000000000 --- a/doc/users/text_props.rst +++ /dev/null @@ -1,159 +0,0 @@ -.. _text-properties: - -============================ - Text properties and layout -============================ - -The :class:`matplotlib.text.Text` instances have a variety of -properties which can be configured via keyword arguments to the text -commands (e.g., :func:`~matplotlib.pyplot.title`, -:func:`~matplotlib.pyplot.xlabel` and :func:`~matplotlib.pyplot.text`). - -========================== ====================================================================================================================== -Property Value Type -========================== ====================================================================================================================== -alpha `float` -backgroundcolor any matplotlib :ref:`color ` -bbox `~matplotlib.patches.Rectangle` prop dict plus key ``'pad'`` which is a pad in points -clip_box a matplotlib.transform.Bbox instance -clip_on [True | False] -clip_path a `~matplotlib.path.Path` instance and a `~matplotlib.transforms.Transform` instance, a `~matplotlib.patches.Patch` -color any matplotlib :ref:`color ` -family [ ``'serif'`` | ``'sans-serif'`` | ``'cursive'`` | ``'fantasy'`` | ``'monospace'`` ] -fontproperties a `~matplotlib.font_manager.FontProperties` instance -horizontalalignment or ha [ ``'center'`` | ``'right'`` | ``'left'`` ] -label any string -linespacing `float` -multialignment [``'left'`` | ``'right'`` | ``'center'`` ] -name or fontname string e.g., [``'Sans'`` | ``'Courier'`` | ``'Helvetica'`` ...] -picker [None|float|boolean|callable] -position (x, y) -rotation [ angle in degrees | ``'vertical'`` | ``'horizontal'`` ] -size or fontsize [ size in points | relative size, e.g., ``'smaller'``, ``'x-large'`` ] -style or fontstyle [ ``'normal'`` | ``'italic'`` | ``'oblique'`` ] -text string or anything printable with '%s' conversion -transform a `~matplotlib.transforms.Transform` instance -variant [ ``'normal'`` | ``'small-caps'`` ] -verticalalignment or va [ ``'center'`` | ``'top'`` | ``'bottom'`` | ``'baseline'`` ] -visible [True | False] -weight or fontweight [ ``'normal'`` | ``'bold'`` | ``'heavy'`` | ``'light'`` | ``'ultrabold'`` | ``'ultralight'``] -x `float` -y `float` -zorder any number -========================== ====================================================================================================================== - - -You can lay out text with the alignment arguments -``horizontalalignment``, ``verticalalignment``, and -``multialignment``. ``horizontalalignment`` controls whether the x -positional argument for the text indicates the left, center or right -side of the text bounding box. ``verticalalignment`` controls whether -the y positional argument for the text indicates the bottom, center or -top side of the text bounding box. ``multialignment``, for newline -separated strings only, controls whether the different lines are left, -center or right justified. Here is an example which uses the -:func:`~matplotlib.pyplot.text` command to show the various alignment -possibilities. The use of ``transform=ax.transAxes`` throughout the -code indicates that the coordinates are given relative to the axes -bounding box, with 0,0 being the lower left of the axes and 1,1 the -upper right. - -.. plot:: pyplots/text_layout.py - :include-source: - - -============== - Default Font -============== - -The base default font is controlled by a set of rcParams: - -+---------------------+----------------------------------------------------+ -| rcParam | usage | -+=====================+====================================================+ -| ``'font.family'`` | List of either names of font or ``{'cursive', | -| | 'fantasy', 'monospace', 'sans', 'sans serif', | -| | 'sans-serif', 'serif'}``. | -| | | -+---------------------+----------------------------------------------------+ -| ``'font.style'`` | The default style, ex ``'normal'``, | -| | ``'italic'``. | -| | | -+---------------------+----------------------------------------------------+ -| ``'font.variant'`` | Default variant, ex ``'normal'``, ``'small-caps'`` | -| | (untested) | -+---------------------+----------------------------------------------------+ -| ``'font.stretch'`` | Default stretch, ex ``'normal'``, ``'condensed'`` | -| | (incomplete) | -| | | -+---------------------+----------------------------------------------------+ -| ``'font.weight'`` | Default weight. Either string or integer | -| | | -| | | -+---------------------+----------------------------------------------------+ -| ``'font.size'`` | Default font size in points. Relative font sizes | -| | (``'large'``, ``'x-small'``) are computed against | -| | this size. | -+---------------------+----------------------------------------------------+ - -The mapping between the family aliases (``{'cursive', 'fantasy', -'monospace', 'sans', 'sans serif', 'sans-serif', 'serif'}``) and actual font names -is controlled by the following rcParams: - - -+------------------------------------------+--------------------------------+ -| family alias | rcParam with mappings | -+==========================================+================================+ -| ``'serif'`` | ``'font.serif'`` | -+------------------------------------------+--------------------------------+ -| ``'monospace'`` | ``'font.monospace'`` | -+------------------------------------------+--------------------------------+ -| ``'fantasy'`` | ``'font.fantasy'`` | -+------------------------------------------+--------------------------------+ -| ``'cursive'`` | ``'font.cursive'`` | -+------------------------------------------+--------------------------------+ -| ``{'sans', 'sans serif', 'sans-serif'}`` | ``'font.sans-serif'`` | -+------------------------------------------+--------------------------------+ - - -which are lists of font names. - -Text with non-latin glyphs -========================== - -As of v2.0 the :ref:`default font ` contains -glyphs for many western alphabets, but still does not cover all of the -glyphs that may be required by mpl users. For example, DejaVu has no -coverage of Chinese, Korean, or Japanese. - - -To set the default font to be one that supports the code points you -need, prepend the font name to ``'font.family'`` or the desired alias -lists :: - - matplotlib.rcParams['font.sans-serif'] = ['Source Han Sans TW', 'sans-serif'] - -or set it in your :file:`.matplotlibrc` file:: - - font.sans-serif: Source Han Sans TW, Ariel, sans-serif - -To control the font used on per-artist basis use the ``'name'``, -``'fontname'`` or ``'fontproperties'`` kwargs documented :ref:`above -`. - - -On linux, `fc-list `__ can be a -useful tool to discover the font name; for example :: - - $ fc-list :lang=zh family - Source Han Sans TW,思源黑體 TW,思源黑體 TW ExtraLight,Source Han Sans TW ExtraLight - Source Han Sans TW,思源黑體 TW,思源黑體 TW Regular,Source Han Sans TW Regular - Droid Sans Fallback - Source Han Sans TW,思源黑體 TW,思源黑體 TW Bold,Source Han Sans TW Bold - Source Han Sans TW,思源黑體 TW,思源黑體 TW Medium,Source Han Sans TW Medium - Source Han Sans TW,思源黑體 TW,思源黑體 TW Normal,Source Han Sans TW Normal - Fixed - Source Han Sans TW,思源黑體 TW,思源黑體 TW Heavy,Source Han Sans TW Heavy - Source Han Sans TW,思源黑體 TW,思源黑體 TW Light,Source Han Sans TW Light - -lists all of the fonts that support Chinese. diff --git a/doc/users/tight_layout_guide.rst b/doc/users/tight_layout_guide.rst deleted file mode 100644 index 4a28c3d2b9bc..000000000000 --- a/doc/users/tight_layout_guide.rst +++ /dev/null @@ -1,324 +0,0 @@ -.. _plotting-guide-tight-layout: - -****************** -Tight Layout guide -****************** - -*tight_layout* automatically adjusts subplot params so that the -subplot(s) fits in to the figure area. This is an experimental -feature and may not work for some cases. It only checks the extents -of ticklabels, axis labels, and titles. - - -Simple Example -============== - -In matplotlib, the location of axes (including subplots) are specified in -normalized figure coordinates. It can happen that your axis labels or -titles (or sometimes even ticklabels) go outside the figure area, and are thus -clipped. - -.. plot:: - :include-source: - :context: - - plt.rcParams['savefig.facecolor'] = "0.8" - - def example_plot(ax, fontsize=12): - ax.plot([1, 2]) - ax.locator_params(nbins=3) - ax.set_xlabel('x-label', fontsize=fontsize) - ax.set_ylabel('y-label', fontsize=fontsize) - ax.set_title('Title', fontsize=fontsize) - - plt.close('all') - fig, ax = plt.subplots() - example_plot(ax, fontsize=24) - -To prevent this, the location of axes needs to be adjusted. For -subplots, this can be done by adjusting the subplot params -(:ref:`howto-subplots-adjust`). Matplotlib v1.1 introduces a new -command :func:`~matplotlib.pyplot.tight_layout` that does this -automatically for you. - -.. plot:: - :include-source: - :context: - - plt.tight_layout() - -When you have multiple subplots, often you see labels of different -axes overlapping each other. - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2) - example_plot(ax1) - example_plot(ax2) - example_plot(ax3) - example_plot(ax4) - - -:func:`~matplotlib.pyplot.tight_layout` will also adjust spacing between -subplots to minimize the overlaps. - -.. plot:: - :include-source: - :context: - - plt.tight_layout() - -:func:`~matplotlib.pyplot.tight_layout` can take keyword arguments of -*pad*, *w_pad* and *h_pad*. These control the extra padding around the -figure border and between subplots. The pads are specified in fraction -of fontsize. - -.. plot:: - :include-source: - :context: - - plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) - -:func:`~matplotlib.pyplot.tight_layout` will work even if the sizes of -subplots are different as far as their grid specification is -compatible. In the example below, *ax1* and *ax2* are subplots of a 2x2 -grid, while *ax3* is of a 1x2 grid. - - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure() - - ax1 = plt.subplot(221) - ax2 = plt.subplot(223) - ax3 = plt.subplot(122) - - example_plot(ax1) - example_plot(ax2) - example_plot(ax3) - - plt.tight_layout() - - -It works with subplots created with -:func:`~matplotlib.pyplot.subplot2grid`. In general, subplots created -from the gridspec (:ref:`gridspec-guide`) will work. - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure() - - ax1 = plt.subplot2grid((3, 3), (0, 0)) - ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2) - ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2) - ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2) - - example_plot(ax1) - example_plot(ax2) - example_plot(ax3) - example_plot(ax4) - - plt.tight_layout() - - -Although not thoroughly tested, it seems to work for subplots with -aspect != "auto" (e.g., axes with images). - - -.. plot:: - :include-source: - :context: - - arr = np.arange(100).reshape((10,10)) - - plt.close('all') - fig = plt.figure(figsize=(5,4)) - - ax = plt.subplot(111) - im = ax.imshow(arr, interpolation="none") - - plt.tight_layout() - - -Caveats -------- - - * :func:`~matplotlib.pyplot.tight_layout` only considers ticklabels, axis - labels, and titles. Thus, other artists may be clipped and also may - overlap. - - * It assumes that the extra space needed for ticklabels, axis labels, - and titles is independent of original location of axes. This is - often true, but there are rare cases where it is not. - - * pad=0 clips some of the texts by a few pixels. This may be a bug or - a limitation of the current algorithm and it is not clear why it - happens. Meanwhile, use of pad at least larger than 0.3 is - recommended. - - - - -Use with GridSpec ------------------ - -GridSpec has its own :func:`~matplotlib.gridspec.GridSpec.tight_layout` method -(the pyplot api :func:`~matplotlib.pyplot.tight_layout` also works). - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure() - - import matplotlib.gridspec as gridspec - - gs1 = gridspec.GridSpec(2, 1) - ax1 = fig.add_subplot(gs1[0]) - ax2 = fig.add_subplot(gs1[1]) - - example_plot(ax1) - example_plot(ax2) - - gs1.tight_layout(fig) - - -You may provide an optional *rect* parameter, which specifies the bounding box -that the subplots will be fit inside. The coordinates must be in normalized -figure coordinates and the default is (0, 0, 1, 1). - -.. plot:: - :include-source: - :context: - - gs1.tight_layout(fig, rect=[0, 0, 0.5, 1]) - - -For example, this can be used for a figure with multiple gridspecs. - -.. plot:: - :include-source: - :context: - - gs2 = gridspec.GridSpec(3, 1) - - for ss in gs2: - ax = fig.add_subplot(ss) - example_plot(ax) - ax.set_title("") - ax.set_xlabel("") - - ax.set_xlabel("x-label", fontsize=12) - - gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.5) - - -We may try to match the top and bottom of two grids :: - - top = min(gs1.top, gs2.top) - bottom = max(gs1.bottom, gs2.bottom) - - gs1.update(top=top, bottom=bottom) - gs2.update(top=top, bottom=bottom) - - -While this should be mostly good enough, adjusting top and bottom -may require adjustment of hspace also. To update hspace & vspace, we -call :func:`~matplotlib.gridspec.GridSpec.tight_layout` again with updated -rect argument. Note that the rect argument specifies the area including the -ticklabels, etc. Thus, we will increase the bottom (which is 0 for the normal -case) by the difference between the *bottom* from above and the bottom of each -gridspec. Same thing for the top. - -.. plot:: - :include-source: - :context: - - top = min(gs1.top, gs2.top) - bottom = max(gs1.bottom, gs2.bottom) - - gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom), - 0.5, 1 - (gs1.top-top)]) - gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom), - None, 1 - (gs2.top-top)], - h_pad=0.5) - - - -Use with AxesGrid1 ------------------- - -While limited, the axes_grid1 toolkit is also supported. - - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure() - - from mpl_toolkits.axes_grid1 import Grid - grid = Grid(fig, rect=111, nrows_ncols=(2,2), - axes_pad=0.25, label_mode='L', - ) - - for ax in grid: - example_plot(ax) - ax.title.set_visible(False) - - plt.tight_layout() - - - -Colorbar --------- - -If you create a colorbar with the :func:`~matplotlib.pyplot.colorbar` -command, the created colorbar is an instance of Axes, *not* Subplot, so -tight_layout does not work. With Matplotlib v1.1, you may create a -colorbar as a subplot using the gridspec. - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure(figsize=(4, 4)) - im = plt.imshow(arr, interpolation="none") - - plt.colorbar(im, use_gridspec=True) - - plt.tight_layout() - -Another option is to use AxesGrid1 toolkit to -explicitly create an axes for colorbar. - -.. plot:: - :include-source: - :context: - - plt.close('all') - fig = plt.figure(figsize=(4, 4)) - im = plt.imshow(arr, interpolation="none") - - from mpl_toolkits.axes_grid1 import make_axes_locatable - divider = make_axes_locatable(plt.gca()) - cax = divider.append_axes("right", "5%", pad="3%") - plt.colorbar(im, cax=cax) - - plt.tight_layout() - - - - diff --git a/doc/users/transforms_tutorial.rst b/doc/users/transforms_tutorial.rst deleted file mode 100644 index 46c509dc6ad3..000000000000 --- a/doc/users/transforms_tutorial.rst +++ /dev/null @@ -1,456 +0,0 @@ -.. _transforms_tutorial: - -************************** -Transformations Tutorial -************************** - -Like any graphics packages, matplotlib is built on top of a -transformation framework to easily move between coordinate systems, -the userland `data` coordinate system, the `axes` coordinate system, -the `figure` coordinate system, and the `display` coordinate system. -In 95% of your plotting, you won't need to think about this, as it -happens under the hood, but as you push the limits of custom figure -generation, it helps to have an understanding of these objects so you -can reuse the existing transformations matplotlib makes available to -you, or create your own (see :mod:`matplotlib.transforms`). The table -below summarizes the existing coordinate systems, the transformation -object you should use to work in that coordinate system, and the -description of that system. In the `Transformation Object` column, -``ax`` is a :class:`~matplotlib.axes.Axes` instance, and ``fig`` is a -:class:`~matplotlib.figure.Figure` instance. - -========== ===================== ==================================================================================== -Coordinate Transformation Object Description -========== ===================== ==================================================================================== -`data` ``ax.transData`` The userland data coordinate system, controlled by the xlim and ylim -`axes` ``ax.transAxes`` The coordinate system of the :class:`~matplotlib.axes.Axes`; (0,0) is - bottom left of the axes, and (1,1) is top right of the axes. -`figure` ``fig.transFigure`` The coordinate system of the :class:`~matplotlib.figure.Figure`; (0,0) - is bottom left of the figure, and (1,1) is top right of the figure. -`display` `None` This is the pixel coordinate system of the display; (0,0) is the bottom - left of the display, and (width, height) is the top right of the display in pixels. - Alternatively, the identity transform - (:class:`matplotlib.transforms.IdentityTransform()`) may be used instead of None. -========== ===================== ==================================================================================== - - -All of the transformation objects in the table above take inputs in -their coordinate system, and transform the input to the `display` -coordinate system. That is why the `display` coordinate system has -`None` for the `Transformation Object` column -- it already is in -display coordinates. The transformations also know how to invert -themselves, to go from `display` back to the native coordinate system. -This is particularly useful when processing events from the user -interface, which typically occur in display space, and you want to -know where the mouse click or key-press occurred in your data -coordinate system. - -.. _data-coords: - -Data coordinates -================ - -Let's start with the most commonly used coordinate, the `data` -coordinate system. Whenever you add data to the axes, matplotlib -updates the datalimits, most commonly updated with the -:meth:`~matplotlib.axes.Axes.set_xlim` and -:meth:`~matplotlib.axes.Axes.set_ylim` methods. For example, in the -figure below, the data limits stretch from 0 to 10 on the x-axis, and --1 to 1 on the y-axis. - -.. plot:: - :include-source: - - import numpy as np - import matplotlib.pyplot as plt - - x = np.arange(0, 10, 0.005) - y = np.exp(-x/2.) * np.sin(2*np.pi*x) - - fig = plt.figure() - ax = fig.add_subplot(111) - ax.plot(x, y) - ax.set_xlim(0, 10) - ax.set_ylim(-1, 1) - - plt.show() - -You can use the ``ax.transData`` instance to transform from your -`data` to your `display` coordinate system, either a single point or a -sequence of points as shown below: - -.. sourcecode:: ipython - - In [14]: type(ax.transData) - Out[14]: - - In [15]: ax.transData.transform((5, 0)) - Out[15]: array([ 335.175, 247. ]) - - In [16]: ax.transData.transform([(5, 0), (1,2)]) - Out[16]: - array([[ 335.175, 247. ], - [ 132.435, 642.2 ]]) - -You can use the :meth:`~matplotlib.transforms.Transform.inverted` -method to create a transform which will take you from display to data -coordinates: - -.. sourcecode:: ipython - - In [41]: inv = ax.transData.inverted() - - In [42]: type(inv) - Out[42]: - - In [43]: inv.transform((335.175, 247.)) - Out[43]: array([ 5., 0.]) - -If your are typing along with this tutorial, the exact values of the -display coordinates may differ if you have a different window size or -dpi setting. Likewise, in the figure below, the display labeled -points are probably not the same as in the ipython session because the -documentation figure size defaults are different. - -.. plot:: pyplots/annotate_transform.py - - -.. note:: - - If you run the source code in the example above in a GUI backend, - you may also find that the two arrows for the `data` and `display` - annotations do not point to exactly the same point. This is because - the display point was computed before the figure was displayed, and - the GUI backend may slightly resize the figure when it is created. - The effect is more pronounced if you resize the figure yourself. - This is one good reason why you rarely want to work in display - space, but you can connect to the ``'on_draw'`` - :class:`~matplotlib.backend_bases.Event` to update figure - coordinates on figure draws; see :ref:`event-handling-tutorial`. - -When you change the x or y limits of your axes, the data limits are -updated so the transformation yields a new display point. Note that -when we just change the ylim, only the y-display coordinate is -altered, and when we change the xlim too, both are altered. More on -this later when we talk about the -:class:`~matplotlib.transforms.Bbox`. - -.. sourcecode:: ipython - - In [54]: ax.transData.transform((5, 0)) - Out[54]: array([ 335.175, 247. ]) - - In [55]: ax.set_ylim(-1,2) - Out[55]: (-1, 2) - - In [56]: ax.transData.transform((5, 0)) - Out[56]: array([ 335.175 , 181.13333333]) - - In [57]: ax.set_xlim(10,20) - Out[57]: (10, 20) - - In [58]: ax.transData.transform((5, 0)) - Out[58]: array([-171.675 , 181.13333333]) - - - -.. _axes-coords: - -Axes coordinates -================ - -After the `data` coordinate system, `axes` is probably the second most -useful coordinate system. Here the point (0,0) is the bottom left of -your axes or subplot, (0.5, 0.5) is the center, and (1.0, 1.0) is the -top right. You can also refer to points outside the range, so (-0.1, -1.1) is to the left and above your axes. This coordinate system is -extremely useful when placing text in your axes, because you often -want a text bubble in a fixed, location, e.g., the upper left of the axes -pane, and have that location remain fixed when you pan or zoom. Here -is a simple example that creates four panels and labels them 'A', 'B', -'C', 'D' as you often see in journals. - -.. plot:: - :include-source: - - import numpy as np - import matplotlib.pyplot as plt - - fig = plt.figure() - for i, label in enumerate(('A', 'B', 'C', 'D')): - ax = fig.add_subplot(2,2,i+1) - ax.text(0.05, 0.95, label, transform=ax.transAxes, - fontsize=16, fontweight='bold', va='top') - - plt.show() - -You can also make lines or patches in the axes coordinate system, but -this is less useful in my experience than using ``ax.transAxes`` for -placing text. Nonetheless, here is a silly example which plots some -random dots in `data` space, and overlays a semi-transparent -:class:`~matplotlib.patches.Circle` centered in the middle of the axes -with a radius one quarter of the axes -- if your axes does not -preserve aspect ratio (see :meth:`~matplotlib.axes.Axes.set_aspect`), -this will look like an ellipse. Use the pan/zoom tool to move around, -or manually change the data xlim and ylim, and you will see the data -move, but the circle will remain fixed because it is not in `data` -coordinates and will always remain at the center of the axes. - -.. plot:: - :include-source: - - import numpy as np - import matplotlib.pyplot as plt - import matplotlib.patches as patches - fig = plt.figure() - ax = fig.add_subplot(111) - x, y = 10*np.random.rand(2, 1000) - ax.plot(x, y, 'go') # plot some data in data coordinates - - circ = patches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes, - facecolor='yellow', alpha=0.5) - ax.add_patch(circ) - - plt.show() - -.. blended_transformations: - -Blended transformations -======================= - -Drawing in `blended` coordinate spaces which mix `axes` with `data` -coordinates is extremely useful, for example to create a horizontal -span which highlights some region of the y-data but spans across the -x-axis regardless of the data limits, pan or zoom level, etc. In fact -these blended lines and spans are so useful, we have built in -functions to make them easy to plot (see -:meth:`~matplotlib.axes.Axes.axhline`, -:meth:`~matplotlib.axes.Axes.axvline`, -:meth:`~matplotlib.axes.Axes.axhspan`, -:meth:`~matplotlib.axes.Axes.axvspan`) but for didactic purposes we -will implement the horizontal span here using a blended -transformation. This trick only works for separable transformations, -like you see in normal Cartesian coordinate systems, but not on -inseparable transformations like the -:class:`~matplotlib.projections.polar.PolarAxes.PolarTransform`. - -.. plot:: - :include-source: - - import numpy as np - import matplotlib.pyplot as plt - import matplotlib.patches as patches - import matplotlib.transforms as transforms - - fig = plt.figure() - ax = fig.add_subplot(111) - - x = np.random.randn(1000) - - ax.hist(x, 30) - ax.set_title(r'$\sigma=1 \/ \dots \/ \sigma=2$', fontsize=16) - - # the x coords of this transformation are data, and the - # y coord are axes - trans = transforms.blended_transform_factory( - ax.transData, ax.transAxes) - - # highlight the 1..2 stddev region with a span. - # We want x to be in data coordinates and y to - # span from 0..1 in axes coords - rect = patches.Rectangle((1,0), width=1, height=1, - transform=trans, color='yellow', - alpha=0.5) - - ax.add_patch(rect) - - plt.show() - -.. note:: - - The blended transformations where x is in data coords and y in axes - coordinates is so useful that we have helper methods to return the - versions mpl uses internally for drawing ticks, ticklabels, etc. - The methods are :meth:`matplotlib.axes.Axes.get_xaxis_transform` and - :meth:`matplotlib.axes.Axes.get_yaxis_transform`. So in the example - above, the call to - :meth:`~matplotlib.transforms.blended_transform_factory` can be - replaced by ``get_xaxis_transform``:: - - trans = ax.get_xaxis_transform() - -.. offset-transforms-shadow: - -Using offset transforms to create a shadow effect -================================================= - -One use of transformations is to create a new transformation that is -offset from another transformation, e.g., to place one object shifted a -bit relative to another object. Typically you want the shift to be in -some physical dimension, like points or inches rather than in data -coordinates, so that the shift effect is constant at different zoom -levels and dpi settings. - -One use for an offset is to create a shadow effect, where you draw one -object identical to the first just to the right of it, and just below -it, adjusting the zorder to make sure the shadow is drawn first and -then the object it is shadowing above it. The transforms module has a -helper transformation -:class:`~matplotlib.transforms.ScaledTranslation`. It is -instantiated with:: - - trans = ScaledTranslation(xt, yt, scale_trans) - -where `xt` and `yt` are the translation offsets, and `scale_trans` is -a transformation which scales `xt` and `yt` at transformation time -before applying the offsets. A typical use case is to use the figure -``fig.dpi_scale_trans`` transformation for the `scale_trans` argument, -to first scale `xt` and `yt` specified in points to `display` space -before doing the final offset. The dpi and inches offset is a -common-enough use case that we have a special helper function to -create it in :func:`matplotlib.transforms.offset_copy`, which returns -a new transform with an added offset. But in the example below, we'll -create the offset transform ourselves. Note the use of the plus -operator in:: - - offset = transforms.ScaledTranslation(dx, dy, - fig.dpi_scale_trans) - shadow_transform = ax.transData + offset - -showing that can chain transformations using the addition operator. -This code says: first apply the data transformation ``ax.transData`` -and then translate the data by `dx` and `dy` points. In typography, -a`point `_ is -1/72 inches, and by specifying your offsets in points, your figure -will look the same regardless of the dpi resolution it is saved in. - -.. plot:: - :include-source: - - import numpy as np - import matplotlib.pyplot as plt - import matplotlib.patches as patches - import matplotlib.transforms as transforms - - fig = plt.figure() - ax = fig.add_subplot(111) - - # make a simple sine wave - x = np.arange(0., 2., 0.01) - y = np.sin(2*np.pi*x) - line, = ax.plot(x, y, lw=3, color='blue') - - # shift the object over 2 points, and down 2 points - dx, dy = 2/72., -2/72. - offset = transforms.ScaledTranslation(dx, dy, - fig.dpi_scale_trans) - shadow_transform = ax.transData + offset - - # now plot the same data with our offset transform; - # use the zorder to make sure we are below the line - ax.plot(x, y, lw=3, color='gray', - transform=shadow_transform, - zorder=0.5*line.get_zorder()) - - ax.set_title('creating a shadow effect with an offset transform') - plt.show() - - -.. transformation-pipeline: - -The transformation pipeline -=========================== - -The ``ax.transData`` transform we have been working with in this -tutorial is a composite of three different transformations that -comprise the transformation pipeline from `data` -> `display` -coordinates. Michael Droettboom implemented the transformations -framework, taking care to provide a clean API that segregated the -nonlinear projections and scales that happen in polar and logarithmic -plots, from the linear affine transformations that happen when you pan -and zoom. There is an efficiency here, because you can pan and zoom -in your axes which affects the affine transformation, but you may not -need to compute the potentially expensive nonlinear scales or -projections on simple navigation events. It is also possible to -multiply affine transformation matrices together, and then apply them -to coordinates in one step. This is not true of all possible -transformations. - - -Here is how the ``ax.transData`` instance is defined in the basic -separable axis :class:`~matplotlib.axes.Axes` class:: - - self.transData = self.transScale + (self.transLimits + self.transAxes) - -We've been introduced to the ``transAxes`` instance above in -:ref:`axes-coords`, which maps the (0,0), (1,1) corners of the -axes or subplot bounding box to `display` space, so let's look at -these other two pieces. - -``self.transLimits`` is the transformation that takes you from -``data`` to ``axes`` coordinates; i.e., it maps your view xlim and ylim -to the unit space of the axes (and ``transAxes`` then takes that unit -space to display space). We can see this in action here - -.. sourcecode:: ipython - - In [80]: ax = subplot(111) - - In [81]: ax.set_xlim(0, 10) - Out[81]: (0, 10) - - In [82]: ax.set_ylim(-1,1) - Out[82]: (-1, 1) - - In [84]: ax.transLimits.transform((0,-1)) - Out[84]: array([ 0., 0.]) - - In [85]: ax.transLimits.transform((10,-1)) - Out[85]: array([ 1., 0.]) - - In [86]: ax.transLimits.transform((10,1)) - Out[86]: array([ 1., 1.]) - - In [87]: ax.transLimits.transform((5,0)) - Out[87]: array([ 0.5, 0.5]) - -and we can use this same inverted transformation to go from the unit -`axes` coordinates back to `data` coordinates. - -.. sourcecode:: ipython - - In [90]: inv.transform((0.25, 0.25)) - Out[90]: array([ 2.5, -0.5]) - -The final piece is the ``self.transScale`` attribute, which is -responsible for the optional non-linear scaling of the data, e.g., for -logarithmic axes. When an Axes is initially setup, this is just set to -the identity transform, since the basic matplotlib axes has linear -scale, but when you call a logarithmic scaling function like -:meth:`~matplotlib.axes.Axes.semilogx` or explicitly set the scale to -logarithmic with :meth:`~matplotlib.axes.Axes.set_xscale`, then the -``ax.transScale`` attribute is set to handle the nonlinear projection. -The scales transforms are properties of the respective ``xaxis`` and -``yaxis`` :class:`~matplotlib.axis.Axis` instances. For example, when -you call ``ax.set_xscale('log')``, the xaxis updates its scale to a -:class:`matplotlib.scale.LogScale` instance. - -For non-separable axes the PolarAxes, there is one more piece to -consider, the projection transformation. The ``transData`` -:class:`matplotlib.projections.polar.PolarAxes` is similar to that for -the typical separable matplotlib Axes, with one additional piece -``transProjection``:: - - self.transData = self.transScale + self.transProjection + \ - (self.transProjectionAffine + self.transAxes) - -``transProjection`` handles the projection from the space, -e.g., latitude and longitude for map data, or radius and theta for polar -data, to a separable Cartesian coordinate system. There are several -projection examples in the ``matplotlib.projections`` package, and the -best way to learn more is to open the source for those packages and -see how to make your own, since matplotlib supports extensible axes -and projections. Michael Droettboom has provided a nice tutorial -example of creating a hammer projection axes; see -:ref:`api-custom_projection_example`. - diff --git a/doc/users/tutorials.rst b/doc/users/tutorials.rst deleted file mode 100644 index 0675f0a4f5a3..000000000000 --- a/doc/users/tutorials.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _tutorials-index: - -=========== - Tutorials -=========== - -.. htmlonly:: - - :Release: |version| - :Date: |today| - -.. _tutorials-index-intro: - -Introductory ------------- - -.. toctree:: - :maxdepth: 2 - - pyplot_tutorial.rst - image_tutorial.rst - gridspec.rst - tight_layout_guide.rst - -.. _tutorials-index-inter: - -Intermediate ------------- -.. toctree:: - :maxdepth: 2 - - artists.rst - legend_guide.rst - -.. _tutorials-index-adv: - -Advanced --------- - -.. toctree:: - :maxdepth: 2 - - transforms_tutorial.rst - path_tutorial.rst - patheffects_guide.rst diff --git a/doc/users/usetex.rst b/doc/users/usetex.rst deleted file mode 100644 index 52a82f1a1b3e..000000000000 --- a/doc/users/usetex.rst +++ /dev/null @@ -1,158 +0,0 @@ -.. _usetex-tutorial: - -************************* -Text rendering With LaTeX -************************* - -Matplotlib has the option to use LaTeX to manage all text layout. This -option is available with the following backends: - -* Agg -* PS -* PDF - -The LaTeX option is activated by setting ``text.usetex : True`` in -your rc settings. Text handling with matplotlib's LaTeX support is -slower than matplotlib's very capable :ref:`mathtext -`, but is more flexible, since different LaTeX -packages (font packages, math packages, etc.) can be used. The -results can be striking, especially when you take care to use the same -fonts in your figures as in the main document. - -Matplotlib's LaTeX support requires a working LaTeX_ installation, dvipng_ -(which may be included with your LaTeX installation), and Ghostscript_ -(GPL Ghostscript 8.60 or later is recommended). The executables for these -external dependencies must all be located on your :envvar:`PATH`. - -There are a couple of options to mention, which can be changed using :ref:`rc -settings `. Here is an example matplotlibrc file:: - - font.family : serif - font.serif : Times, Palatino, New Century Schoolbook, Bookman, Computer Modern Roman - font.sans-serif : Helvetica, Avant Garde, Computer Modern Sans serif - font.cursive : Zapf Chancery - font.monospace : Courier, Computer Modern Typewriter - - text.usetex : true - -The first valid font in each family is the one that will be loaded. If the -fonts are not specified, the Computer Modern fonts are used by default. All of -the other fonts are Adobe fonts. Times and Palatino each have their own -accompanying math fonts, while the other Adobe serif fonts make use of the -Computer Modern math fonts. See the PSNFSS_ documentation for more details. - -To use LaTeX and select Helvetica as the default font, without editing -matplotlibrc use:: - - from matplotlib import rc - rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']}) - ## for Palatino and other serif fonts use: - #rc('font',**{'family':'serif','serif':['Palatino']}) - rc('text', usetex=True) - -Here is the standard example, `tex_demo.py`: - -.. plot:: pyplots/tex_demo.py - :include-source: - -Note that display math mode (``$$ e=mc^2 $$``) is not supported, but adding the -command ``\displaystyle``, as in `tex_demo.py`, will produce the same -results. - -.. note:: - Certain characters require special escaping in TeX, such as:: - - # $ % & ~ _ ^ \ { } \( \) \[ \] - - Therefore, these characters will behave differently depending on - the rcParam ``text.usetex`` flag. - -.. _usetex-unicode: - -usetex with unicode -=================== -It is also possible to use unicode strings with the LaTeX text manager, here is -an example taken from `tex_unicode_demo.py`: - -.. plot:: mpl_examples/pylab_examples/tex_unicode_demo.py - :include-source: - -.. _usetex-postscript: - -Postscript options -================== - -In order to produce encapsulated postscript files that can be embedded in a new -LaTeX document, the default behavior of matplotlib is to distill the output, -which removes some postscript operators used by LaTeX that are illegal in an -eps file. This step produces results which may be unacceptable to some users, -because the text is coarsely rasterized and converted to bitmaps, which are not -scalable like standard postscript, and the text is not searchable. One -workaround is to to set ``ps.distiller.res`` to a higher value (perhaps 6000) -in your rc settings, which will produce larger files but may look better and -scale reasonably. A better workaround, which requires Poppler_ or Xpdf_, can be -activated by changing the ``ps.usedistiller`` rc setting to ``xpdf``. This -alternative produces postscript without rasterizing text, so it scales -properly, can be edited in Adobe Illustrator, and searched text in pdf -documents. - -.. _usetex-hangups: - -Possible hangups -================ - -* On Windows, the :envvar:`PATH` environment variable may need to be modified - to include the directories containing the latex, dvipng and ghostscript - executables. See :ref:`environment-variables` and - :ref:`setting-windows-environment-variables` for details. - -* Using MiKTeX with Computer Modern fonts, if you get odd \*Agg and PNG - results, go to MiKTeX/Options and update your format files - -* The fonts look terrible on screen. You are probably running Mac OS, and there - is some funny business with older versions of dvipng on the mac. Set - ``text.dvipnghack : True`` in your matplotlibrc file. - -* On Ubuntu and Gentoo, the base texlive install does not ship with - the type1cm package. You may need to install some of the extra - packages to get all the goodies that come bundled with other latex - distributions. - -* Some progress has been made so matplotlib uses the dvi files - directly for text layout. This allows latex to be used for text - layout with the pdf and svg backends, as well as the \*Agg and PS - backends. In the future, a latex installation may be the only - external dependency. - -.. _usetex-troubleshooting: - -Troubleshooting -=============== - -* Try deleting your :file:`.matplotlib/tex.cache` directory. If you don't know - where to find :file:`.matplotlib`, see :ref:`locating-matplotlib-config-dir`. - -* Make sure LaTeX, dvipng and ghostscript are each working and on your - :envvar:`PATH`. - -* Make sure what you are trying to do is possible in a LaTeX document, - that your LaTeX syntax is valid and that you are using raw strings - if necessary to avoid unintended escape sequences. - -* Most problems reported on the mailing list have been cleared up by - upgrading Ghostscript_. If possible, please try upgrading to the - latest release before reporting problems to the list. - -* The ``text.latex.preamble`` rc setting is not officially supported. This - option provides lots of flexibility, and lots of ways to cause - problems. Please disable this option before reporting problems to - the mailing list. - -* If you still need help, please see :ref:`reporting-problems` - -.. _LaTeX: http://www.tug.org -.. _dvipng: http://www.nongnu.org/dvipng/ -.. _Ghostscript: http://www.cs.wisc.edu/~ghost/ -.. _PSNFSS: http://www.ctan.org/tex-archive/macros/latex/required/psnfss/psnfss2e.pdf -.. _Poppler: http://poppler.freedesktop.org/ -.. _Xpdf: http://www.foolabs.com/xpdf diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst deleted file mode 100644 index 226f3ee1f572..000000000000 --- a/doc/users/whats_new.rst +++ /dev/null @@ -1,240 +0,0 @@ -.. _whats-new: - -========================== - What's new in matplotlib -========================== - -For a list of all of the issues and pull requests since the last -revision, see the :ref:`github-stats`. - -.. contents:: Table of Contents - :depth: 3 - - - -New in matplotlib 2.0 -===================== - -.. note:: - - matplotlib 2.0 supports Python 2.7, and 3.4+ - - - -Default style changes ---------------------- - -The major changes in v2.0 are related to overhauling the default styles. - -.. toctree:: - :maxdepth: 2 - - dflt_style_changes - - -Improved color conversion API and RGBA support ----------------------------------------------- - -The :mod:`~matplotlib.colors` gained a new color conversion API with -full support for the alpha channel. The main public functions are -:func:`~matplotlib.colors.is_color_like`, :func:`matplotlib.colors.to_rgba`, -:func:`matplotlib.colors.to_rgba_array` and :func:`~matplotlib.colors.to_hex`. -RGBA quadruplets are encoded in hex format as `#rrggbbaa`. - -A side benefit is that the Qt options editor now allows setting the alpha -channel of the artists as well. - - -New Configuration (rcParams) ----------------------------- - -New rcparams added - -+---------------------------------+--------------------------------------------------+ -| Parameter | Description | -+=================================+==================================================+ -|`date.autoformatter.year` | foramt string for 'year' scale dates | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.month` | format string for 'month' scale dates | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.day` | format string for 'day' scale dates | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.hour` | format string for 'hour' scale times | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.minute` | format string for 'minute' scale times | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.second` | format string for 'second' scale times | -+---------------------------------+--------------------------------------------------+ -|`date.autoformatter.microsecond` | format string for 'microsecond' scale times | -+---------------------------------+--------------------------------------------------+ -|`scatter.marker` | default marker for scatter plot | -+---------------------------------+--------------------------------------------------+ -|`svg.hashsalt` | see note | -+---------------------------------+--------------------------------------------------+ -|`xtick.top`, `xtick.minor.top`, | Control where major and minor ticks are drawn. | -|`xtick.major.top` | The global values are `and` ed with the | -|`xtick.bottom`, | corosponding major/minor values. | -|`xtick.minor.bottom`, | | -|`xtick.major.bottom` | | -|`ytick.left`, `ytick.minor.left`,| | -|`ytick.major.left` | | -|`ytick.right`, | | -|`ytick.minor.right`, | | -|`ytick.major.right` | | -+---------------------------------+--------------------------------------------------+ -|`hist.bins` | the default number of bins to use in | -| | `~matplotlib.axes.Axes.hist`. This can be an | -| | `int`, a list of floats, or ``'auto'`` if numpy | -| | >= 1.11 is installed. | -+---------------------------------+--------------------------------------------------+ -|`lines.scale_dashes` | If the line dash patterns should scale with | -| | linewidth | -+---------------------------------+--------------------------------------------------+ - - - -Added ``svg.hashsalt`` key to rcParams -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If ``svg.hashsalt`` is ``None`` (which it is by default), the svg -backend uses ``uuid4`` to generate the hash salt. If it is not -``None``, it must be a string that is used as the hash salt instead of -``uuid4``. This allows for deterministic SVG output. - - -Removed the ``svg.image_noscale`` rcParam -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As a result of the extensive changes to image handling, the -``svg.image_noscale`` rcParam has been removed. The same -functionality may be achieved by setting ``interpolation='none'`` on -individual images or globally using the ``image.interpolation`` -rcParam. - - -Qualitative colormaps ---------------------- - -ColorBrewer's "qualitative" colormaps ("Accent", "Dark2", "Paired", -"Pastel1", "Pastel2", "Set1", "Set2", "Set3") were intended for discrete -categorical data, with no implication of value, and therefore have been -converted to ``ListedColormap`` instead of ``LinearSegmentedColormap``, so -the colors will no longer be interpolated and they can be used for -choropleths, labeled image features, etc. - - - -Axis offset label now responds to `labelcolor` ----------------------------------------------- - -Axis offset labels are now colored the same as axis tick markers when `labelcolor` is altered. - -Improved offset text choice ---------------------------- -The default offset-text choice was changed to only use significant digits that -are common to all ticks (e.g. 1231..1239 -> 1230, instead of 1231), except when -they straddle a relatively large multiple of a power of ten, in which case that -multiple is chosen (e.g. 1999..2001->2000). - - -Style parameter blacklist -------------------------- - -In order to prevent unexpected consequences from using a style, style -files are no longer able to set parameters that affect things -unrelated to style. These parameters include:: - - 'interactive', 'backend', 'backend.qt4', 'webagg.port', - 'webagg.port_retries', 'webagg.open_in_browser', 'backend_fallback', - 'toolbar', 'timezone', 'datapath', 'figure.max_open_warning', - 'savefig.directory', 'tk.window_focus', 'hardcopy.docstring' - - -Change in default font ----------------------- - -The default font used by matplotlib in text has been changed to DejaVu Sans and -DejaVu Serif for the sans-serif and serif families, respectively. The DejaVu -font family is based on the previous matplotlib default --Bitstream Vera-- but -includes a much wider range of characters. - -The default mathtext font has been changed from Computer Modern to the DejaVu -family to maintain consistency with regular text. Two new options for the -``mathtext.fontset`` configuration parameter have been added: ``dejavusans`` -(default) and ``dejavuserif``. Both of these options use DejaVu glyphs whenever -possible and fall back to STIX symbols when a glyph is not found in DejaVu. To -return to the previous behavior, set the rcParam ``mathtext.fontset`` to ``cm``. - - -Faster text rendering ---------------------- - -Rendering text in the Agg backend is now less fuzzy and about 20% -faster to draw. - - -Improvements for the Qt figure options editor ---------------------------------------------- - -Various usability improvements were implemented for the Qt figure options -editor, among which: - -- Line style entries are now sorted without duplicates. -- The colormap and normalization limits can now be set for images. -- Line edits for floating values now display only as many digits as necessary - to avoid precision loss. An important bug was also fixed regarding input - validation using Qt5 and a locale where the decimal separator is ",". -- The axes selector now uses shorter, more user-friendly names for axes, and - does not crash if there are no axes. -- Line and image entries using the default labels ("_lineX", "_imageX") are now - sorted numerically even when there are more than 10 entries. - - -Improved image support ----------------------- - -Prior to version 2.0, matplotlib resampled images by first applying -the color map and then resizing the result. Since the resampling was -performed on the colored image, this introduced colors in the output -image that didn't actually exist in the color map. Now, images are -resampled first (and entirely in floating-point, if the input image is -floating-point), and then the color map is applied. - -In order to make this important change, the image handling code was -almost entirely rewritten. As a side effect, image resampling uses -less memory and fewer datatype conversions than before. - -The experimental private feature where one could "skew" an image by -setting the private member ``_image_skew_coordinate`` has been -removed. Instead, images will obey the transform of the axes on which -they are drawn. - - -Support for HiDPI (Retina) displays in the NbAgg and WebAgg backends --------------------------------------------------------------------- - -The NbAgg and WebAgg backends will now use the full resolution of your -high-pixel-density display. - -Change in the default animation codec -------------------------------------- - -The default animation codec has been changed from ``mpeg4`` to ``h264``, -which is more efficient. It can be set via the ``animation.codec`` rcParam. - -Deprecated support for mencoder in animation --------------------------------------------- - -The use of mencoder for writing video files with mpl is problematic; -switching to ffmpeg is strongly advised. All support for mencoder -will be removed in version 2.2. - - -Previous Whats New -================== - -.. toctree:: - :glob: - :maxdepth: 1 - - prev_whats_new/whats_new_* diff --git a/doc/users/whats_new/2015-10-31_TransformedPatchPath.rst b/doc/users/whats_new/2015-10-31_TransformedPatchPath.rst deleted file mode 100644 index 32a0458c822d..000000000000 --- a/doc/users/whats_new/2015-10-31_TransformedPatchPath.rst +++ /dev/null @@ -1,13 +0,0 @@ -New TransformedPatchPath caching object ---------------------------------------- - -A newly added :class:`~matplotlib.transforms.TransformedPatchPath` provides a -means to transform a :class:`~matplotlib.patches.Patch` into a -:class:`~matplotlib.path.Path` via a :class:`~matplotlib.transforms.Transform` -while caching the resulting path. If neither the patch nor the transform have -changed, a cached copy of the path is returned. - -This class differs from the older -:class:`~matplotlib.transforms.TransformedPath` in that it is able to refresh -itself based on the underlying patch while the older class uses an immutable -path. diff --git a/doc/users/whats_new/CheckButtons_widget_get_status.rst b/doc/users/whats_new/CheckButtons_widget_get_status.rst deleted file mode 100644 index 84a2baa68a02..000000000000 --- a/doc/users/whats_new/CheckButtons_widget_get_status.rst +++ /dev/null @@ -1,4 +0,0 @@ -CheckButtons widget get_status function ---------------------------------------- - -A :func:`get_status` function has been added the the :class:`matplotlib.widgets.CheckButtons` class. This :func:`get_status` function allows user to query the status (True/False) of all of the buttons in the CheckButtons object. \ No newline at end of file diff --git a/doc/users/whats_new/README.rst b/doc/users/whats_new/README.rst deleted file mode 100644 index dd8e4a8a5214..000000000000 --- a/doc/users/whats_new/README.rst +++ /dev/null @@ -1,16 +0,0 @@ -This folder is for placing new portions of `whats_new.rst`. - -When adding an entry please look at the currently existing files to -see if you can extend any of them. If you create a file, name it -something like :file:`cool_new_feature.rst` if you have added a brand new -feature or something like :file:`updated_feature.rst` for extensions of -existing features. Include contents of the form: :: - - Section Title for Feature - ------------------------- - - A bunch of text about how awesome the new feature is and examples of how - to use it. - - A sub-section - ````````````` diff --git a/doc/users/whats_new/abstract_movie_writer.rst b/doc/users/whats_new/abstract_movie_writer.rst deleted file mode 100644 index 44dc7bd5f182..000000000000 --- a/doc/users/whats_new/abstract_movie_writer.rst +++ /dev/null @@ -1,8 +0,0 @@ -Abstract base class for movie writers -------------------------------------- - -The new :class:`~matplotlib.animation.AbstractMovieWriter` class defines -the API required by a class that is to be used as the `writer` in the -`save` method of the :class:`~matplotlib.animation.Animation` class. -The existing :class:`~matplotlib.animation.MovieWriter` class now derives -from the new abstract base class. diff --git a/doc/users/whats_new/annotation-default-arrow.rst b/doc/users/whats_new/annotation-default-arrow.rst deleted file mode 100644 index e885b5b7f71a..000000000000 --- a/doc/users/whats_new/annotation-default-arrow.rst +++ /dev/null @@ -1,5 +0,0 @@ -Annotation can use a default arrow style ----------------------------------------- - -Annotations now use the default arrow style when setting `arrowprops={}`, -rather than no arrow (the new behavior actually matches the documentation). diff --git a/doc/users/whats_new/boxplot_zorder_kwarg.rst b/doc/users/whats_new/boxplot_zorder_kwarg.rst deleted file mode 100644 index bbb44aa59ee8..000000000000 --- a/doc/users/whats_new/boxplot_zorder_kwarg.rst +++ /dev/null @@ -1,11 +0,0 @@ -Boxplot Zorder Keyword Argument -------------------------------- - -The ``zorder`` parameter now exists for :func:`boxplot`. This allows the zorder -of a boxplot to be set in the plotting function call. - -Example -``````` -:: - - boxplot(np.arange(10), zorder=10) diff --git a/doc/users/whats_new/filled_plus_and_x_markers.rst b/doc/users/whats_new/filled_plus_and_x_markers.rst deleted file mode 100644 index f73dcec3b575..000000000000 --- a/doc/users/whats_new/filled_plus_and_x_markers.rst +++ /dev/null @@ -1,7 +0,0 @@ -Filled ``+`` and ``x`` markers ------------------------------- - -New fillable *plus* and *x* markers have been added. See -the :mod:`~matplotlib.markers` module and -:ref:`marker reference ` -examples. diff --git a/doc/users/whats_new/multiple_legend_keys.rst b/doc/users/whats_new/multiple_legend_keys.rst deleted file mode 100644 index 9be34e4b36a7..000000000000 --- a/doc/users/whats_new/multiple_legend_keys.rst +++ /dev/null @@ -1,10 +0,0 @@ -Multiple legend keys for legend entries ---------------------------------------- - -A legend entry can now contain more than one legend key. The extended -``HandlerTuple`` class now accepts two parameters: ``ndivide`` divides the -legend area in the specified number of sections; ``pad`` changes the padding -between the legend keys. - -.. plot:: mpl_examples/pylab_examples/legend_demo6.py - diff --git a/doc/users/whats_new/percent_formatter.rst b/doc/users/whats_new/percent_formatter.rst deleted file mode 100644 index 5948d588ca90..000000000000 --- a/doc/users/whats_new/percent_formatter.rst +++ /dev/null @@ -1,6 +0,0 @@ -Added `matplotlib.ticker.PercentFormatter` ------------------------------------------- - -The new formatter has some nice features like being able to convert from -arbitrary data scales to percents, a customizable percent symbol and -either automatic or manual control over the decimal points. diff --git a/doc/users/whats_new/reversed_colormap.rst b/doc/users/whats_new/reversed_colormap.rst deleted file mode 100644 index fb42757a7e5c..000000000000 --- a/doc/users/whats_new/reversed_colormap.rst +++ /dev/null @@ -1,7 +0,0 @@ -Colormap reversed method ------------------------- - -The methods :meth:`~matplotlib.colors.LinearSegmentedColormap.reversed` and -:meth:`~matplotlib.colors.ListedColormap.reversed` return a reversed -instance of the Colormap. This implements a way for any Colormap to be -reversed. \ No newline at end of file diff --git a/doc/users/whats_new/setp_output.rst b/doc/users/whats_new/setp_output.rst deleted file mode 100644 index cd4af662e6d3..000000000000 --- a/doc/users/whats_new/setp_output.rst +++ /dev/null @@ -1,7 +0,0 @@ -`Artist.setp` (and `pyplot.setp`) accept a `file` argument ----------------------------------------------------------- - -The argument is keyword-only. It allows an output file other than -`sys.stdout` to be specified. It works exactly like the `file` argument -to `print`. - diff --git a/doc/users/whats_new/streamplot_zorder.rst b/doc/users/whats_new/streamplot_zorder.rst deleted file mode 100644 index ac8e15f3aee8..000000000000 --- a/doc/users/whats_new/streamplot_zorder.rst +++ /dev/null @@ -1,7 +0,0 @@ -Streamplot Zorder Keyword Argument Changes ------------------------------------------- - -The ``zorder`` parameter for :func:`streamplot` now has default -value of ``None`` instead of ``2``. If ``None`` is given as ``zorder``, -:func:`streamplot` has a default ``zorder`` of -``matplotlib.lines.Line2D.zorder``. diff --git a/doc/users/whats_new/tick_params_rotation.rst b/doc/users/whats_new/tick_params_rotation.rst deleted file mode 100644 index 1c90b4475896..000000000000 --- a/doc/users/whats_new/tick_params_rotation.rst +++ /dev/null @@ -1,10 +0,0 @@ -`Axis.set_tick_params` now responds to 'rotation' -------------------------------------------------- - -Bulk setting of tick label rotation is now possible via :func:`set_tick_params` using the `rotation` keyword. - -Example -``````` -:: - - ax.xaxis.set_tick_params(which='both', rotation=90) \ No newline at end of file diff --git a/doc/users/whats_new/update_subplot2grid.rst b/doc/users/whats_new/update_subplot2grid.rst deleted file mode 100644 index b5075e2f3f52..000000000000 --- a/doc/users/whats_new/update_subplot2grid.rst +++ /dev/null @@ -1,13 +0,0 @@ -New Figure Parameter for subplot2grid --------------------------------------- - -A ``fig`` parameter now exists for the method :func:`subplot2grid`. This allows -a user to specify the figure where the subplots will be created. If ``fig`` -is ``None`` (default) then the method will use the current figure retrieved by -:func:`gcf`. - -Example -``````` -:: - - subplot2grid(shape, loc, rowspan=1, colspan=1, fig=myfig) diff --git a/doc/utils/pylab_names.py b/doc/utils/pylab_names.py deleted file mode 100644 index 379c6baabca8..000000000000 --- a/doc/utils/pylab_names.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import print_function -""" -autogenerate some tables for pylab namespace -""" -from pylab import * -d = locals() -keys = d.keys() -keys.sort() - -modd = dict() -for k in keys: - o = d[k] - if not callable(o): - continue - doc = getattr(o, '__doc__', None) - if doc is not None: - doc = ' - '.join([line for line in doc.split('\n') if line.strip()][:2]) - - mod = getattr(o, '__module__', None) - if mod is None: - mod = 'unknown' - - if mod is not None: - if mod.startswith('matplotlib'): - if k[0].isupper(): - k = ':class:`~%s.%s`'%(mod, k) - else: - k = ':func:`~%s.%s`'%(mod, k) - mod = ':mod:`%s`'%mod - elif mod.startswith('numpy'): - #k = '`%s <%s>`_'%(k, 'http://scipy.org/Numpy_Example_List_With_Doc#%s'%k) - k = '`%s <%s>`_'%(k, 'http://sd-2116.dedibox.fr/pydocweb/doc/%s.%s'%(mod, k)) - - - if doc is None: doc = 'TODO' - - mod, k, doc = mod.strip(), k.strip(), doc.strip()[:80] - modd.setdefault(mod, []).append((k, doc)) - -mods = modd.keys() -mods.sort() -for mod in mods: - border = '*'*len(mod) - print(mod) - print(border) - - print() - funcs, docs = zip(*modd[mod]) - maxfunc = max([len(f) for f in funcs]) - maxdoc = max(40, max([len(d) for d in docs]) ) - border = ' '.join(['='*maxfunc, '='*maxdoc]) - print(border) - print(' '.join(['symbol'.ljust(maxfunc), 'description'.ljust(maxdoc)])) - print(border) - for func, doc in modd[mod]: - row = ' '.join([func.ljust(maxfunc), doc.ljust(maxfunc)]) - print(row) - - print(border) - print() - #break diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000000..418062d0d237 --- /dev/null +++ b/environment.yml @@ -0,0 +1,74 @@ +# To set up a development environment using conda run: +# +# conda env create -f environment.yml +# conda activate mpl-dev +# pip install --verbose --no-build-isolation --editable ".[dev]" +# +--- +name: mpl-dev +channels: + - conda-forge +dependencies: + # runtime dependencies + - cairocffi + - c-compiler + - cxx-compiler + - contourpy>=1.0.1 + - cycler>=0.10.0 + - fonttools>=4.22.0 + - importlib-resources>=3.2.0 + - kiwisolver>=1.3.1 + - pybind11>=2.13.2 + - meson-python>=0.13.1 + - numpy>=1.25 + - pillow>=9 + - pkg-config + - pygobject + - pyparsing>=3 + - pyqt + - python>=3.11 + - python-dateutil>=2.1 + - setuptools_scm + - wxpython + # building documentation + - colorspacious + - graphviz + - ipython + - ipywidgets + - numpydoc>=1.0 + - packaging>=20 + - pydata-sphinx-theme=0.16.1 # required by mpl-sphinx-theme=3.10 + - pyyaml + - sphinx>=3.0.0,!=6.1.2 + - sphinx-copybutton + - sphinx-gallery>=0.12.0 + - joblib # needed for sphinx-gallery[parallel] + - sphinx-design + - sphinx-tags>=0.4.0 + - pystemmer + - pikepdf + - pip + - pip: + - mpl-sphinx-theme~=3.10.0 + - sphinxcontrib-svg2pdfconverter>=1.1.0 + - sphinxcontrib-video>=0.2.1 + # testing + - black<26 + - coverage + - gtk4 + - ipykernel + - nbconvert[execute]!=6.0.0,!=6.0.1,!=7.3.0,!=7.3.1 + - nbformat!=5.0.0,!=5.0.1 + - pandas!=0.25.0 + - psutil + - pre-commit + - pydocstyle>=5.1.0 + - pytest!=4.6.0,!=5.4.0,!=8.1.0 + - pytest-cov + - pytest-rerunfailures + - pytest-timeout + - pytest-xdist + - tornado + - pytz + - ruff + - tox diff --git a/examples/README.txt b/examples/README.txt deleted file mode 100644 index c696f1955ba3..000000000000 --- a/examples/README.txt +++ /dev/null @@ -1,45 +0,0 @@ -matplotlib examples -=================== - -There are a variety of ways to use matplotlib, and most of them are -illustrated in the examples in this directory. - -Probably the most common way people use matplotlib is with the -procedural interface, which follows the matlab/IDL/mathematica -approach of using simple procedures like "plot" or "title" to modify -the current figure. These examples are included in the "pylab_examples" -directory. If you want to write more robust scripts, e.g., for -production use or in a web application server, you will probably want -to use the matplotlib API for full control. These examples are found -in the "api" directory. Below is a brief description of the different -directories found here: - - * animation - dynamic plots, see the tutorial at - http://www.scipy.org/Cookbook/Matplotlib/Animations - - * api - working with the matplotlib API directory. - - * axes_grid1 - Examples related to the axes_grid1 toolkit - - * axisartist - Examples related to the axisartist toolkit - - * event_handling - how to interact with your figure, mouse presses, - key presses, object picking, etc. - - * misc - some miscellaneous examples. some demos for loading and - working with record arrays - - * mplot3d - 3D examples - - * pylab_examples - the interface to matplotlib similar to matlab - - * tests - tests used by matplotlib developers to check functionality - (These tests are still sometimes useful, but mostly developers should - use the nosetests which perform automatic image comparison.) - - * units - working with unit data an custom types in matplotlib - - * user_interfaces - using matplotlib in a GUI application, e.g., - TkInter, WXPython, pygtk, pyqt widgets - - * widgets - Examples using interactive widgets diff --git a/examples/animation/animate_decay.py b/examples/animation/animate_decay.py deleted file mode 100644 index 271dc6a33392..000000000000 --- a/examples/animation/animate_decay.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -===== -Decay -===== - -This example showcases a sinusoidal decay animation. -""" - - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - - -def data_gen(t=0): - cnt = 0 - while cnt < 1000: - cnt += 1 - t += 0.1 - yield t, np.sin(2*np.pi*t) * np.exp(-t/10.) - - -def init(): - ax.set_ylim(-1.1, 1.1) - ax.set_xlim(0, 10) - del xdata[:] - del ydata[:] - line.set_data(xdata, ydata) - return line, - -fig, ax = plt.subplots() -line, = ax.plot([], [], lw=2) -ax.grid() -xdata, ydata = [], [] - - -def run(data): - # update the data - t, y = data - xdata.append(t) - ydata.append(y) - xmin, xmax = ax.get_xlim() - - if t >= xmax: - ax.set_xlim(xmin, 2*xmax) - ax.figure.canvas.draw() - line.set_data(xdata, ydata) - - return line, - -ani = animation.FuncAnimation(fig, run, data_gen, blit=False, interval=10, - repeat=False, init_func=init) -plt.show() diff --git a/examples/animation/basic_example.py b/examples/animation/basic_example.py deleted file mode 100644 index 4a9c0f6e624d..000000000000 --- a/examples/animation/basic_example.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -========================= -Simple animation examples -========================= - -This example contains two animations. The first is a random walk plot. The -second is an image animation. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - - -def update_line(num, data, line): - line.set_data(data[..., :num]) - return line, - -fig1 = plt.figure() - -data = np.random.rand(2, 25) -l, = plt.plot([], [], 'r-') -plt.xlim(0, 1) -plt.ylim(0, 1) -plt.xlabel('x') -plt.title('test') -line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), - interval=50, blit=True) - -# To save the animation, use the command: line_ani.save('lines.mp4') - -fig2 = plt.figure() - -x = np.arange(-9, 10) -y = np.arange(-9, 10).reshape(-1, 1) -base = np.hypot(x, y) -ims = [] -for add in np.arange(15): - ims.append((plt.pcolor(x, y, base + add, norm=plt.Normalize(0, 30)),)) - -im_ani = animation.ArtistAnimation(fig2, ims, interval=50, repeat_delay=3000, - blit=True) -# To save this second animation with some metadata, use the following command: -# im_ani.save('im.mp4', metadata={'artist':'Guido'}) - -plt.show() diff --git a/examples/animation/basic_example_writer.py b/examples/animation/basic_example_writer.py deleted file mode 100644 index 31146900f1ca..000000000000 --- a/examples/animation/basic_example_writer.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -=================== -Saving an animation -=================== - -This example showcases the same animations as `basic_example.py`, but instead -of displaying the animation to the user, it writes to files using a -MovieWriter instance. -""" - -# -*- noplot -*- -import numpy as np -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt -import matplotlib.animation as animation - - -def update_line(num, data, line): - line.set_data(data[..., :num]) - return line, - -# Set up formatting for the movie files -Writer = animation.writers['ffmpeg'] -writer = Writer(fps=15, metadata=dict(artist='Me'), bitrate=1800) - - -fig1 = plt.figure() - -data = np.random.rand(2, 25) -l, = plt.plot([], [], 'r-') -plt.xlim(0, 1) -plt.ylim(0, 1) -plt.xlabel('x') -plt.title('test') -line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), - interval=50, blit=True) -line_ani.save('lines.mp4', writer=writer) - -fig2 = plt.figure() - -x = np.arange(-9, 10) -y = np.arange(-9, 10).reshape(-1, 1) -base = np.hypot(x, y) -ims = [] -for add in np.arange(15): - ims.append((plt.pcolor(x, y, base + add, norm=plt.Normalize(0, 30)),)) - -im_ani = animation.ArtistAnimation(fig2, ims, interval=50, repeat_delay=3000, - blit=True) -im_ani.save('im.mp4', writer=writer) diff --git a/examples/animation/bayes_update.py b/examples/animation/bayes_update.py deleted file mode 100644 index a29420d977f6..000000000000 --- a/examples/animation/bayes_update.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -================ -The Bayes update -================ - -This animation displays the posterior estimate updates as it is refitted when -new data arrives. -The vertical line represents the theoretical value to which the plotted -distribution should converge. -""" - -# update a distribution based on new data. -import numpy as np -import matplotlib.pyplot as plt -import scipy.stats as ss -from matplotlib.animation import FuncAnimation - - -class UpdateDist(object): - def __init__(self, ax, prob=0.5): - self.success = 0 - self.prob = prob - self.line, = ax.plot([], [], 'k-') - self.x = np.linspace(0, 1, 200) - self.ax = ax - - # Set up plot parameters - self.ax.set_xlim(0, 1) - self.ax.set_ylim(0, 15) - self.ax.grid(True) - - # This vertical line represents the theoretical value, to - # which the plotted distribution should converge. - self.ax.axvline(prob, linestyle='--', color='black') - - def init(self): - self.success = 0 - self.line.set_data([], []) - return self.line, - - def __call__(self, i): - # This way the plot can continuously run and we just keep - # watching new realizations of the process - if i == 0: - return self.init() - - # Choose success based on exceed a threshold with a uniform pick - if np.random.rand(1,) < self.prob: - self.success += 1 - y = ss.beta.pdf(self.x, self.success + 1, (i - self.success) + 1) - self.line.set_data(self.x, y) - return self.line, - -fig, ax = plt.subplots() -ud = UpdateDist(ax, prob=0.7) -anim = FuncAnimation(fig, ud, frames=np.arange(100), init_func=ud.init, - interval=100, blit=True) -plt.show() diff --git a/examples/animation/double_pendulum_animated.py b/examples/animation/double_pendulum_animated.py deleted file mode 100644 index 56af089bbb6f..000000000000 --- a/examples/animation/double_pendulum_animated.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -=========================== -The double pendulum problem -=========================== - -This animation illustrates the double pendulum problem. -""" - -# Double pendulum formula translated from the C code at -# http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c - -from numpy import sin, cos -import numpy as np -import matplotlib.pyplot as plt -import scipy.integrate as integrate -import matplotlib.animation as animation - -G = 9.8 # acceleration due to gravity, in m/s^2 -L1 = 1.0 # length of pendulum 1 in m -L2 = 1.0 # length of pendulum 2 in m -M1 = 1.0 # mass of pendulum 1 in kg -M2 = 1.0 # mass of pendulum 2 in kg - - -def derivs(state, t): - - dydx = np.zeros_like(state) - dydx[0] = state[1] - - del_ = state[2] - state[0] - den1 = (M1 + M2)*L1 - M2*L1*cos(del_)*cos(del_) - dydx[1] = (M2*L1*state[1]*state[1]*sin(del_)*cos(del_) + - M2*G*sin(state[2])*cos(del_) + - M2*L2*state[3]*state[3]*sin(del_) - - (M1 + M2)*G*sin(state[0]))/den1 - - dydx[2] = state[3] - - den2 = (L2/L1)*den1 - dydx[3] = (-M2*L2*state[3]*state[3]*sin(del_)*cos(del_) + - (M1 + M2)*G*sin(state[0])*cos(del_) - - (M1 + M2)*L1*state[1]*state[1]*sin(del_) - - (M1 + M2)*G*sin(state[2]))/den2 - - return dydx - -# create a time array from 0..100 sampled at 0.05 second steps -dt = 0.05 -t = np.arange(0.0, 20, dt) - -# th1 and th2 are the initial angles (degrees) -# w10 and w20 are the initial angular velocities (degrees per second) -th1 = 120.0 -w1 = 0.0 -th2 = -10.0 -w2 = 0.0 - -# initial state -state = np.radians([th1, w1, th2, w2]) - -# integrate your ODE using scipy.integrate. -y = integrate.odeint(derivs, state, t) - -x1 = L1*sin(y[:, 0]) -y1 = -L1*cos(y[:, 0]) - -x2 = L2*sin(y[:, 2]) + x1 -y2 = -L2*cos(y[:, 2]) + y1 - -fig = plt.figure() -ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2)) -ax.set_aspect('equal') -ax.grid() - -line, = ax.plot([], [], 'o-', lw=2) -time_template = 'time = %.1fs' -time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes) - - -def init(): - line.set_data([], []) - time_text.set_text('') - return line, time_text - - -def animate(i): - thisx = [0, x1[i], x2[i]] - thisy = [0, y1[i], y2[i]] - - line.set_data(thisx, thisy) - time_text.set_text(time_template % (i*dt)) - return line, time_text - -ani = animation.FuncAnimation(fig, animate, np.arange(1, len(y)), - interval=25, blit=True, init_func=init) - -# ani.save('double_pendulum.mp4', fps=15) -plt.show() diff --git a/examples/animation/dynamic_image.py b/examples/animation/dynamic_image.py deleted file mode 100644 index cea3327209dc..000000000000 --- a/examples/animation/dynamic_image.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -================= -An animated image -================= - -This example demonstrates how to animate an image. -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - -fig = plt.figure() - - -def f(x, y): - return np.sin(x) + np.cos(y) - -x = np.linspace(0, 2 * np.pi, 120) -y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) - -im = plt.imshow(f(x, y), animated=True) - - -def updatefig(*args): - global x, y - x += np.pi / 15. - y += np.pi / 20. - im.set_array(f(x, y)) - return im, - -ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True) -plt.show() diff --git a/examples/animation/dynamic_image2.py b/examples/animation/dynamic_image2.py deleted file mode 100644 index 7fd635b65699..000000000000 --- a/examples/animation/dynamic_image2.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -======================================== -An animated image using a list of images -======================================== - -This examples demonstrates how to animate an image from a list of images (or -Artists). -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - -fig = plt.figure() - - -def f(x, y): - return np.sin(x) + np.cos(y) - -x = np.linspace(0, 2 * np.pi, 120) -y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) -# ims is a list of lists, each row is a list of artists to draw in the -# current frame; here we are just animating one artist, the image, in -# each frame -ims = [] -for i in range(60): - x += np.pi / 15. - y += np.pi / 20. - im = plt.imshow(f(x, y), animated=True) - ims.append([im]) - -ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, - repeat_delay=1000) - -# ani.save('dynamic_images.mp4') - -plt.show() diff --git a/examples/animation/histogram.py b/examples/animation/histogram.py deleted file mode 100644 index 810a9756d1cc..000000000000 --- a/examples/animation/histogram.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -================== -Animated histogram -================== - -This example shows how to use a path patch to draw a bunch of -rectangles for an animated histogram. - -""" -import numpy as np - -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import matplotlib.path as path -import matplotlib.animation as animation - -fig, ax = plt.subplots() - -# histogram our data with numpy -data = np.random.randn(1000) -n, bins = np.histogram(data, 100) - -# get the corners of the rectangles for the histogram -left = np.array(bins[:-1]) -right = np.array(bins[1:]) -bottom = np.zeros(len(left)) -top = bottom + n -nrects = len(left) - -# here comes the tricky part -- we have to set up the vertex and path -# codes arrays using moveto, lineto and closepoly - -# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the -# CLOSEPOLY; the vert for the closepoly is ignored but we still need -# it to keep the codes aligned with the vertices -nverts = nrects*(1 + 3 + 1) -verts = np.zeros((nverts, 2)) -codes = np.ones(nverts, int) * path.Path.LINETO -codes[0::5] = path.Path.MOVETO -codes[4::5] = path.Path.CLOSEPOLY -verts[0::5, 0] = left -verts[0::5, 1] = bottom -verts[1::5, 0] = left -verts[1::5, 1] = top -verts[2::5, 0] = right -verts[2::5, 1] = top -verts[3::5, 0] = right -verts[3::5, 1] = bottom - -barpath = path.Path(verts, codes) -patch = patches.PathPatch( - barpath, facecolor='green', edgecolor='yellow', alpha=0.5) -ax.add_patch(patch) - -ax.set_xlim(left[0], right[-1]) -ax.set_ylim(bottom.min(), top.max()) - - -def animate(i): - # simulate new data coming in - data = np.random.randn(1000) - n, bins = np.histogram(data, 100) - top = bottom + n - verts[1::5, 1] = top - verts[2::5, 1] = top - return [patch, ] - -ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True) -plt.show() diff --git a/examples/animation/moviewriter.py b/examples/animation/moviewriter.py deleted file mode 100644 index afe200ad5746..000000000000 --- a/examples/animation/moviewriter.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -=========== -MovieWriter -=========== - -This example uses a MovieWriter directly to grab individual frames and write -them to a file. This avoids any event loop integration, but has the advantage -of working with even the Agg backend. This is not recommended for use in an -interactive setting. - -""" -# -*- noplot -*- - -import numpy as np -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt -import matplotlib.animation as manimation - -FFMpegWriter = manimation.writers['ffmpeg'] -metadata = dict(title='Movie Test', artist='Matplotlib', - comment='Movie support!') -writer = FFMpegWriter(fps=15, metadata=metadata) - -fig = plt.figure() -l, = plt.plot([], [], 'k-o') - -plt.xlim(-5, 5) -plt.ylim(-5, 5) - -x0, y0 = 0, 0 - -with writer.saving(fig, "writer_test.mp4", 100): - for i in range(100): - x0 += 0.1 * np.random.randn() - y0 += 0.1 * np.random.randn() - l.set_data(x0, y0) - writer.grab_frame() diff --git a/examples/animation/random_data.py b/examples/animation/random_data.py deleted file mode 100644 index e638768f9ff4..000000000000 --- a/examples/animation/random_data.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -=========== -Random data -=========== - -An animation of random data. - -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - -fig, ax = plt.subplots() -line, = ax.plot(np.random.rand(10)) -ax.set_ylim(0, 1) - - -def update(data): - line.set_ydata(data) - return line, - - -def data_gen(): - while True: - yield np.random.rand(10) - -ani = animation.FuncAnimation(fig, update, data_gen, interval=100) -plt.show() diff --git a/examples/animation/simple_3danim.py b/examples/animation/simple_3danim.py deleted file mode 100644 index 6c3c536d31d5..000000000000 --- a/examples/animation/simple_3danim.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -============ -3D animation -============ - -A simple example of an animated plot... In 3D! -""" -import numpy as np -import matplotlib.pyplot as plt -import mpl_toolkits.mplot3d.axes3d as p3 -import matplotlib.animation as animation - - -def Gen_RandLine(length, dims=2): - """ - Create a line using a random walk algorithm - - length is the number of points for the line. - dims is the number of dimensions the line has. - """ - lineData = np.empty((dims, length)) - lineData[:, 0] = np.random.rand(dims) - for index in range(1, length): - # scaling the random numbers by 0.1 so - # movement is small compared to position. - # subtraction by 0.5 is to change the range to [-0.5, 0.5] - # to allow a line to move backwards. - step = ((np.random.rand(dims) - 0.5) * 0.1) - lineData[:, index] = lineData[:, index - 1] + step - - return lineData - - -def update_lines(num, dataLines, lines): - for line, data in zip(lines, dataLines): - # NOTE: there is no .set_data() for 3 dim data... - line.set_data(data[0:2, :num]) - line.set_3d_properties(data[2, :num]) - return lines - -# Attaching 3D axis to the figure -fig = plt.figure() -ax = p3.Axes3D(fig) - -# Fifty lines of random 3-D lines -data = [Gen_RandLine(25, 3) for index in range(50)] - -# Creating fifty line objects. -# NOTE: Can't pass empty arrays into 3d version of plot() -lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1])[0] for dat in data] - -# Setting the axes properties -ax.set_xlim3d([0.0, 1.0]) -ax.set_xlabel('X') - -ax.set_ylim3d([0.0, 1.0]) -ax.set_ylabel('Y') - -ax.set_zlim3d([0.0, 1.0]) -ax.set_zlabel('Z') - -ax.set_title('3D Test') - -# Creating the Animation object -line_ani = animation.FuncAnimation(fig, update_lines, 25, fargs=(data, lines), - interval=50, blit=False) - -plt.show() diff --git a/examples/animation/simple_anim.py b/examples/animation/simple_anim.py deleted file mode 100644 index 730ad926868e..000000000000 --- a/examples/animation/simple_anim.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -A simple example of an animated plot -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.animation as animation - -fig, ax = plt.subplots() - -x = np.arange(0, 2*np.pi, 0.01) -line, = ax.plot(x, np.sin(x)) - - -def animate(i): - line.set_ydata(np.sin(x + i/10.0)) # update the data - return line, - - -# Init only required for blitting to give a clean slate. -def init(): - line.set_ydata(np.ma.array(x, mask=True)) - return line, - -ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init, - interval=25, blit=True) -plt.show() diff --git a/examples/animation/strip_chart_demo.py b/examples/animation/strip_chart_demo.py deleted file mode 100644 index 6beeaa99ba07..000000000000 --- a/examples/animation/strip_chart_demo.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -============ -Oscilloscope -============ - -Emulates an oscilloscope. -""" -import numpy as np -from matplotlib.lines import Line2D -import matplotlib.pyplot as plt -import matplotlib.animation as animation - - -class Scope(object): - def __init__(self, ax, maxt=2, dt=0.02): - self.ax = ax - self.dt = dt - self.maxt = maxt - self.tdata = [0] - self.ydata = [0] - self.line = Line2D(self.tdata, self.ydata) - self.ax.add_line(self.line) - self.ax.set_ylim(-.1, 1.1) - self.ax.set_xlim(0, self.maxt) - - def update(self, y): - lastt = self.tdata[-1] - if lastt > self.tdata[0] + self.maxt: # reset the arrays - self.tdata = [self.tdata[-1]] - self.ydata = [self.ydata[-1]] - self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt) - self.ax.figure.canvas.draw() - - t = self.tdata[-1] + self.dt - self.tdata.append(t) - self.ydata.append(y) - self.line.set_data(self.tdata, self.ydata) - return self.line, - - -def emitter(p=0.03): - 'return a random value with probability p, else 0' - while True: - v = np.random.rand(1) - if v > p: - yield 0. - else: - yield np.random.rand(1) - -fig, ax = plt.subplots() -scope = Scope(ax) - -# pass a generator in "emitter" to produce data for the update func -ani = animation.FuncAnimation(fig, scope.update, emitter, interval=10, - blit=True) - - -plt.show() diff --git a/examples/animation/subplots.py b/examples/animation/subplots.py deleted file mode 100644 index 9af8296471a1..000000000000 --- a/examples/animation/subplots.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -================= -Animated subplots -================= - -This example uses subclassing, but there is no reason that the proper function -couldn't be set up and then use FuncAnimation. The code is long, but not -really complex. The length is due solely to the fact that there are a total of -9 lines that need to be changed for the animation as well as 3 subplots that -need initial set up. - -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.lines import Line2D -import matplotlib.animation as animation - - -class SubplotAnimation(animation.TimedAnimation): - def __init__(self): - fig = plt.figure() - ax1 = fig.add_subplot(1, 2, 1) - ax2 = fig.add_subplot(2, 2, 2) - ax3 = fig.add_subplot(2, 2, 4) - - self.t = np.linspace(0, 80, 400) - self.x = np.cos(2 * np.pi * self.t / 10.) - self.y = np.sin(2 * np.pi * self.t / 10.) - self.z = 10 * self.t - - ax1.set_xlabel('x') - ax1.set_ylabel('y') - self.line1 = Line2D([], [], color='black') - self.line1a = Line2D([], [], color='red', linewidth=2) - self.line1e = Line2D( - [], [], color='red', marker='o', markeredgecolor='r') - ax1.add_line(self.line1) - ax1.add_line(self.line1a) - ax1.add_line(self.line1e) - ax1.set_xlim(-1, 1) - ax1.set_ylim(-2, 2) - ax1.set_aspect('equal', 'datalim') - - ax2.set_xlabel('y') - ax2.set_ylabel('z') - self.line2 = Line2D([], [], color='black') - self.line2a = Line2D([], [], color='red', linewidth=2) - self.line2e = Line2D( - [], [], color='red', marker='o', markeredgecolor='r') - ax2.add_line(self.line2) - ax2.add_line(self.line2a) - ax2.add_line(self.line2e) - ax2.set_xlim(-1, 1) - ax2.set_ylim(0, 800) - - ax3.set_xlabel('x') - ax3.set_ylabel('z') - self.line3 = Line2D([], [], color='black') - self.line3a = Line2D([], [], color='red', linewidth=2) - self.line3e = Line2D( - [], [], color='red', marker='o', markeredgecolor='r') - ax3.add_line(self.line3) - ax3.add_line(self.line3a) - ax3.add_line(self.line3e) - ax3.set_xlim(-1, 1) - ax3.set_ylim(0, 800) - - animation.TimedAnimation.__init__(self, fig, interval=50, blit=True) - - def _draw_frame(self, framedata): - i = framedata - head = i - 1 - head_slice = (self.t > self.t[i] - 1.0) & (self.t < self.t[i]) - - self.line1.set_data(self.x[:i], self.y[:i]) - self.line1a.set_data(self.x[head_slice], self.y[head_slice]) - self.line1e.set_data(self.x[head], self.y[head]) - - self.line2.set_data(self.y[:i], self.z[:i]) - self.line2a.set_data(self.y[head_slice], self.z[head_slice]) - self.line2e.set_data(self.y[head], self.z[head]) - - self.line3.set_data(self.x[:i], self.z[:i]) - self.line3a.set_data(self.x[head_slice], self.z[head_slice]) - self.line3e.set_data(self.x[head], self.z[head]) - - self._drawn_artists = [self.line1, self.line1a, self.line1e, - self.line2, self.line2a, self.line2e, - self.line3, self.line3a, self.line3e] - - def new_frame_seq(self): - return iter(range(self.t.size)) - - def _init_draw(self): - lines = [self.line1, self.line1a, self.line1e, - self.line2, self.line2a, self.line2e, - self.line3, self.line3a, self.line3e] - for l in lines: - l.set_data([], []) - -ani = SubplotAnimation() -# ani.save('test_sub.mp4') -plt.show() diff --git a/examples/api/README.txt b/examples/api/README.txt deleted file mode 100644 index a0b901f9d20c..000000000000 --- a/examples/api/README.txt +++ /dev/null @@ -1,39 +0,0 @@ -matplotlib API -============== - -These examples use the matplotlib api rather than the pylab/pyplot -procedural state machine. For robust, production level scripts, or -for applications or web application servers, we recommend you use the -matplotlib API directly as it gives you the maximum control over your -figures, axes and plottng commands. - -The example agg_oo.py is the simplest example of using the Agg backend -which is readily ported to other output formats. This example is a -good starting point if your are a web application developer. Many of -the other examples in this directory use matplotlib.pyplot just to -create the figure and show calls, and use the API for everything else. -This is a good solution for production quality scripts. For full -fledged GUI applications, see the user_interfaces examples. - -Example style guide -=================== - -If you are creating new examples, you cannot import pylab or import * -from any module in your examples. The only three functions allowed -from pyplot are "figure", "show" and "close", which you can use as -convenience functions for managing figures. All other matplotlib -functionality must illustrate the API. - -A simple example of the recommended style is:: - - import numpy as np - import matplotlib.pyplot as plt - - fig, ax = plt.subplots() - ax.plot(np.random.rand(10)) - ax.set_xlabel('some x data') - ax.set_ylabel('some y data') - ax.set_title('some title') - ax.grid(True) - fig.savefig('myfig') - plt.show() diff --git a/examples/api/agg_oo.py b/examples/api/agg_oo.py deleted file mode 100644 index cf42a2a0a82c..000000000000 --- a/examples/api/agg_oo.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- noplot -*- -""" -A pure OO (look Ma, no pylab!) example using the agg backend -""" -from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas -from matplotlib.figure import Figure - -fig = Figure() -canvas = FigureCanvas(fig) -ax = fig.add_subplot(111) -ax.plot([1, 2, 3]) -ax.set_title('hi mom') -ax.grid(True) -ax.set_xlabel('time') -ax.set_ylabel('volts') -canvas.print_figure('test') diff --git a/examples/api/barchart_demo.py b/examples/api/barchart_demo.py deleted file mode 100644 index e651b77538f4..000000000000 --- a/examples/api/barchart_demo.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -A bar plot with errorbars and height labels on individual bars -""" -import numpy as np -import matplotlib.pyplot as plt - -N = 5 -men_means = (20, 35, 30, 35, 27) -men_std = (2, 3, 4, 1, 2) - -ind = np.arange(N) # the x locations for the groups -width = 0.35 # the width of the bars - -fig, ax = plt.subplots() -rects1 = ax.bar(ind, men_means, width, color='r', yerr=men_std) - -women_means = (25, 32, 34, 20, 25) -women_std = (3, 5, 2, 3, 3) -rects2 = ax.bar(ind + width, women_means, width, color='y', yerr=women_std) - -# add some text for labels, title and axes ticks -ax.set_ylabel('Scores') -ax.set_title('Scores by group and gender') -ax.set_xticks(ind + width) -ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5')) - -ax.legend((rects1[0], rects2[0]), ('Men', 'Women')) - - -def autolabel(rects): - """ - Attach a text label above each bar displaying its height - """ - for rect in rects: - height = rect.get_height() - ax.text(rect.get_x() + rect.get_width()/2., 1.05*height, - '%d' % int(height), - ha='center', va='bottom') - -autolabel(rects1) -autolabel(rects2) - -plt.show() diff --git a/examples/api/bbox_intersect.py b/examples/api/bbox_intersect.py deleted file mode 100644 index 765877ddeac1..000000000000 --- a/examples/api/bbox_intersect.py +++ /dev/null @@ -1,23 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.transforms import Bbox -from matplotlib.path import Path - -left, bottom, width, height = (-1, -1, 2, 2) -rect = plt.Rectangle((left, bottom), width, height, facecolor="#aaaaaa") - -fig, ax = plt.subplots() -ax.add_patch(rect) - -bbox = Bbox.from_bounds(left, bottom, width, height) - -for i in range(12): - vertices = (np.random.random((2, 2)) - 0.5) * 6.0 - path = Path(vertices) - if path.intersects_bbox(bbox): - color = 'r' - else: - color = 'b' - ax.plot(vertices[:, 0], vertices[:, 1], color=color) - -plt.show() diff --git a/examples/api/collections_demo.py b/examples/api/collections_demo.py deleted file mode 100644 index 2c3edad6d105..000000000000 --- a/examples/api/collections_demo.py +++ /dev/null @@ -1,126 +0,0 @@ -'''Demonstration of LineCollection, PolyCollection, and -RegularPolyCollection with autoscaling. - -For the first two subplots, we will use spirals. Their -size will be set in plot units, not data units. Their positions -will be set in data units by using the "offsets" and "transOffset" -kwargs of the LineCollection and PolyCollection. - -The third subplot will make regular polygons, with the same -type of scaling and positioning as in the first two. - -The last subplot illustrates the use of "offsets=(xo,yo)", -that is, a single tuple instead of a list of tuples, to generate -successively offset curves, with the offset given in data -units. This behavior is available only for the LineCollection. - -''' - -import matplotlib.pyplot as plt -from matplotlib import collections, colors, transforms -import numpy as np - -nverts = 50 -npts = 100 - -# Make some spirals -r = np.arange(nverts) -theta = np.linspace(0, 2*np.pi, nverts) -xx = r * np.sin(theta) -yy = r * np.cos(theta) -spiral = list(zip(xx, yy)) - -# Make some offsets -rs = np.random.RandomState([12345678]) -xo = rs.randn(npts) -yo = rs.randn(npts) -xyo = list(zip(xo, yo)) - -# Make a list of colors cycling through the default series. -colors = [colors.to_rgba(c) - for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] - -fig, axes = plt.subplots(2, 2) -fig.subplots_adjust(top=0.92, left=0.07, right=0.97, - hspace=0.3, wspace=0.3) -((ax1, ax2), (ax3, ax4)) = axes # unpack the axes - - -col = collections.LineCollection([spiral], offsets=xyo, - transOffset=ax1.transData) -trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0/72.0) -col.set_transform(trans) # the points to pixels transform -# Note: the first argument to the collection initializer -# must be a list of sequences of x,y tuples; we have only -# one sequence, but we still have to put it in a list. -ax1.add_collection(col, autolim=True) -# autolim=True enables autoscaling. For collections with -# offsets like this, it is neither efficient nor accurate, -# but it is good enough to generate a plot that you can use -# as a starting point. If you know beforehand the range of -# x and y that you want to show, it is better to set them -# explicitly, leave out the autolim kwarg (or set it to False), -# and omit the 'ax1.autoscale_view()' call below. - -# Make a transform for the line segments such that their size is -# given in points: -col.set_color(colors) - -ax1.autoscale_view() # See comment above, after ax1.add_collection. -ax1.set_title('LineCollection using offsets') - - -# The same data as above, but fill the curves. -col = collections.PolyCollection([spiral], offsets=xyo, - transOffset=ax2.transData) -trans = transforms.Affine2D().scale(fig.dpi/72.0) -col.set_transform(trans) # the points to pixels transform -ax2.add_collection(col, autolim=True) -col.set_color(colors) - - -ax2.autoscale_view() -ax2.set_title('PolyCollection using offsets') - -# 7-sided regular polygons - -col = collections.RegularPolyCollection(7, - sizes=np.fabs(xx) * 10.0, offsets=xyo, - transOffset=ax3.transData) -trans = transforms.Affine2D().scale(fig.dpi / 72.0) -col.set_transform(trans) # the points to pixels transform -ax3.add_collection(col, autolim=True) -col.set_color(colors) -ax3.autoscale_view() -ax3.set_title('RegularPolyCollection using offsets') - - -# Simulate a series of ocean current profiles, successively -# offset by 0.1 m/s so that they form what is sometimes called -# a "waterfall" plot or a "stagger" plot. - -nverts = 60 -ncurves = 20 -offs = (0.1, 0.0) - -yy = np.linspace(0, 2*np.pi, nverts) -ym = np.amax(yy) -xx = (0.2 + (ym - yy)/ym)**2 * np.cos(yy - 0.4)*0.5 -segs = [] -for i in range(ncurves): - xxx = xx + 0.02*rs.randn(nverts) - curve = list(zip(xxx, yy*100)) - segs.append(curve) - -col = collections.LineCollection(segs, offsets=offs) -ax4.add_collection(col, autolim=True) -col.set_color(colors) -ax4.autoscale_view() -ax4.set_title('Successive data offsets') -ax4.set_xlabel('Zonal velocity component (m/s)') -ax4.set_ylabel('Depth (m)') -# Reverse the y-axis so depth increases downward -ax4.set_ylim(ax4.get_ylim()[::-1]) - - -plt.show() diff --git a/examples/api/colorbar_basics.py b/examples/api/colorbar_basics.py deleted file mode 100644 index 3d0cf93a65d6..000000000000 --- a/examples/api/colorbar_basics.py +++ /dev/null @@ -1,28 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -# setup some generic data -N = 37 -x, y = np.mgrid[:N, :N] -Z = (np.cos(x*0.2) + np.sin(y*0.3)) - -# mask out the negative and positve values, respectively -Zpos = np.ma.masked_less(Z, 0) -Zneg = np.ma.masked_greater(Z, 0) - -fig, (ax1, ax2) = plt.subplots(figsize=(8, 3), ncols=2) - -# plot just the positive data and save the -# color "mappable" object returned by ax1.imshow -pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none') - -# add the colorbar using the figure's method, -# telling which mappable we're talking about and -# which axes object it should be near -fig.colorbar(pos, ax=ax1) - -# repeat everything above for the the negative data -neg = ax2.imshow(Zneg, cmap='Reds_r', interpolation='none') -fig.colorbar(neg, ax=ax2) - -plt.show() diff --git a/examples/api/colorbar_only.py b/examples/api/colorbar_only.py deleted file mode 100644 index cba7e3183e1d..000000000000 --- a/examples/api/colorbar_only.py +++ /dev/null @@ -1,74 +0,0 @@ -''' -Make a colorbar as a separate figure. -''' - -import matplotlib.pyplot as plt -import matplotlib as mpl - -# Make a figure and axes with dimensions as desired. -fig = plt.figure(figsize=(8, 3)) -ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15]) -ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15]) -ax3 = fig.add_axes([0.05, 0.15, 0.9, 0.15]) - -# Set the colormap and norm to correspond to the data for which -# the colorbar will be used. -cmap = mpl.cm.cool -norm = mpl.colors.Normalize(vmin=5, vmax=10) - -# ColorbarBase derives from ScalarMappable and puts a colorbar -# in a specified axes, so it has everything needed for a -# standalone colorbar. There are many more kwargs, but the -# following gives a basic continuous colorbar with ticks -# and labels. -cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap, - norm=norm, - orientation='horizontal') -cb1.set_label('Some Units') - -# The second example illustrates the use of a ListedColormap, a -# BoundaryNorm, and extended ends to show the "over" and "under" -# value colors. -cmap = mpl.colors.ListedColormap(['r', 'g', 'b', 'c']) -cmap.set_over('0.25') -cmap.set_under('0.75') - -# If a ListedColormap is used, the length of the bounds array must be -# one greater than the length of the color list. The bounds must be -# monotonically increasing. -bounds = [1, 2, 4, 7, 8] -norm = mpl.colors.BoundaryNorm(bounds, cmap.N) -cb2 = mpl.colorbar.ColorbarBase(ax2, cmap=cmap, - norm=norm, - # to use 'extend', you must - # specify two extra boundaries: - boundaries=[0] + bounds + [13], - extend='both', - ticks=bounds, # optional - spacing='proportional', - orientation='horizontal') -cb2.set_label('Discrete intervals, some other units') - -# The third example illustrates the use of custom length colorbar -# extensions, used on a colorbar with discrete intervals. -cmap = mpl.colors.ListedColormap([[0., .4, 1.], [0., .8, 1.], - [1., .8, 0.], [1., .4, 0.]]) -cmap.set_over((1., 0., 0.)) -cmap.set_under((0., 0., 1.)) - -bounds = [-1., -.5, 0., .5, 1.] -norm = mpl.colors.BoundaryNorm(bounds, cmap.N) -cb3 = mpl.colorbar.ColorbarBase(ax3, cmap=cmap, - norm=norm, - boundaries=[-10] + bounds + [10], - extend='both', - # Make the length of each extension - # the same as the length of the - # interior colors: - extendfrac='auto', - ticks=bounds, - spacing='uniform', - orientation='horizontal') -cb3.set_label('Custom extension lengths, some other units') - -plt.show() diff --git a/examples/api/compound_path.py b/examples/api/compound_path.py deleted file mode 100644 index 7783ec822e1d..000000000000 --- a/examples/api/compound_path.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Make a compund path -- in this case two simple polygons, a rectangle -and a triangle. Use CLOSEOPOLY and MOVETO for the different parts of -the compound path -""" -import numpy as np -from matplotlib.path import Path -from matplotlib.patches import PathPatch -import matplotlib.pyplot as plt - - -vertices = [] -codes = [] - -codes = [Path.MOVETO] + [Path.LINETO]*3 + [Path.CLOSEPOLY] -vertices = [(1, 1), (1, 2), (2, 2), (2, 1), (0, 0)] - -codes += [Path.MOVETO] + [Path.LINETO]*2 + [Path.CLOSEPOLY] -vertices += [(4, 4), (5, 5), (5, 4), (0, 0)] - -vertices = np.array(vertices, float) -path = Path(vertices, codes) - -pathpatch = PathPatch(path, facecolor='None', edgecolor='green') - -fig, ax = plt.subplots() -ax.add_patch(pathpatch) -ax.set_title('A compound path') - -ax.dataLim.update_from_data_xy(vertices) -ax.autoscale_view() - - -plt.show() diff --git a/examples/api/custom_projection_example.py b/examples/api/custom_projection_example.py deleted file mode 100644 index c0ab7f1f6ef3..000000000000 --- a/examples/api/custom_projection_example.py +++ /dev/null @@ -1,467 +0,0 @@ -from __future__ import unicode_literals - -import matplotlib -from matplotlib.axes import Axes -from matplotlib.patches import Circle -from matplotlib.path import Path -from matplotlib.ticker import NullLocator, Formatter, FixedLocator -from matplotlib.transforms import Affine2D, BboxTransformTo, Transform -from matplotlib.projections import register_projection -import matplotlib.spines as mspines -import matplotlib.axis as maxis -import numpy as np - -rcParams = matplotlib.rcParams - -# This example projection class is rather long, but it is designed to -# illustrate many features, not all of which will be used every time. -# It is also common to factor out a lot of these methods into common -# code used by a number of projections with similar characteristics -# (see geo.py). - - -class GeoAxes(Axes): - """ - An abstract base class for geographic projections - """ - class ThetaFormatter(Formatter): - """ - Used to format the theta tick labels. Converts the native - unit of radians into degrees and adds a degree symbol. - """ - def __init__(self, round_to=1.0): - self._round_to = round_to - - def __call__(self, x, pos=None): - degrees = (x / np.pi) * 180.0 - degrees = np.round(degrees / self._round_to) * self._round_to - if rcParams['text.usetex'] and not rcParams['text.latex.unicode']: - return r"$%0.0f^\circ$" % degrees - else: - return "%0.0f\u00b0" % degrees - - RESOLUTION = 75 - - def _init_axis(self): - self.xaxis = maxis.XAxis(self) - self.yaxis = maxis.YAxis(self) - # Do not register xaxis or yaxis with spines -- as done in - # Axes._init_axis() -- until GeoAxes.xaxis.cla() works. - # self.spines['geo'].register_axis(self.yaxis) - self._update_transScale() - - def cla(self): - Axes.cla(self) - - self.set_longitude_grid(30) - self.set_latitude_grid(15) - self.set_longitude_grid_ends(75) - self.xaxis.set_minor_locator(NullLocator()) - self.yaxis.set_minor_locator(NullLocator()) - self.xaxis.set_ticks_position('none') - self.yaxis.set_ticks_position('none') - self.yaxis.set_tick_params(label1On=True) - # Why do we need to turn on yaxis tick labels, but - # xaxis tick labels are already on? - - self.grid(rcParams['axes.grid']) - - Axes.set_xlim(self, -np.pi, np.pi) - Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0) - - def _set_lim_and_transforms(self): - # A (possibly non-linear) projection on the (already scaled) data - - # There are three important coordinate spaces going on here: - # - # 1. Data space: The space of the data itself - # - # 2. Axes space: The unit rectangle (0, 0) to (1, 1) - # covering the entire plot area. - # - # 3. Display space: The coordinates of the resulting image, - # often in pixels or dpi/inch. - - # This function makes heavy use of the Transform classes in - # ``lib/matplotlib/transforms.py.`` For more information, see - # the inline documentation there. - - # The goal of the first two transformations is to get from the - # data space (in this case longitude and latitude) to axes - # space. It is separated into a non-affine and affine part so - # that the non-affine part does not have to be recomputed when - # a simple affine change to the figure has been made (such as - # resizing the window or changing the dpi). - - # 1) The core transformation from data space into - # rectilinear space defined in the HammerTransform class. - self.transProjection = self._get_core_transform(self.RESOLUTION) - - # 2) The above has an output range that is not in the unit - # rectangle, so scale and translate it so it fits correctly - # within the axes. The peculiar calculations of xscale and - # yscale are specific to a Aitoff-Hammer projection, so don't - # worry about them too much. - self.transAffine = self._get_affine_transform() - - # 3) This is the transformation from axes space to display - # space. - self.transAxes = BboxTransformTo(self.bbox) - - # Now put these 3 transforms together -- from data all the way - # to display coordinates. Using the '+' operator, these - # transforms will be applied "in order". The transforms are - # automatically simplified, if possible, by the underlying - # transformation framework. - self.transData = \ - self.transProjection + \ - self.transAffine + \ - self.transAxes - - # The main data transformation is set up. Now deal with - # gridlines and tick labels. - - # Longitude gridlines and ticklabels. The input to these - # transforms are in display space in x and axes space in y. - # Therefore, the input values will be in range (-xmin, 0), - # (xmax, 1). The goal of these transforms is to go from that - # space to display space. The tick labels will be offset 4 - # pixels from the equator. - self._xaxis_pretransform = \ - Affine2D() \ - .scale(1.0, self._longitude_cap * 2.0) \ - .translate(0.0, -self._longitude_cap) - self._xaxis_transform = \ - self._xaxis_pretransform + \ - self.transData - self._xaxis_text1_transform = \ - Affine2D().scale(1.0, 0.0) + \ - self.transData + \ - Affine2D().translate(0.0, 4.0) - self._xaxis_text2_transform = \ - Affine2D().scale(1.0, 0.0) + \ - self.transData + \ - Affine2D().translate(0.0, -4.0) - - # Now set up the transforms for the latitude ticks. The input to - # these transforms are in axes space in x and display space in - # y. Therefore, the input values will be in range (0, -ymin), - # (1, ymax). The goal of these transforms is to go from that - # space to display space. The tick labels will be offset 4 - # pixels from the edge of the axes ellipse. - yaxis_stretch = Affine2D().scale(np.pi*2, 1).translate(-np.pi, 0) - yaxis_space = Affine2D().scale(1.0, 1.1) - self._yaxis_transform = \ - yaxis_stretch + \ - self.transData - yaxis_text_base = \ - yaxis_stretch + \ - self.transProjection + \ - (yaxis_space + - self.transAffine + - self.transAxes) - self._yaxis_text1_transform = \ - yaxis_text_base + \ - Affine2D().translate(-8.0, 0.0) - self._yaxis_text2_transform = \ - yaxis_text_base + \ - Affine2D().translate(8.0, 0.0) - - def _get_affine_transform(self): - transform = self._get_core_transform(1) - xscale, _ = transform.transform_point((np.pi, 0)) - _, yscale = transform.transform_point((0, np.pi / 2.0)) - return Affine2D() \ - .scale(0.5 / xscale, 0.5 / yscale) \ - .translate(0.5, 0.5) - - def get_xaxis_transform(self, which='grid'): - """ - Override this method to provide a transformation for the - x-axis tick labels. - - Returns a tuple of the form (transform, valign, halign) - """ - if which not in ['tick1', 'tick2', 'grid']: - msg = "'which' must be on of [ 'tick1' | 'tick2' | 'grid' ]" - raise ValueError(msg) - return self._xaxis_transform - - def get_xaxis_text1_transform(self, pad): - return self._xaxis_text1_transform, 'bottom', 'center' - - def get_xaxis_text2_transform(self, pad): - """ - Override this method to provide a transformation for the - secondary x-axis tick labels. - - Returns a tuple of the form (transform, valign, halign) - """ - return self._xaxis_text2_transform, 'top', 'center' - - def get_yaxis_transform(self, which='grid'): - """ - Override this method to provide a transformation for the - y-axis grid and ticks. - """ - if which not in ['tick1', 'tick2', 'grid']: - msg = "'which' must be one of [ 'tick1' | 'tick2' | 'grid' ]" - raise ValueError(msg) - return self._yaxis_transform - - def get_yaxis_text1_transform(self, pad): - """ - Override this method to provide a transformation for the - y-axis tick labels. - - Returns a tuple of the form (transform, valign, halign) - """ - return self._yaxis_text1_transform, 'center', 'right' - - def get_yaxis_text2_transform(self, pad): - """ - Override this method to provide a transformation for the - secondary y-axis tick labels. - - Returns a tuple of the form (transform, valign, halign) - """ - return self._yaxis_text2_transform, 'center', 'left' - - def _gen_axes_patch(self): - """ - Override this method to define the shape that is used for the - background of the plot. It should be a subclass of Patch. - - In this case, it is a Circle (that may be warped by the axes - transform into an ellipse). Any data and gridlines will be - clipped to this shape. - """ - return Circle((0.5, 0.5), 0.5) - - def _gen_axes_spines(self): - return {'geo': mspines.Spine.circular_spine(self, (0.5, 0.5), 0.5)} - - def set_yscale(self, *args, **kwargs): - if args[0] != 'linear': - raise NotImplementedError - - # Prevent the user from applying scales to one or both of the - # axes. In this particular case, scaling the axes wouldn't make - # sense, so we don't allow it. - set_xscale = set_yscale - - # Prevent the user from changing the axes limits. In our case, we - # want to display the whole sphere all the time, so we override - # set_xlim and set_ylim to ignore any input. This also applies to - # interactive panning and zooming in the GUI interfaces. - def set_xlim(self, *args, **kwargs): - raise TypeError("It is not possible to change axes limits " - "for geographic projections. Please consider " - "using Basemap or Cartopy.") - - set_ylim = set_xlim - - def format_coord(self, lon, lat): - """ - Override this method to change how the values are displayed in - the status bar. - - In this case, we want them to be displayed in degrees N/S/E/W. - """ - lon = lon * (180.0 / np.pi) - lat = lat * (180.0 / np.pi) - if lat >= 0.0: - ns = 'N' - else: - ns = 'S' - if lon >= 0.0: - ew = 'E' - else: - ew = 'W' - return '%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(lon), ew) - - def set_longitude_grid(self, degrees): - """ - Set the number of degrees between each longitude grid. - - This is an example method that is specific to this projection - class -- it provides a more convenient interface to set the - ticking than set_xticks would. - """ - number = (360.0 / degrees) + 1 - self.xaxis.set_major_locator( - FixedLocator( - np.linspace(-np.pi, np.pi, number, True)[1:-1])) - self.xaxis.set_major_formatter(self.ThetaFormatter(degrees)) - - def set_latitude_grid(self, degrees): - """ - Set the number of degrees between each longitude grid. - - This is an example method that is specific to this projection - class -- it provides a more convenient interface than - set_yticks would. - """ - number = (180.0 / degrees) + 1 - self.yaxis.set_major_locator( - FixedLocator( - np.linspace(-np.pi / 2.0, np.pi / 2.0, number, True)[1:-1])) - self.yaxis.set_major_formatter(self.ThetaFormatter(degrees)) - - def set_longitude_grid_ends(self, degrees): - """ - Set the latitude(s) at which to stop drawing the longitude grids. - - Often, in geographic projections, you wouldn't want to draw - longitude gridlines near the poles. This allows the user to - specify the degree at which to stop drawing longitude grids. - - This is an example method that is specific to this projection - class -- it provides an interface to something that has no - analogy in the base Axes class. - """ - self._longitude_cap = degrees * (np.pi / 180.0) - self._xaxis_pretransform \ - .clear() \ - .scale(1.0, self._longitude_cap * 2.0) \ - .translate(0.0, -self._longitude_cap) - - def get_data_ratio(self): - """ - Return the aspect ratio of the data itself. - - This method should be overridden by any Axes that have a - fixed data ratio. - """ - return 1.0 - - # Interactive panning and zooming is not supported with this projection, - # so we override all of the following methods to disable it. - def can_zoom(self): - """ - Return *True* if this axes supports the zoom box button functionality. - This axes object does not support interactive zoom box. - """ - return False - - def can_pan(self): - """ - Return *True* if this axes supports the pan/zoom button functionality. - This axes object does not support interactive pan/zoom. - """ - return False - - def start_pan(self, x, y, button): - pass - - def end_pan(self): - pass - - def drag_pan(self, button, key, x, y): - pass - - -class HammerAxes(GeoAxes): - """ - A custom class for the Aitoff-Hammer projection, an equal-area map - projection. - - https://en.wikipedia.org/wiki/Hammer_projection - """ - - # The projection must specify a name. This will be used by the - # user to select the projection, - # i.e. ``subplot(111, projection='custom_hammer')``. - name = 'custom_hammer' - - class HammerTransform(Transform): - """ - The base Hammer transform. - """ - input_dims = 2 - output_dims = 2 - is_separable = False - - def __init__(self, resolution): - """ - Create a new Hammer transform. Resolution is the number of steps - to interpolate between each input line segment to approximate its - path in curved Hammer space. - """ - Transform.__init__(self) - self._resolution = resolution - - def transform_non_affine(self, ll): - longitude = ll[:, 0:1] - latitude = ll[:, 1:2] - - # Pre-compute some values - half_long = longitude / 2.0 - cos_latitude = np.cos(latitude) - sqrt2 = np.sqrt(2.0) - - alpha = np.sqrt(1.0 + cos_latitude * np.cos(half_long)) - x = (2.0 * sqrt2) * (cos_latitude * np.sin(half_long)) / alpha - y = (sqrt2 * np.sin(latitude)) / alpha - return np.concatenate((x, y), 1) - transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ - - def transform_path_non_affine(self, path): - # vertices = path.vertices - ipath = path.interpolated(self._resolution) - return Path(self.transform(ipath.vertices), ipath.codes) - transform_path_non_affine.__doc__ = \ - Transform.transform_path_non_affine.__doc__ - - def inverted(self): - return HammerAxes.InvertedHammerTransform(self._resolution) - inverted.__doc__ = Transform.inverted.__doc__ - - class InvertedHammerTransform(Transform): - input_dims = 2 - output_dims = 2 - is_separable = False - - def __init__(self, resolution): - Transform.__init__(self) - self._resolution = resolution - - def transform_non_affine(self, xy): - x = xy[:, 0:1] - y = xy[:, 1:2] - - quarter_x = 0.25 * x - half_y = 0.5 * y - z = np.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y) - longitude = 2 * np.arctan((z*x) / (2.0 * (2.0*z*z - 1.0))) - latitude = np.arcsin(y*z) - return np.concatenate((longitude, latitude), 1) - transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ - - def inverted(self): - return HammerAxes.HammerTransform(self._resolution) - inverted.__doc__ = Transform.inverted.__doc__ - - def __init__(self, *args, **kwargs): - self._longitude_cap = np.pi / 2.0 - GeoAxes.__init__(self, *args, **kwargs) - self.set_aspect(0.5, adjustable='box', anchor='C') - self.cla() - - def _get_core_transform(self, resolution): - return self.HammerTransform(resolution) - - -# Now register the projection with matplotlib so the user can select -# it. -register_projection(HammerAxes) - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - # Now make a simple example using the custom projection. - plt.subplot(111, projection="custom_hammer") - p = plt.plot([-1, 1, 1], [-1, -1, 1], "o-") - plt.grid(True) - - plt.show() diff --git a/examples/api/custom_scale_example.py b/examples/api/custom_scale_example.py deleted file mode 100644 index 7a25c415860c..000000000000 --- a/examples/api/custom_scale_example.py +++ /dev/null @@ -1,178 +0,0 @@ -from __future__ import unicode_literals - -import numpy as np -from numpy import ma -from matplotlib import scale as mscale -from matplotlib import transforms as mtransforms -from matplotlib.ticker import Formatter, FixedLocator -from matplotlib import rcParams - - -# BUG: this example fails with any other setting of axisbelow -rcParams['axes.axisbelow'] = False - - -class MercatorLatitudeScale(mscale.ScaleBase): - """ - Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using - the system used to scale latitudes in a Mercator projection. - - The scale function: - ln(tan(y) + sec(y)) - - The inverse scale function: - atan(sinh(y)) - - Since the Mercator scale tends to infinity at +/- 90 degrees, - there is user-defined threshold, above and below which nothing - will be plotted. This defaults to +/- 85 degrees. - - source: - http://en.wikipedia.org/wiki/Mercator_projection - """ - - # The scale class must have a member ``name`` that defines the - # string used to select the scale. For example, - # ``gca().set_yscale("mercator")`` would be used to select this - # scale. - name = 'mercator' - - def __init__(self, axis, **kwargs): - """ - Any keyword arguments passed to ``set_xscale`` and - ``set_yscale`` will be passed along to the scale's - constructor. - - thresh: The degree above which to crop the data. - """ - mscale.ScaleBase.__init__(self) - thresh = kwargs.pop("thresh", np.radians(85)) - if thresh >= np.pi / 2.0: - raise ValueError("thresh must be less than pi/2") - self.thresh = thresh - - def get_transform(self): - """ - Override this method to return a new instance that does the - actual transformation of the data. - - The MercatorLatitudeTransform class is defined below as a - nested class of this one. - """ - return self.MercatorLatitudeTransform(self.thresh) - - def set_default_locators_and_formatters(self, axis): - """ - Override to set up the locators and formatters to use with the - scale. This is only required if the scale requires custom - locators and formatters. Writing custom locators and - formatters is rather outside the scope of this example, but - there are many helpful examples in ``ticker.py``. - - In our case, the Mercator example uses a fixed locator from - -90 to 90 degrees and a custom formatter class to put convert - the radians to degrees and put a degree symbol after the - value:: - """ - class DegreeFormatter(Formatter): - def __call__(self, x, pos=None): - # \u00b0 : degree symbol - return "%d\u00b0" % (np.degrees(x)) - - axis.set_major_locator(FixedLocator( - np.radians(np.arange(-90, 90, 10)))) - axis.set_major_formatter(DegreeFormatter()) - axis.set_minor_formatter(DegreeFormatter()) - - def limit_range_for_scale(self, vmin, vmax, minpos): - """ - Override to limit the bounds of the axis to the domain of the - transform. In the case of Mercator, the bounds should be - limited to the threshold that was passed in. Unlike the - autoscaling provided by the tick locators, this range limiting - will always be adhered to, whether the axis range is set - manually, determined automatically or changed through panning - and zooming. - """ - return max(vmin, -self.thresh), min(vmax, self.thresh) - - class MercatorLatitudeTransform(mtransforms.Transform): - # There are two value members that must be defined. - # ``input_dims`` and ``output_dims`` specify number of input - # dimensions and output dimensions to the transformation. - # These are used by the transformation framework to do some - # error checking and prevent incompatible transformations from - # being connected together. When defining transforms for a - # scale, which are, by definition, separable and have only one - # dimension, these members should always be set to 1. - input_dims = 1 - output_dims = 1 - is_separable = True - has_inverse = True - - def __init__(self, thresh): - mtransforms.Transform.__init__(self) - self.thresh = thresh - - def transform_non_affine(self, a): - """ - This transform takes an Nx1 ``numpy`` array and returns a - transformed copy. Since the range of the Mercator scale - is limited by the user-specified threshold, the input - array must be masked to contain only valid values. - ``matplotlib`` will handle masked arrays and remove the - out-of-range data from the plot. Importantly, the - ``transform`` method *must* return an array that is the - same shape as the input array, since these values need to - remain synchronized with values in the other dimension. - """ - masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a) - if masked.mask.any(): - return ma.log(np.abs(ma.tan(masked) + 1.0 / ma.cos(masked))) - else: - return np.log(np.abs(np.tan(a) + 1.0 / np.cos(a))) - - def inverted(self): - """ - Override this method so matplotlib knows how to get the - inverse transform for this transform. - """ - return MercatorLatitudeScale.InvertedMercatorLatitudeTransform( - self.thresh) - - class InvertedMercatorLatitudeTransform(mtransforms.Transform): - input_dims = 1 - output_dims = 1 - is_separable = True - has_inverse = True - - def __init__(self, thresh): - mtransforms.Transform.__init__(self) - self.thresh = thresh - - def transform_non_affine(self, a): - return np.arctan(np.sinh(a)) - - def inverted(self): - return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh) - -# Now that the Scale class has been defined, it must be registered so -# that ``matplotlib`` can find it. -mscale.register_scale(MercatorLatitudeScale) - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - - t = np.arange(-180.0, 180.0, 0.1) - s = np.radians(t)/2. - - plt.plot(t, s, '-', lw=2) - plt.gca().set_yscale('mercator') - - plt.xlabel('Longitude') - plt.ylabel('Latitude') - plt.title('Mercator: Projection of the Oppressor') - plt.grid(True) - - plt.show() diff --git a/examples/api/date_demo.py b/examples/api/date_demo.py deleted file mode 100644 index 96202c465c5f..000000000000 --- a/examples/api/date_demo.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Show how to make date plots in matplotlib using date tick locators and -formatters. See major_minor_demo1.py for more information on -controlling major and minor ticks - -All matplotlib date plotting is done by converting date instances into -days since the 0001-01-01 UTC. The conversion, tick locating and -formatting is done behind the scenes so this is most transparent to -you. The dates module provides several converter functions date2num -and num2date - -""" -import datetime -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.dates as mdates -import matplotlib.cbook as cbook - -years = mdates.YearLocator() # every year -months = mdates.MonthLocator() # every month -yearsFmt = mdates.DateFormatter('%Y') - -# load a numpy record array from yahoo csv data with fields date, -# open, close, volume, adj_close from the mpl-data/example directory. -# The record array stores python datetime.date as an object array in -# the date column -datafile = cbook.get_sample_data('goog.npy') -try: - # Python3 cannot load python2 .npy files with datetime(object) arrays - # unless the encoding is set to bytes. Hovever this option was - # not added until numpy 1.10 so this example will only work with - # python 2 or with numpy 1.10 and later. - r = np.load(datafile, encoding='bytes').view(np.recarray) -except TypeError: - r = np.load(datafile).view(np.recarray) - -fig, ax = plt.subplots() -ax.plot(r.date, r.adj_close) - - -# format the ticks -ax.xaxis.set_major_locator(years) -ax.xaxis.set_major_formatter(yearsFmt) -ax.xaxis.set_minor_locator(months) - -datemin = datetime.date(r.date.min().year, 1, 1) -datemax = datetime.date(r.date.max().year + 1, 1, 1) -ax.set_xlim(datemin, datemax) - - -# format the coords message box -def price(x): - return '$%1.2f' % x -ax.format_xdata = mdates.DateFormatter('%Y-%m-%d') -ax.format_ydata = price -ax.grid(True) - -# rotates and right aligns the x labels, and moves the bottom of the -# axes up to make room for them -fig.autofmt_xdate() - -plt.show() diff --git a/examples/api/date_index_formatter.py b/examples/api/date_index_formatter.py deleted file mode 100644 index 070adcf6de38..000000000000 --- a/examples/api/date_index_formatter.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -When plotting time series, e.g., financial time series, one often wants -to leave out days on which there is no data, eh weekends. The example -below shows how to use an 'index formatter' to achieve the desired plot -""" -from __future__ import print_function -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab -import matplotlib.cbook as cbook -import matplotlib.ticker as ticker - -datafile = cbook.get_sample_data('aapl.csv', asfileobj=False) -print('loading %s' % datafile) -r = mlab.csv2rec(datafile) - -r.sort() -r = r[-30:] # get the last 30 days - - -# first we'll do it the default way, with gaps on weekends -fig, ax = plt.subplots() -ax.plot(r.date, r.adj_close, 'o-') -fig.autofmt_xdate() - -# next we'll write a custom formatter -N = len(r) -ind = np.arange(N) # the evenly spaced plot indices - - -def format_date(x, pos=None): - thisind = np.clip(int(x + 0.5), 0, N - 1) - return r.date[thisind].strftime('%Y-%m-%d') - -fig, ax = plt.subplots() -ax.plot(ind, r.adj_close, 'o-') -ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date)) -fig.autofmt_xdate() - -plt.show() diff --git a/examples/api/demo_affine_image.py b/examples/api/demo_affine_image.py deleted file mode 100644 index 7f1f91aff940..000000000000 --- a/examples/api/demo_affine_image.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -For the backends that support draw_image with optional affine -transform (e.g., agg, ps backend), the image of the output should -have its boundary match the dashed yellow rectangle. -""" - -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt -import matplotlib.transforms as mtransforms - - -def get_image(): - delta = 0.25 - x = y = np.arange(-3.0, 3.0, delta) - X, Y = np.meshgrid(x, y) - Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) - Z = Z2 - Z1 # difference of Gaussians - return Z - - -def do_plot(ax, Z, transform): - im = ax.imshow(Z, interpolation='none', - origin='lower', - extent=[-2, 4, -3, 2], clip_on=True) - - trans_data = transform + ax.transData - im.set_transform(trans_data) - - # display intended extent of the image - x1, x2, y1, y2 = im.get_extent() - ax.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "y--", - transform=trans_data) - ax.set_xlim(-5, 5) - ax.set_ylim(-4, 4) - - -# prepare image and figure -fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2) -Z = get_image() - -# image rotation -do_plot(ax1, Z, mtransforms.Affine2D().rotate_deg(30)) - -# image skew -do_plot(ax2, Z, mtransforms.Affine2D().skew_deg(30, 15)) - -# scale and reflection -do_plot(ax3, Z, mtransforms.Affine2D().scale(-1, .5)) - -# everything and a translation -do_plot(ax4, Z, mtransforms.Affine2D(). - rotate_deg(30).skew_deg(30, 15).scale(-1, .5).translate(.5, -1)) - -plt.show() diff --git a/examples/api/donut_demo.py b/examples/api/donut_demo.py deleted file mode 100644 index ed99b0229246..000000000000 --- a/examples/api/donut_demo.py +++ /dev/null @@ -1,54 +0,0 @@ -import numpy as np -import matplotlib.path as mpath -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - - -def wise(v): - if v == 1: - return "CCW" - else: - return "CW" - - -def make_circle(r): - t = np.arange(0, np.pi * 2.0, 0.01) - t = t.reshape((len(t), 1)) - x = r * np.cos(t) - y = r * np.sin(t) - return np.hstack((x, y)) - -Path = mpath.Path - -fig, ax = plt.subplots() - -inside_vertices = make_circle(0.5) -outside_vertices = make_circle(1.0) -codes = np.ones( - len(inside_vertices), dtype=mpath.Path.code_type) * mpath.Path.LINETO -codes[0] = mpath.Path.MOVETO - -for i, (inside, outside) in enumerate(((1, 1), (1, -1), (-1, 1), (-1, -1))): - # Concatenate the inside and outside subpaths together, changing their - # order as needed - vertices = np.concatenate((outside_vertices[::outside], - inside_vertices[::inside])) - # Shift the path - vertices[:, 0] += i * 2.5 - # The codes will be all "LINETO" commands, except for "MOVETO"s at the - # beginning of each subpath - all_codes = np.concatenate((codes, codes)) - # Create the Path object - path = mpath.Path(vertices, all_codes) - # Add plot it - patch = mpatches.PathPatch(path, facecolor='#885500', edgecolor='black') - ax.add_patch(patch) - - ax.annotate("Outside %s,\nInside %s" % (wise(outside), wise(inside)), - (i * 2.5, -1.5), va="top", ha="center") - -ax.set_xlim(-2, 10) -ax.set_ylim(-3, 2) -ax.set_title('Mmm, donuts!') -ax.set_aspect(1.0) -plt.show() diff --git a/examples/api/engineering_formatter.py b/examples/api/engineering_formatter.py deleted file mode 100644 index dac48d074bd7..000000000000 --- a/examples/api/engineering_formatter.py +++ /dev/null @@ -1,21 +0,0 @@ -''' -Demo to show use of the engineering Formatter. -''' - -import matplotlib.pyplot as plt -import numpy as np - -from matplotlib.ticker import EngFormatter - -prng = np.random.RandomState(123) - -fig, ax = plt.subplots() -ax.set_xscale('log') -formatter = EngFormatter(unit='Hz') -ax.xaxis.set_major_formatter(formatter) - -xs = np.logspace(1, 9, 100) -ys = (0.8 + 0.4 * prng.uniform(size=100)) * np.log10(xs)**2 -ax.plot(xs, ys) - -plt.show() diff --git a/examples/api/filled_step.py b/examples/api/filled_step.py deleted file mode 100644 index 7778512bddf5..000000000000 --- a/examples/api/filled_step.py +++ /dev/null @@ -1,212 +0,0 @@ -import itertools -from collections import OrderedDict -from functools import partial - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.ticker as mticker -from cycler import cycler -from six.moves import zip - - -def filled_hist(ax, edges, values, bottoms=None, orientation='v', - **kwargs): - """ - Draw a histogram as a stepped patch. - - Extra kwargs are passed through to `fill_between` - - Parameters - ---------- - ax : Axes - The axes to plot to - - edges : array - A length n+1 array giving the left edges of each bin and the - right edge of the last bin. - - values : array - A length n array of bin counts or values - - bottoms : scalar or array, optional - A length n array of the bottom of the bars. If None, zero is used. - - orientation : {'v', 'h'} - Orientation of the histogram. 'v' (default) has - the bars increasing in the positive y-direction. - - Returns - ------- - ret : PolyCollection - Artist added to the Axes - """ - print(orientation) - if orientation not in set('hv'): - raise ValueError("orientation must be in {{'h', 'v'}} " - "not {o}".format(o=orientation)) - - kwargs.setdefault('step', 'post') - edges = np.asarray(edges) - values = np.asarray(values) - if len(edges) - 1 != len(values): - raise ValueError('Must provide one more bin edge than value not: ' - 'len(edges): {lb} len(values): {lv}'.format( - lb=len(edges), lv=len(values))) - - if bottoms is None: - bottoms = np.zeros_like(values) - if np.isscalar(bottoms): - bottoms = np.ones_like(values) * bottoms - - values = np.r_[values, values[-1]] - bottoms = np.r_[bottoms, bottoms[-1]] - if orientation == 'h': - return ax.fill_betweenx(edges, values, bottoms, left_margin=False, - **kwargs) - elif orientation == 'v': - return ax.fill_between(edges, values, bottoms, bottom_margin=False, - **kwargs) - else: - raise AssertionError("you should never be here") - - -def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, - hist_func=None, labels=None, - plot_func=None, plot_kwargs=None): - """ - ax : axes.Axes - The axes to add artists too - - stacked_data : array or Mapping - A (N, M) shaped array. The first dimension will be iterated over to - compute histograms row-wise - - sty_cycle : Cycler or operable of dict - Style to apply to each set - - bottoms : array, optional - The initial positions of the bottoms, defaults to 0 - - hist_func : callable, optional - Must have signature `bin_vals, bin_edges = f(data)`. - `bin_edges` expected to be one longer than `bin_vals` - - labels : list of str, optional - The label for each set. - - If not given and stacked data is an array defaults to 'default set {n}' - - If stacked_data is a mapping, and labels is None, default to the keys - (which may come out in a random order). - - If stacked_data is a mapping and labels is given then only - the columns listed by be plotted. - - plot_func : callable, optional - Function to call to draw the histogram must have signature: - - ret = plot_func(ax, edges, top, bottoms=bottoms, - label=label, **kwargs) - - plot_kwargs : dict, optional - Any extra kwargs to pass through to the plotting function. This - will be the same for all calls to the plotting function and will - over-ride the values in cycle. - - Returns - ------- - arts : dict - Dictionary of artists keyed on their labels - """ - # deal with default binning function - if hist_func is None: - hist_func = np.histogram - - # deal with default plotting function - if plot_func is None: - plot_func = filled_hist - - # deal with default - if plot_kwargs is None: - plot_kwargs = {} - print(plot_kwargs) - try: - l_keys = stacked_data.keys() - label_data = True - if labels is None: - labels = l_keys - - except AttributeError: - label_data = False - if labels is None: - labels = itertools.repeat(None) - - if label_data: - loop_iter = enumerate((stacked_data[lab], lab, s) for lab, s in - zip(labels, sty_cycle)) - else: - loop_iter = enumerate(zip(stacked_data, labels, sty_cycle)) - - arts = {} - for j, (data, label, sty) in loop_iter: - if label is None: - label = 'dflt set {n}'.format(n=j) - label = sty.pop('label', label) - vals, edges = hist_func(data) - if bottoms is None: - bottoms = np.zeros_like(vals) - top = bottoms + vals - print(sty) - sty.update(plot_kwargs) - print(sty) - ret = plot_func(ax, edges, top, bottoms=bottoms, - label=label, **sty) - bottoms = top - arts[label] = ret - ax.legend(fontsize=10) - return arts - - -# set up histogram function to fixed bins -edges = np.linspace(-3, 3, 20, endpoint=True) -hist_func = partial(np.histogram, bins=edges) - -# set up style cycles -color_cycle = cycler(facecolor=plt.rcParams['axes.prop_cycle'][:4]) -label_cycle = cycler('label', ['set {n}'.format(n=n) for n in range(4)]) -hatch_cycle = cycler('hatch', ['/', '*', '+', '|']) - -# make some synthetic data -np.random.seed(0) -stack_data = np.random.randn(4, 12250) -dict_data = OrderedDict(zip((c['label'] for c in label_cycle), stack_data)) - -# work with plain arrays -fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True) -arts = stack_hist(ax1, stack_data, color_cycle + label_cycle + hatch_cycle, - hist_func=hist_func) - -arts = stack_hist(ax2, stack_data, color_cycle, - hist_func=hist_func, - plot_kwargs=dict(edgecolor='w', orientation='h')) -ax1.set_ylabel('counts') -ax1.set_xlabel('x') -ax2.set_xlabel('counts') -ax2.set_ylabel('x') - -# work with labeled data - -fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), - tight_layout=True, sharey=True) - -arts = stack_hist(ax1, dict_data, color_cycle + hatch_cycle, - hist_func=hist_func) - -arts = stack_hist(ax2, dict_data, color_cycle + hatch_cycle, - hist_func=hist_func, labels=['set 0', 'set 3']) -ax1.xaxis.set_major_locator(mticker.MaxNLocator(5)) -ax1.set_xlabel('counts') -ax1.set_ylabel('x') -ax2.set_ylabel('x') - -plt.show() diff --git a/examples/api/font_family_rc.py b/examples/api/font_family_rc.py deleted file mode 100644 index a4e1b96168c5..000000000000 --- a/examples/api/font_family_rc.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -You can explicitly set which font family is picked up for a given font -style (e.g., 'serif', 'sans-serif', or 'monospace'). - -In the example below, we only allow one font family (Tahoma) for the -san-serif font style. You the default family with the font.family rc -param, e.g.,:: - - rcParams['font.family'] = 'sans-serif' - -and for the font.family you set a list of font styles to try to find -in order:: - - rcParams['font.sans-serif'] = ['Tahoma', 'DejaVu Sans', - 'Lucida Grande', 'Verdana'] - -""" - -# -*- noplot -*- - -from matplotlib import rcParams -rcParams['font.family'] = 'sans-serif' -rcParams['font.sans-serif'] = ['Tahoma'] -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.plot([1, 2, 3], label='test') - -ax.legend() -plt.show() diff --git a/examples/api/font_file.py b/examples/api/font_file.py deleted file mode 100644 index 9f321afeded4..000000000000 --- a/examples/api/font_file.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- noplot -*- -""" -Although it is usually not a good idea to explicitly point to a single -ttf file for a font instance, you can do so using the -font_manager.FontProperties fname argument (for a more flexible -solution, see the font_fmaily_rc.py and fonts_demo.py examples). -""" -import sys -import os -import matplotlib.font_manager as fm - -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.plot([1, 2, 3]) - -if sys.platform == 'win32': - fpath = 'C:\\Windows\\Fonts\\Tahoma.ttf' -elif sys.platform.startswith('linux'): - basedir = '/usr/share/fonts/truetype' - fonts = ['freefont/FreeSansBoldOblique.ttf', - 'ttf-liberation/LiberationSans-BoldItalic.ttf', - 'msttcorefonts/Comic_Sans_MS.ttf'] - for fpath in fonts: - if os.path.exists(os.path.join(basedir, fpath)): - break -else: - fpath = '/Library/Fonts/Tahoma.ttf' - -if os.path.exists(fpath): - prop = fm.FontProperties(fname=fpath) - fname = os.path.split(fpath)[1] - ax.set_title('this is a special font: %s' % fname, fontproperties=prop) -else: - ax.set_title('Demo fails--cannot find a demo font') -ax.set_xlabel('This is the default font') - -plt.show() diff --git a/examples/api/histogram_path_demo.py b/examples/api/histogram_path_demo.py deleted file mode 100644 index d43f542547d7..000000000000 --- a/examples/api/histogram_path_demo.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -This example shows how to use a path patch to draw a bunch of -rectangles. The technique of using lots of Rectangle instances, or -the faster method of using PolyCollections, were implemented before we -had proper paths with moveto/lineto, closepoly etc in mpl. Now that -we have them, we can draw collections of regularly shaped objects with -homogeous properties more efficiently with a PathCollection. This -example makes a histogram -- its more work to set up the vertex arrays -at the outset, but it should be much faster for large numbers of -objects -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import matplotlib.path as path - -fig, ax = plt.subplots() - -# histogram our data with numpy -data = np.random.randn(1000) -n, bins = np.histogram(data, 50) - -# get the corners of the rectangles for the histogram -left = np.array(bins[:-1]) -right = np.array(bins[1:]) -bottom = np.zeros(len(left)) -top = bottom + n - - -# we need a (numrects x numsides x 2) numpy array for the path helper -# function to build a compound path -XY = np.array([[left, left, right, right], [bottom, top, top, bottom]]).T - -# get the Path object -barpath = path.Path.make_compound_path_from_polys(XY) - -# make a patch out of it -patch = patches.PathPatch(barpath, facecolor='blue') -ax.add_patch(patch) - -# update the view limits -ax.set_xlim(left[0], right[-1]) -ax.set_ylim(bottom.min(), top.max()) - -plt.show() diff --git a/examples/api/image_zcoord.py b/examples/api/image_zcoord.py deleted file mode 100644 index b0a0908d3f50..000000000000 --- a/examples/api/image_zcoord.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Show how to modify the coordinate formatter to report the image "z" -value of the nearest pixel given x and y -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cm as cm - -X = 10*np.random.rand(5, 3) - -fig, ax = plt.subplots() -ax.imshow(X, interpolation='nearest') - -numrows, numcols = X.shape - - -def format_coord(x, y): - col = int(x + 0.5) - row = int(y + 0.5) - if col >= 0 and col < numcols and row >= 0 and row < numrows: - z = X[row, col] - return 'x=%1.4f, y=%1.4f, z=%1.4f' % (x, y, z) - else: - return 'x=%1.4f, y=%1.4f' % (x, y) - -ax.format_coord = format_coord -plt.show() diff --git a/examples/api/joinstyle.py b/examples/api/joinstyle.py deleted file mode 100644 index 24544e19cff2..000000000000 --- a/examples/api/joinstyle.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Illustrate the three different join styles -""" - -import numpy as np -import matplotlib.pyplot as plt - - -def plot_angle(ax, x, y, angle, style): - phi = np.radians(angle) - xx = [x + .5, x, x + .5*np.cos(phi)] - yy = [y, y, y + .5*np.sin(phi)] - ax.plot(xx, yy, lw=8, color='blue', solid_joinstyle=style) - ax.plot(xx[1:], yy[1:], lw=1, color='black') - ax.plot(xx[1::-1], yy[1::-1], lw=1, color='black') - ax.plot(xx[1:2], yy[1:2], 'o', color='red', markersize=3) - ax.text(x, y + .2, '%.0f degrees' % angle) - -fig, ax = plt.subplots() -ax.set_title('Join style') - -for x, style in enumerate((('miter', 'round', 'bevel'))): - ax.text(x, 5, style) - for i in range(5): - plot_angle(ax, x, i, pow(2.0, 3 + i), style) - -ax.set_xlim(-.5, 2.75) -ax.set_ylim(-.5, 5.5) -plt.show() diff --git a/examples/api/legend_demo.py b/examples/api/legend_demo.py deleted file mode 100644 index 9b50fec3ea52..000000000000 --- a/examples/api/legend_demo.py +++ /dev/null @@ -1,20 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -# Make some fake data. -a = b = np.arange(0, 3, .02) -c = np.exp(a) -d = c[::-1] - -# Create plots with pre-defined labels. -fig, ax = plt.subplots() -ax.plot(a, c, 'k--', label='Model length') -ax.plot(a, d, 'k:', label='Data length') -ax.plot(a, c + d, 'k', label='Total message length') - -legend = ax.legend(loc='upper center', shadow=True, fontsize='x-large') - -# Put a nicer background color on the legend. -legend.get_frame().set_facecolor('#00FFCC') - -plt.show() diff --git a/examples/api/line_with_text.py b/examples/api/line_with_text.py deleted file mode 100644 index f5e4bd383331..000000000000 --- a/examples/api/line_with_text.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Show how to override basic methods so an artist can contain another -artist. In this case, the line contains a Text instance to label it. -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.lines as lines -import matplotlib.transforms as mtransforms -import matplotlib.text as mtext - - -class MyLine(lines.Line2D): - def __init__(self, *args, **kwargs): - # we'll update the position when the line data is set - self.text = mtext.Text(0, 0, '') - lines.Line2D.__init__(self, *args, **kwargs) - - # we can't access the label attr until *after* the line is - # inited - self.text.set_text(self.get_label()) - - def set_figure(self, figure): - self.text.set_figure(figure) - lines.Line2D.set_figure(self, figure) - - def set_axes(self, axes): - self.text.set_axes(axes) - lines.Line2D.set_axes(self, axes) - - def set_transform(self, transform): - # 2 pixel offset - texttrans = transform + mtransforms.Affine2D().translate(2, 2) - self.text.set_transform(texttrans) - lines.Line2D.set_transform(self, transform) - - def set_data(self, x, y): - if len(x): - self.text.set_position((x[-1], y[-1])) - - lines.Line2D.set_data(self, x, y) - - def draw(self, renderer): - # draw my label at the end of the line with 2 pixel offset - lines.Line2D.draw(self, renderer) - self.text.draw(renderer) - - -fig, ax = plt.subplots() -x, y = np.random.rand(2, 20) -line = MyLine(x, y, mfc='red', ms=12, label='line label') -#line.text.set_text('line label') -line.text.set_color('red') -line.text.set_fontsize(16) - - -ax.add_line(line) - - -plt.show() diff --git a/examples/api/logo2.py b/examples/api/logo2.py deleted file mode 100644 index abb15b7929dd..000000000000 --- a/examples/api/logo2.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Thanks to Tony Yu for the logo design -""" - -import numpy as np -import matplotlib as mpl -import matplotlib.pyplot as plt -import matplotlib.cm as cm - -mpl.rcParams['xtick.labelsize'] = 10 -mpl.rcParams['ytick.labelsize'] = 12 -mpl.rcParams['axes.edgecolor'] = 'gray' - - -axalpha = 0.05 -#figcolor = '#EFEFEF' -figcolor = 'white' -dpi = 80 -fig = plt.figure(figsize=(6, 1.1), dpi=dpi) -fig.figurePatch.set_edgecolor(figcolor) -fig.figurePatch.set_facecolor(figcolor) - - -def add_math_background(): - ax = fig.add_axes([0., 0., 1., 1.]) - - text = [] - text.append( - (r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = " - r"U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2}" - r"\int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 " - r"\left[\frac{ U^{2\beta}_{\delta_1 \rho_1} - " - r"\alpha^\prime_2U^{1\beta}_{\rho_1 \sigma_2} " - r"}{U^{0\beta}_{\rho_1 \sigma_2}}\right]$", (0.7, 0.2), 20)) - text.append((r"$\frac{d\rho}{d t} + \rho \vec{v}\cdot\nabla\vec{v} " - r"= -\nabla p + \mu\nabla^2 \vec{v} + \rho \vec{g}$", - (0.35, 0.9), 20)) - text.append((r"$\int_{-\infty}^\infty e^{-x^2}dx=\sqrt{\pi}$", - (0.15, 0.3), 25)) - #text.append((r"$E = mc^2 = \sqrt{{m_0}^2c^4 + p^2c^2}$", - # (0.7, 0.42), 30)) - text.append((r"$F_G = G\frac{m_1m_2}{r^2}$", - (0.85, 0.7), 30)) - for eq, (x, y), size in text: - ax.text(x, y, eq, ha='center', va='center', color="#11557c", - alpha=0.25, transform=ax.transAxes, fontsize=size) - ax.set_axis_off() - return ax - - -def add_matplotlib_text(ax): - ax.text(0.95, 0.5, 'matplotlib', color='#11557c', fontsize=65, - ha='right', va='center', alpha=1.0, transform=ax.transAxes) - - -def add_polar_bar(): - ax = fig.add_axes([0.025, 0.075, 0.2, 0.85], projection='polar') - - ax.axesPatch.set_alpha(axalpha) - ax.set_axisbelow(True) - N = 7 - arc = 2. * np.pi - theta = np.arange(0.0, arc, arc/N) - radii = 10 * np.array([0.2, 0.6, 0.8, 0.7, 0.4, 0.5, 0.8]) - width = np.pi / 4 * np.array([0.4, 0.4, 0.6, 0.8, 0.2, 0.5, 0.3]) - bars = ax.bar(theta, radii, width=width, bottom=0.0) - for r, bar in zip(radii, bars): - bar.set_facecolor(cm.jet(r/10.)) - bar.set_alpha(0.6) - - for label in ax.get_xticklabels() + ax.get_yticklabels(): - label.set_visible(False) - - for line in ax.get_ygridlines() + ax.get_xgridlines(): - line.set_lw(0.8) - line.set_alpha(0.9) - line.set_ls('-') - line.set_color('0.5') - - ax.set_yticks(np.arange(1, 9, 2)) - ax.set_rmax(9) - -if __name__ == '__main__': - main_axes = add_math_background() - add_polar_bar() - add_matplotlib_text(main_axes) - plt.show() diff --git a/examples/api/mathtext_asarray.py b/examples/api/mathtext_asarray.py deleted file mode 100644 index 8f41cdb35030..000000000000 --- a/examples/api/mathtext_asarray.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Load a mathtext image as numpy array -""" - -import matplotlib.mathtext as mathtext -import matplotlib.pyplot as plt -import matplotlib -matplotlib.rc('image', origin='upper') - -parser = mathtext.MathTextParser("Bitmap") -parser.to_png('test2.png', - r'$\left[\left\lfloor\frac{5}{\frac{\left(3\right)}{4}} ' - r'y\right)\right]$', color='green', fontsize=14, dpi=100) - -rgba1, depth1 = parser.to_rgba( - r'IQ: $\sigma_i=15$', color='blue', fontsize=20, dpi=200) -rgba2, depth2 = parser.to_rgba( - r'some other string', color='red', fontsize=20, dpi=200) - -fig = plt.figure() -fig.figimage(rgba1.astype(float)/255., 100, 100) -fig.figimage(rgba2.astype(float)/255., 100, 300) - -plt.show() diff --git a/examples/api/patch_collection.py b/examples/api/patch_collection.py deleted file mode 100644 index 48a8a7a14f06..000000000000 --- a/examples/api/patch_collection.py +++ /dev/null @@ -1,47 +0,0 @@ -import numpy as np -import matplotlib -from matplotlib.patches import Circle, Wedge, Polygon -from matplotlib.collections import PatchCollection -import matplotlib.pyplot as plt - - -fig, ax = plt.subplots() - -resolution = 50 # the number of vertices -N = 3 -x = np.random.rand(N) -y = np.random.rand(N) -radii = 0.1*np.random.rand(N) -patches = [] -for x1, y1, r in zip(x, y, radii): - circle = Circle((x1, y1), r) - patches.append(circle) - -x = np.random.rand(N) -y = np.random.rand(N) -radii = 0.1*np.random.rand(N) -theta1 = 360.0*np.random.rand(N) -theta2 = 360.0*np.random.rand(N) -for x1, y1, r, t1, t2 in zip(x, y, radii, theta1, theta2): - wedge = Wedge((x1, y1), r, t1, t2) - patches.append(wedge) - -# Some limiting conditions on Wedge -patches += [ - Wedge((.3, .7), .1, 0, 360), # Full circle - Wedge((.7, .8), .2, 0, 360, width=0.05), # Full ring - Wedge((.8, .3), .2, 0, 45), # Full sector - Wedge((.8, .3), .2, 45, 90, width=0.10), # Ring sector -] - -for i in range(N): - polygon = Polygon(np.random.rand(N, 2), True) - patches.append(polygon) - -colors = 100*np.random.rand(len(patches)) -p = PatchCollection(patches, alpha=0.4) -p.set_array(np.array(colors)) -ax.add_collection(p) -fig.colorbar(p, ax=ax) - -plt.show() diff --git a/examples/api/power_norm_demo.py b/examples/api/power_norm_demo.py deleted file mode 100755 index eead07730902..000000000000 --- a/examples/api/power_norm_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -from matplotlib import pyplot as plt -import matplotlib.colors as mcolors -import numpy as np -from numpy.random import multivariate_normal - -data = np.vstack([ - multivariate_normal([10, 10], [[3, 2], [2, 3]], size=100000), - multivariate_normal([30, 20], [[2, 3], [1, 3]], size=1000) -]) - -gammas = [0.8, 0.5, 0.3] - -fig, axes = plt.subplots(nrows=2, ncols=2) - -axes[0, 0].set_title('Linear normalization') -axes[0, 0].hist2d(data[:, 0], data[:, 1], bins=100) - -for ax, gamma in zip(axes.flat[1:], gammas): - ax.set_title('Power law $(\gamma=%1.1f)$' % gamma) - ax.hist2d(data[:, 0], data[:, 1], - bins=100, norm=mcolors.PowerNorm(gamma)) - -fig.tight_layout() - -plt.show() diff --git a/examples/api/quad_bezier.py b/examples/api/quad_bezier.py deleted file mode 100644 index 0816bb0a8d8e..000000000000 --- a/examples/api/quad_bezier.py +++ /dev/null @@ -1,17 +0,0 @@ -import matplotlib.path as mpath -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - -Path = mpath.Path - -fig, ax = plt.subplots() -pp1 = mpatches.PathPatch( - Path([(0, 0), (1, 0), (1, 1), (0, 0)], - [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]), - fc="none", transform=ax.transData) - -ax.add_patch(pp1) -ax.plot([0.75], [0.25], "ro") -ax.set_title('The red point should be on the path') - -plt.show() diff --git a/examples/api/radar_chart.py b/examples/api/radar_chart.py deleted file mode 100644 index b52476761479..000000000000 --- a/examples/api/radar_chart.py +++ /dev/null @@ -1,196 +0,0 @@ -""" -Example of creating a radar chart (a.k.a. a spider or star chart) [1]_. - -Although this example allows a frame of either 'circle' or 'polygon', polygon -frames don't have proper gridlines (the lines are circles instead of polygons). -It's possible to get a polygon grid by setting GRIDLINE_INTERPOLATION_STEPS in -matplotlib.axis to the desired number of vertices, but the orientation of the -polygon is not aligned with the radial axes. - -.. [1] http://en.wikipedia.org/wiki/Radar_chart -""" -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib.path import Path -from matplotlib.spines import Spine -from matplotlib.projections.polar import PolarAxes -from matplotlib.projections import register_projection - - -def radar_factory(num_vars, frame='circle'): - """Create a radar chart with `num_vars` axes. - - This function creates a RadarAxes projection and registers it. - - Parameters - ---------- - num_vars : int - Number of variables for radar chart. - frame : {'circle' | 'polygon'} - Shape of frame surrounding axes. - - """ - # calculate evenly-spaced axis angles - theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False) - # rotate theta such that the first axis is at the top - theta += np.pi/2 - - def draw_poly_patch(self): - verts = unit_poly_verts(theta) - return plt.Polygon(verts, closed=True, edgecolor='k') - - def draw_circle_patch(self): - # unit circle centered on (0.5, 0.5) - return plt.Circle((0.5, 0.5), 0.5) - - patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch} - if frame not in patch_dict: - raise ValueError('unknown value for `frame`: %s' % frame) - - class RadarAxes(PolarAxes): - - name = 'radar' - # use 1 line segment to connect specified points - RESOLUTION = 1 - # define draw_frame method - draw_patch = patch_dict[frame] - - def fill(self, *args, **kwargs): - """Override fill so that line is closed by default""" - closed = kwargs.pop('closed', True) - return super(RadarAxes, self).fill(closed=closed, *args, **kwargs) - - def plot(self, *args, **kwargs): - """Override plot so that line is closed by default""" - lines = super(RadarAxes, self).plot(*args, **kwargs) - for line in lines: - self._close_line(line) - - def _close_line(self, line): - x, y = line.get_data() - # FIXME: markers at x[0], y[0] get doubled-up - if x[0] != x[-1]: - x = np.concatenate((x, [x[0]])) - y = np.concatenate((y, [y[0]])) - line.set_data(x, y) - - def set_varlabels(self, labels): - self.set_thetagrids(np.degrees(theta), labels) - - def _gen_axes_patch(self): - return self.draw_patch() - - def _gen_axes_spines(self): - if frame == 'circle': - return PolarAxes._gen_axes_spines(self) - # The following is a hack to get the spines (i.e. the axes frame) - # to draw correctly for a polygon frame. - - # spine_type must be 'left', 'right', 'top', 'bottom', or `circle`. - spine_type = 'circle' - verts = unit_poly_verts(theta) - # close off polygon by repeating first vertex - verts.append(verts[0]) - path = Path(verts) - - spine = Spine(self, spine_type, path) - spine.set_transform(self.transAxes) - return {'polar': spine} - - register_projection(RadarAxes) - return theta - - -def unit_poly_verts(theta): - """Return vertices of polygon for subplot axes. - - This polygon is circumscribed by a unit circle centered at (0.5, 0.5) - """ - x0, y0, r = [0.5] * 3 - verts = [(r*np.cos(t) + x0, r*np.sin(t) + y0) for t in theta] - return verts - - -def example_data(): - # The following data is from the Denver Aerosol Sources and Health study. - # See doi:10.1016/j.atmosenv.2008.12.017 - # - # The data are pollution source profile estimates for five modeled - # pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical - # species. The radar charts are experimented with here to see if we can - # nicely visualize how the modeled source profiles change across four - # scenarios: - # 1) No gas-phase species present, just seven particulate counts on - # Sulfate - # Nitrate - # Elemental Carbon (EC) - # Organic Carbon fraction 1 (OC) - # Organic Carbon fraction 2 (OC2) - # Organic Carbon fraction 3 (OC3) - # Pyrolized Organic Carbon (OP) - # 2)Inclusion of gas-phase specie carbon monoxide (CO) - # 3)Inclusion of gas-phase specie ozone (O3). - # 4)Inclusion of both gas-phase speciesis present... - data = [ - ['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'], - ('Basecase', [ - [0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00], - [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00], - [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00], - [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00], - [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]), - ('With CO', [ - [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00], - [0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00], - [0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00], - [0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00], - [0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]), - ('With O3', [ - [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03], - [0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00], - [0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00], - [0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95], - [0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]), - ('CO & O3', [ - [0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01], - [0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00], - [0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00], - [0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88], - [0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]]) - ] - return data - - -if __name__ == '__main__': - N = 9 - theta = radar_factory(N, frame='polygon') - - data = example_data() - spoke_labels = data.pop(0) - - fig, axes = plt.subplots(figsize=(9, 9), nrows=2, ncols=2, - subplot_kw=dict(projection='radar')) - fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05) - - colors = ['b', 'r', 'g', 'm', 'y'] - # Plot the four cases from the example data on separate axes - for ax, (title, case_data) in zip(axes.flatten(), data): - ax.set_rgrids([0.2, 0.4, 0.6, 0.8]) - ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1), - horizontalalignment='center', verticalalignment='center') - for d, color in zip(case_data, colors): - ax.plot(theta, d, color=color) - ax.fill(theta, d, facecolor=color, alpha=0.25) - ax.set_varlabels(spoke_labels) - - # add legend relative to top-left plot - ax = axes[0, 0] - labels = ('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4', 'Factor 5') - legend = ax.legend(labels, loc=(0.9, .95), labelspacing=0.1, fontsize='small') - - fig.text(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios', - horizontalalignment='center', color='black', weight='bold', - size='large') - - plt.show() diff --git a/examples/api/sankey_demo_basics.py b/examples/api/sankey_demo_basics.py deleted file mode 100644 index ea7b6111df01..000000000000 --- a/examples/api/sankey_demo_basics.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Demonstrate the Sankey class by producing three basic diagrams. -""" -import numpy as np -import matplotlib.pyplot as plt - -from matplotlib.sankey import Sankey - - -# Example 1 -- Mostly defaults -# This demonstrates how to create a simple diagram by implicitly calling the -# Sankey.add() method and by appending finish() to the call to the class. -Sankey(flows=[0.25, 0.15, 0.60, -0.20, -0.15, -0.05, -0.50, -0.10], - labels=['', '', '', 'First', 'Second', 'Third', 'Fourth', 'Fifth'], - orientations=[-1, 1, 0, 1, 1, 1, 0, -1]).finish() -plt.title("The default settings produce a diagram like this.") -# Notice: -# 1. Axes weren't provided when Sankey() was instantiated, so they were -# created automatically. -# 2. The scale argument wasn't necessary since the data was already -# normalized. -# 3. By default, the lengths of the paths are justified. - -# Example 2 -# This demonstrates: -# 1. Setting one path longer than the others -# 2. Placing a label in the middle of the diagram -# 3. Using the scale argument to normalize the flows -# 4. Implicitly passing keyword arguments to PathPatch() -# 5. Changing the angle of the arrow heads -# 6. Changing the offset between the tips of the paths and their labels -# 7. Formatting the numbers in the path labels and the associated unit -# 8. Changing the appearance of the patch and the labels after the figure is -# created -fig = plt.figure() -ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], - title="Flow Diagram of a Widget") -sankey = Sankey(ax=ax, scale=0.01, offset=0.2, head_angle=180, - format='%.0f', unit='%') -sankey.add(flows=[25, 0, 60, -10, -20, -5, -15, -10, -40], - labels=['', '', '', 'First', 'Second', 'Third', 'Fourth', - 'Fifth', 'Hurray!'], - orientations=[-1, 1, 0, 1, 1, 1, -1, -1, 0], - pathlengths=[0.25, 0.25, 0.25, 0.25, 0.25, 0.6, 0.25, 0.25, - 0.25], - patchlabel="Widget\nA") # Arguments to matplotlib.patches.PathPatch() -diagrams = sankey.finish() -diagrams[0].texts[-1].set_color('r') -diagrams[0].text.set_fontweight('bold') -# Notice: -# 1. Since the sum of the flows is nonzero, the width of the trunk isn't -# uniform. If verbose.level is helpful (in matplotlibrc), a message is -# given in the terminal window. -# 2. The second flow doesn't appear because its value is zero. Again, if -# verbose.level is helpful, a message is given in the terminal window. - -# Example 3 -# This demonstrates: -# 1. Connecting two systems -# 2. Turning off the labels of the quantities -# 3. Adding a legend -fig = plt.figure() -ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], title="Two Systems") -flows = [0.25, 0.15, 0.60, -0.10, -0.05, -0.25, -0.15, -0.10, -0.35] -sankey = Sankey(ax=ax, unit=None) -sankey.add(flows=flows, label='one', - orientations=[-1, 1, 0, 1, 1, 1, -1, -1, 0]) -sankey.add(flows=[-0.25, 0.15, 0.1], label='two', - orientations=[-1, -1, -1], prior=0, connect=(0, 0)) -diagrams = sankey.finish() -diagrams[-1].patch.set_hatch('/') -plt.legend(loc='best') -# Notice that only one connection is specified, but the systems form a -# circuit since: (1) the lengths of the paths are justified and (2) the -# orientation and ordering of the flows is mirrored. - -plt.show() diff --git a/examples/api/sankey_demo_links.py b/examples/api/sankey_demo_links.py deleted file mode 100644 index f68fc251f811..000000000000 --- a/examples/api/sankey_demo_links.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Demonstrate/test the Sankey class by producing a long chain of connections. -""" - -from itertools import cycle - -import matplotlib.pyplot as plt -from matplotlib.sankey import Sankey - -links_per_side = 6 - - -def side(sankey, n=1): - """Generate a side chain.""" - prior = len(sankey.diagrams) - for i in range(0, 2*n, 2): - sankey.add(flows=[1, -1], orientations=[-1, -1], - patchlabel=str(prior + i), - prior=prior + i - 1, connect=(1, 0), alpha=0.5) - sankey.add(flows=[1, -1], orientations=[1, 1], - patchlabel=str(prior + i + 1), - prior=prior + i, connect=(1, 0), alpha=0.5) - - -def corner(sankey): - """Generate a corner link.""" - prior = len(sankey.diagrams) - sankey.add(flows=[1, -1], orientations=[0, 1], - patchlabel=str(prior), facecolor='k', - prior=prior - 1, connect=(1, 0), alpha=0.5) - - -fig = plt.figure() -ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], - title="Why would you want to do this?\n(But you could.)") -sankey = Sankey(ax=ax, unit=None) -sankey.add(flows=[1, -1], orientations=[0, 1], - patchlabel="0", facecolor='k', - rotation=45) -side(sankey, n=links_per_side) -corner(sankey) -side(sankey, n=links_per_side) -corner(sankey) -side(sankey, n=links_per_side) -corner(sankey) -side(sankey, n=links_per_side) -sankey.finish() -# Notice: -# 1. The alignment doesn't drift significantly (if at all; with 16007 -# subdiagrams there is still closure). -# 2. The first diagram is rotated 45 deg, so all other diagrams are rotated -# accordingly. - -plt.show() diff --git a/examples/api/sankey_demo_old.py b/examples/api/sankey_demo_old.py deleted file mode 100755 index 7bf01b690a3a..000000000000 --- a/examples/api/sankey_demo_old.py +++ /dev/null @@ -1,192 +0,0 @@ -from __future__ import print_function - -__author__ = "Yannick Copin " -__version__ = "Time-stamp: <10/02/2010 16:49 ycopin@lyopc548.in2p3.fr>" - -import numpy as np - - -def sankey(ax, - outputs=[100.], outlabels=None, - inputs=[100.], inlabels='', - dx=40, dy=10, outangle=45, w=3, inangle=30, offset=2, **kwargs): - """Draw a Sankey diagram. - - outputs: array of outputs, should sum up to 100% - outlabels: output labels (same length as outputs), - or None (use default labels) or '' (no labels) - inputs and inlabels: similar for inputs - dx: horizontal elongation - dy: vertical elongation - outangle: output arrow angle [deg] - w: output arrow shoulder - inangle: input dip angle - offset: text offset - **kwargs: propagated to Patch (e.g., fill=False) - - Return (patch,[intexts,outtexts]). - """ - import matplotlib.patches as mpatches - from matplotlib.path import Path - - outs = np.absolute(outputs) - outsigns = np.sign(outputs) - outsigns[-1] = 0 # Last output - - ins = np.absolute(inputs) - insigns = np.sign(inputs) - insigns[0] = 0 # First input - - assert sum(outs) == 100, "Outputs don't sum up to 100%" - assert sum(ins) == 100, "Inputs don't sum up to 100%" - - def add_output(path, loss, sign=1): - # Arrow tip height - h = (loss/2 + w) * np.tan(np.radians(outangle)) - move, (x, y) = path[-1] # Use last point as reference - if sign == 0: # Final loss (horizontal) - path.extend([(Path.LINETO, [x + dx, y]), - (Path.LINETO, [x + dx, y + w]), - (Path.LINETO, [x + dx + h, y - loss/2]), # Tip - (Path.LINETO, [x + dx, y - loss - w]), - (Path.LINETO, [x + dx, y - loss])]) - outtips.append((sign, path[-3][1])) - else: # Intermediate loss (vertical) - path.extend([(Path.CURVE4, [x + dx/2, y]), - (Path.CURVE4, [x + dx, y]), - (Path.CURVE4, [x + dx, y + sign*dy]), - (Path.LINETO, [x + dx - w, y + sign*dy]), - # Tip - (Path.LINETO, [ - x + dx + loss/2, y + sign*(dy + h)]), - (Path.LINETO, [x + dx + loss + w, y + sign*dy]), - (Path.LINETO, [x + dx + loss, y + sign*dy]), - (Path.CURVE3, [x + dx + loss, y - sign*loss]), - (Path.CURVE3, [x + dx/2 + loss, y - sign*loss])]) - outtips.append((sign, path[-5][1])) - - def add_input(path, gain, sign=1): - h = (gain / 2) * np.tan(np.radians(inangle)) # Dip depth - move, (x, y) = path[-1] # Use last point as reference - if sign == 0: # First gain (horizontal) - path.extend([(Path.LINETO, [x - dx, y]), - (Path.LINETO, [x - dx + h, y + gain/2]), # Dip - (Path.LINETO, [x - dx, y + gain])]) - xd, yd = path[-2][1] # Dip position - indips.append((sign, [xd - h, yd])) - else: # Intermediate gain (vertical) - path.extend([(Path.CURVE4, [x - dx/2, y]), - (Path.CURVE4, [x - dx, y]), - (Path.CURVE4, [x - dx, y + sign*dy]), - # Dip - (Path.LINETO, [ - x - dx - gain / 2, y + sign*(dy - h)]), - (Path.LINETO, [x - dx - gain, y + sign*dy]), - (Path.CURVE3, [x - dx - gain, y - sign*gain]), - (Path.CURVE3, [x - dx/2 - gain, y - sign*gain])]) - xd, yd = path[-4][1] # Dip position - indips.append((sign, [xd, yd + sign*h])) - - outtips = [] # Output arrow tip dir. and positions - urpath = [(Path.MOVETO, [0, 100])] # 1st point of upper right path - lrpath = [(Path.LINETO, [0, 0])] # 1st point of lower right path - for loss, sign in zip(outs, outsigns): - add_output(sign >= 0 and urpath or lrpath, loss, sign=sign) - - indips = [] # Input arrow tip dir. and positions - llpath = [(Path.LINETO, [0, 0])] # 1st point of lower left path - ulpath = [(Path.MOVETO, [0, 100])] # 1st point of upper left path - for gain, sign in reversed(list(zip(ins, insigns))): - add_input(sign <= 0 and llpath or ulpath, gain, sign=sign) - - def revert(path): - """A path is not just revertable by path[::-1] because of Bezier - curves.""" - rpath = [] - nextmove = Path.LINETO - for move, pos in path[::-1]: - rpath.append((nextmove, pos)) - nextmove = move - return rpath - - # Concatenate subpathes in correct order - path = urpath + revert(lrpath) + llpath + revert(ulpath) - - codes, verts = zip(*path) - verts = np.array(verts) - - # Path patch - path = Path(verts, codes) - patch = mpatches.PathPatch(path, **kwargs) - ax.add_patch(patch) - - if False: # DEBUG - print("urpath", urpath) - print("lrpath", revert(lrpath)) - print("llpath", llpath) - print("ulpath", revert(ulpath)) - xs, ys = zip(*verts) - ax.plot(xs, ys, 'go-') - - # Labels - - def set_labels(labels, values): - """Set or check labels according to values.""" - if labels == '': # No labels - return labels - elif labels is None: # Default labels - return ['%2d%%' % val for val in values] - else: - assert len(labels) == len(values) - return labels - - def put_labels(labels, positions, output=True): - """Put labels to positions.""" - texts = [] - lbls = output and labels or labels[::-1] - for i, label in enumerate(lbls): - s, (x, y) = positions[i] # Label direction and position - if s == 0: - t = ax.text(x + offset, y, label, - ha=output and 'left' or 'right', va='center') - elif s > 0: - t = ax.text(x, y + offset, label, ha='center', va='bottom') - else: - t = ax.text(x, y - offset, label, ha='center', va='top') - texts.append(t) - return texts - - outlabels = set_labels(outlabels, outs) - outtexts = put_labels(outlabels, outtips, output=True) - - inlabels = set_labels(inlabels, ins) - intexts = put_labels(inlabels, indips, output=False) - - # Axes management - ax.set_xlim(verts[:, 0].min() - dx, verts[:, 0].max() + dx) - ax.set_ylim(verts[:, 1].min() - dy, verts[:, 1].max() + dy) - ax.set_aspect('equal', adjustable='datalim') - - return patch, [intexts, outtexts] - - -if __name__ == '__main__': - - import matplotlib.pyplot as plt - - outputs = [10., -20., 5., 15., -10., 40.] - outlabels = ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Hurray!'] - outlabels = [s + '\n%d%%' % abs(l) for l, s in zip(outputs, outlabels)] - - inputs = [60., -25., 15.] - - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], title="Sankey diagram") - - patch, (intexts, outtexts) = sankey(ax, outputs=outputs, - outlabels=outlabels, inputs=inputs, - inlabels=None) - outtexts[1].set_color('r') - outtexts[-1].set_fontweight('bold') - - plt.show() diff --git a/examples/api/sankey_demo_rankine.py b/examples/api/sankey_demo_rankine.py deleted file mode 100644 index ccf1ccc4a1c9..000000000000 --- a/examples/api/sankey_demo_rankine.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Demonstrate the Sankey class with a practicle example of a Rankine power -cycle. - -""" -import matplotlib.pyplot as plt - -from matplotlib.sankey import Sankey - -fig = plt.figure(figsize=(8, 9)) -ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], - title="Rankine Power Cycle: Example 8.6 from Moran and " - "Shapiro\n\x22Fundamentals of Engineering Thermodynamics " - "\x22, 6th ed., 2008") -Hdot = [260.431, 35.078, 180.794, 221.115, 22.700, - 142.361, 10.193, 10.210, 43.670, 44.312, - 68.631, 10.758, 10.758, 0.017, 0.642, - 232.121, 44.559, 100.613, 132.168] # MW -sankey = Sankey(ax=ax, format='%.3G', unit=' MW', gap=0.5, scale=1.0/Hdot[0]) -sankey.add(patchlabel='\n\nPump 1', rotation=90, facecolor='#37c959', - flows=[Hdot[13], Hdot[6], -Hdot[7]], - labels=['Shaft power', '', None], - pathlengths=[0.4, 0.883, 0.25], - orientations=[1, -1, 0]) -sankey.add(patchlabel='\n\nOpen\nheater', facecolor='#37c959', - flows=[Hdot[11], Hdot[7], Hdot[4], -Hdot[8]], - labels=[None, '', None, None], - pathlengths=[0.25, 0.25, 1.93, 0.25], - orientations=[1, 0, -1, 0], prior=0, connect=(2, 1)) -sankey.add(patchlabel='\n\nPump 2', facecolor='#37c959', - flows=[Hdot[14], Hdot[8], -Hdot[9]], - labels=['Shaft power', '', None], - pathlengths=[0.4, 0.25, 0.25], - orientations=[1, 0, 0], prior=1, connect=(3, 1)) -sankey.add(patchlabel='Closed\nheater', trunklength=2.914, fc='#37c959', - flows=[Hdot[9], Hdot[1], -Hdot[11], -Hdot[10]], - pathlengths=[0.25, 1.543, 0.25, 0.25], - labels=['', '', None, None], - orientations=[0, -1, 1, -1], prior=2, connect=(2, 0)) -sankey.add(patchlabel='Trap', facecolor='#37c959', trunklength=5.102, - flows=[Hdot[11], -Hdot[12]], - labels=['\n', None], - pathlengths=[1.0, 1.01], - orientations=[1, 1], prior=3, connect=(2, 0)) -sankey.add(patchlabel='Steam\ngenerator', facecolor='#ff5555', - flows=[Hdot[15], Hdot[10], Hdot[2], -Hdot[3], -Hdot[0]], - labels=['Heat rate', '', '', None, None], - pathlengths=0.25, - orientations=[1, 0, -1, -1, -1], prior=3, connect=(3, 1)) -sankey.add(patchlabel='\n\n\nTurbine 1', facecolor='#37c959', - flows=[Hdot[0], -Hdot[16], -Hdot[1], -Hdot[2]], - labels=['', None, None, None], - pathlengths=[0.25, 0.153, 1.543, 0.25], - orientations=[0, 1, -1, -1], prior=5, connect=(4, 0)) -sankey.add(patchlabel='\n\n\nReheat', facecolor='#37c959', - flows=[Hdot[2], -Hdot[2]], - labels=[None, None], - pathlengths=[0.725, 0.25], - orientations=[-1, 0], prior=6, connect=(3, 0)) -sankey.add(patchlabel='Turbine 2', trunklength=3.212, facecolor='#37c959', - flows=[Hdot[3], Hdot[16], -Hdot[5], -Hdot[4], -Hdot[17]], - labels=[None, 'Shaft power', None, '', 'Shaft power'], - pathlengths=[0.751, 0.15, 0.25, 1.93, 0.25], - orientations=[0, -1, 0, -1, 1], prior=6, connect=(1, 1)) -sankey.add(patchlabel='Condenser', facecolor='#58b1fa', trunklength=1.764, - flows=[Hdot[5], -Hdot[18], -Hdot[6]], - labels=['', 'Heat rate', None], - pathlengths=[0.45, 0.25, 0.883], - orientations=[-1, 1, 0], prior=8, connect=(2, 0)) -diagrams = sankey.finish() -for diagram in diagrams: - diagram.text.set_fontweight('bold') - diagram.text.set_fontsize('10') - for text in diagram.texts: - text.set_fontsize('10') -# Notice that the explicit connections are handled automatically, but the -# implicit ones currently are not. The lengths of the paths and the trunks -# must be adjusted manually, and that is a bit tricky. - -plt.show() diff --git a/examples/api/scatter_piecharts.py b/examples/api/scatter_piecharts.py deleted file mode 100644 index 2c02294ff166..000000000000 --- a/examples/api/scatter_piecharts.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -This example makes custom 'pie charts' as the markers for a scatter plotqu - -Thanks to Manuel Metz for the example -""" -import math -import numpy as np -import matplotlib.pyplot as plt - -# first define the ratios -r1 = 0.2 # 20% -r2 = r1 + 0.4 # 40% - -# define some sizes of the scatter marker -sizes = [60, 80, 120] - -# calculate the points of the first pie marker -# -# these are just the origin (0,0) + -# some points on a circle cos,sin -x = [0] + np.cos(np.linspace(0, 2*math.pi*r1, 10)).tolist() -y = [0] + np.sin(np.linspace(0, 2*math.pi*r1, 10)).tolist() - -xy1 = list(zip(x, y)) -s1 = max(max(x), max(y)) - -# ... -x = [0] + np.cos(np.linspace(2*math.pi*r1, 2*math.pi*r2, 10)).tolist() -y = [0] + np.sin(np.linspace(2*math.pi*r1, 2*math.pi*r2, 10)).tolist() -xy2 = list(zip(x, y)) -s2 = max(max(x), max(y)) - -x = [0] + np.cos(np.linspace(2*math.pi*r2, 2*math.pi, 10)).tolist() -y = [0] + np.sin(np.linspace(2*math.pi*r2, 2*math.pi, 10)).tolist() -xy3 = list(zip(x, y)) -s3 = max(max(x), max(y)) - -fig, ax = plt.subplots() -ax.scatter(np.arange(3), np.arange(3), marker=(xy1, 0), - s=[s1*s1*_ for _ in sizes], facecolor='blue') -ax.scatter(np.arange(3), np.arange(3), marker=(xy2, 0), - s=[s2*s2*_ for _ in sizes], facecolor='green') -ax.scatter(np.arange(3), np.arange(3), marker=(xy3, 0), - s=[s3*s3*_ for _ in sizes], facecolor='red') - -plt.show() diff --git a/examples/api/skewt.py b/examples/api/skewt.py deleted file mode 100644 index 0eca185e6df9..000000000000 --- a/examples/api/skewt.py +++ /dev/null @@ -1,255 +0,0 @@ -# This serves as an intensive exercise of matplotlib's transforms -# and custom projection API. This example produces a so-called -# SkewT-logP diagram, which is a common plot in meteorology for -# displaying vertical profiles of temperature. As far as matplotlib is -# concerned, the complexity comes from having X and Y axes that are -# not orthogonal. This is handled by including a skew component to the -# basic Axes transforms. Additional complexity comes in handling the -# fact that the upper and lower X-axes have different data ranges, which -# necessitates a bunch of custom classes for ticks,spines, and the axis -# to handle this. - -from matplotlib.axes import Axes -import matplotlib.transforms as transforms -import matplotlib.axis as maxis -import matplotlib.spines as mspines -import matplotlib.path as mpath -from matplotlib.projections import register_projection - -# The sole purpose of this class is to look at the upper, lower, or total -# interval as appropriate and see what parts of the tick to draw, if any. - - -class SkewXTick(maxis.XTick): - def draw(self, renderer): - if not self.get_visible(): - return - renderer.open_group(self.__name__) - - lower_interval = self.axes.xaxis.lower_interval - upper_interval = self.axes.xaxis.upper_interval - - if self.gridOn and transforms.interval_contains( - self.axes.xaxis.get_view_interval(), self.get_loc()): - self.gridline.draw(renderer) - - if transforms.interval_contains(lower_interval, self.get_loc()): - if self.tick1On: - self.tick1line.draw(renderer) - if self.label1On: - self.label1.draw(renderer) - - if transforms.interval_contains(upper_interval, self.get_loc()): - if self.tick2On: - self.tick2line.draw(renderer) - if self.label2On: - self.label2.draw(renderer) - - renderer.close_group(self.__name__) - - -# This class exists to provide two separate sets of intervals to the tick, -# as well as create instances of the custom tick -class SkewXAxis(maxis.XAxis): - def __init__(self, *args, **kwargs): - maxis.XAxis.__init__(self, *args, **kwargs) - self.upper_interval = 0.0, 1.0 - - def _get_tick(self, major): - return SkewXTick(self.axes, 0, '', major=major) - - @property - def lower_interval(self): - return self.axes.viewLim.intervalx - - def get_view_interval(self): - return self.upper_interval[0], self.axes.viewLim.intervalx[1] - - -# This class exists to calculate the separate data range of the -# upper X-axis and draw the spine there. It also provides this range -# to the X-axis artist for ticking and gridlines -class SkewSpine(mspines.Spine): - def _adjust_location(self): - trans = self.axes.transDataToAxes.inverted() - if self.spine_type == 'top': - yloc = 1.0 - else: - yloc = 0.0 - left = trans.transform_point((0.0, yloc))[0] - right = trans.transform_point((1.0, yloc))[0] - - pts = self._path.vertices - pts[0, 0] = left - pts[1, 0] = right - self.axis.upper_interval = (left, right) - - -# This class handles registration of the skew-xaxes as a projection as well -# as setting up the appropriate transformations. It also overrides standard -# spines and axes instances as appropriate. -class SkewXAxes(Axes): - # The projection must specify a name. This will be used be the - # user to select the projection, i.e. ``subplot(111, - # projection='skewx')``. - name = 'skewx' - - def _init_axis(self): - # Taken from Axes and modified to use our modified X-axis - self.xaxis = SkewXAxis(self) - self.spines['top'].register_axis(self.xaxis) - self.spines['bottom'].register_axis(self.xaxis) - self.yaxis = maxis.YAxis(self) - self.spines['left'].register_axis(self.yaxis) - self.spines['right'].register_axis(self.yaxis) - - def _gen_axes_spines(self): - spines = {'top': SkewSpine.linear_spine(self, 'top'), - 'bottom': mspines.Spine.linear_spine(self, 'bottom'), - 'left': mspines.Spine.linear_spine(self, 'left'), - 'right': mspines.Spine.linear_spine(self, 'right')} - return spines - - def _set_lim_and_transforms(self): - """ - This is called once when the plot is created to set up all the - transforms for the data, text and grids. - """ - rot = 30 - - # Get the standard transform setup from the Axes base class - Axes._set_lim_and_transforms(self) - - # Need to put the skew in the middle, after the scale and limits, - # but before the transAxes. This way, the skew is done in Axes - # coordinates thus performing the transform around the proper origin - # We keep the pre-transAxes transform around for other users, like the - # spines for finding bounds - self.transDataToAxes = self.transScale + \ - self.transLimits + transforms.Affine2D().skew_deg(rot, 0) - - # Create the full transform from Data to Pixels - self.transData = self.transDataToAxes + self.transAxes - - # Blended transforms like this need to have the skewing applied using - # both axes, in axes coords like before. - self._xaxis_transform = (transforms.blended_transform_factory( - self.transScale + self.transLimits, - transforms.IdentityTransform()) + - transforms.Affine2D().skew_deg(rot, 0)) + self.transAxes - -# Now register the projection with matplotlib so the user can select -# it. -register_projection(SkewXAxes) - -if __name__ == '__main__': - # Now make a simple example using the custom projection. - from matplotlib.ticker import ScalarFormatter, MultipleLocator - import matplotlib.pyplot as plt - from six import StringIO - import numpy as np - - # Some examples data - data_txt = ''' - 978.0 345 7.8 0.8 61 4.16 325 14 282.7 294.6 283.4 - 971.0 404 7.2 0.2 61 4.01 327 17 282.7 294.2 283.4 - 946.7 610 5.2 -1.8 61 3.56 335 26 282.8 293.0 283.4 - 944.0 634 5.0 -2.0 61 3.51 336 27 282.8 292.9 283.4 - 925.0 798 3.4 -2.6 65 3.43 340 32 282.8 292.7 283.4 - 911.8 914 2.4 -2.7 69 3.46 345 37 282.9 292.9 283.5 - 906.0 966 2.0 -2.7 71 3.47 348 39 283.0 293.0 283.6 - 877.9 1219 0.4 -3.2 77 3.46 0 48 283.9 293.9 284.5 - 850.0 1478 -1.3 -3.7 84 3.44 0 47 284.8 294.8 285.4 - 841.0 1563 -1.9 -3.8 87 3.45 358 45 285.0 295.0 285.6 - 823.0 1736 1.4 -0.7 86 4.44 353 42 290.3 303.3 291.0 - 813.6 1829 4.5 1.2 80 5.17 350 40 294.5 309.8 295.4 - 809.0 1875 6.0 2.2 77 5.57 347 39 296.6 313.2 297.6 - 798.0 1988 7.4 -0.6 57 4.61 340 35 299.2 313.3 300.1 - 791.0 2061 7.6 -1.4 53 4.39 335 33 300.2 313.6 301.0 - 783.9 2134 7.0 -1.7 54 4.32 330 31 300.4 313.6 301.2 - 755.1 2438 4.8 -3.1 57 4.06 300 24 301.2 313.7 301.9 - 727.3 2743 2.5 -4.4 60 3.81 285 29 301.9 313.8 302.6 - 700.5 3048 0.2 -5.8 64 3.57 275 31 302.7 313.8 303.3 - 700.0 3054 0.2 -5.8 64 3.56 280 31 302.7 313.8 303.3 - 698.0 3077 0.0 -6.0 64 3.52 280 31 302.7 313.7 303.4 - 687.0 3204 -0.1 -7.1 59 3.28 281 31 304.0 314.3 304.6 - 648.9 3658 -3.2 -10.9 55 2.59 285 30 305.5 313.8 305.9 - 631.0 3881 -4.7 -12.7 54 2.29 289 33 306.2 313.6 306.6 - 600.7 4267 -6.4 -16.7 44 1.73 295 39 308.6 314.3 308.9 - 592.0 4381 -6.9 -17.9 41 1.59 297 41 309.3 314.6 309.6 - 577.6 4572 -8.1 -19.6 39 1.41 300 44 310.1 314.9 310.3 - 555.3 4877 -10.0 -22.3 36 1.16 295 39 311.3 315.3 311.5 - 536.0 5151 -11.7 -24.7 33 0.97 304 39 312.4 315.8 312.6 - 533.8 5182 -11.9 -25.0 33 0.95 305 39 312.5 315.8 312.7 - 500.0 5680 -15.9 -29.9 29 0.64 290 44 313.6 315.9 313.7 - 472.3 6096 -19.7 -33.4 28 0.49 285 46 314.1 315.8 314.1 - 453.0 6401 -22.4 -36.0 28 0.39 300 50 314.4 315.8 314.4 - 400.0 7310 -30.7 -43.7 27 0.20 285 44 315.0 315.8 315.0 - 399.7 7315 -30.8 -43.8 27 0.20 285 44 315.0 315.8 315.0 - 387.0 7543 -33.1 -46.1 26 0.16 281 47 314.9 315.5 314.9 - 382.7 7620 -33.8 -46.8 26 0.15 280 48 315.0 315.6 315.0 - 342.0 8398 -40.5 -53.5 23 0.08 293 52 316.1 316.4 316.1 - 320.4 8839 -43.7 -56.7 22 0.06 300 54 317.6 317.8 317.6 - 318.0 8890 -44.1 -57.1 22 0.05 301 55 317.8 318.0 317.8 - 310.0 9060 -44.7 -58.7 19 0.04 304 61 319.2 319.4 319.2 - 306.1 9144 -43.9 -57.9 20 0.05 305 63 321.5 321.7 321.5 - 305.0 9169 -43.7 -57.7 20 0.05 303 63 322.1 322.4 322.1 - 300.0 9280 -43.5 -57.5 20 0.05 295 64 323.9 324.2 323.9 - 292.0 9462 -43.7 -58.7 17 0.05 293 67 326.2 326.4 326.2 - 276.0 9838 -47.1 -62.1 16 0.03 290 74 326.6 326.7 326.6 - 264.0 10132 -47.5 -62.5 16 0.03 288 79 330.1 330.3 330.1 - 251.0 10464 -49.7 -64.7 16 0.03 285 85 331.7 331.8 331.7 - 250.0 10490 -49.7 -64.7 16 0.03 285 85 332.1 332.2 332.1 - 247.0 10569 -48.7 -63.7 16 0.03 283 88 334.7 334.8 334.7 - 244.0 10649 -48.9 -63.9 16 0.03 280 91 335.6 335.7 335.6 - 243.3 10668 -48.9 -63.9 16 0.03 280 91 335.8 335.9 335.8 - 220.0 11327 -50.3 -65.3 15 0.03 280 85 343.5 343.6 343.5 - 212.0 11569 -50.5 -65.5 15 0.03 280 83 346.8 346.9 346.8 - 210.0 11631 -49.7 -64.7 16 0.03 280 83 349.0 349.1 349.0 - 200.0 11950 -49.9 -64.9 15 0.03 280 80 353.6 353.7 353.6 - 194.0 12149 -49.9 -64.9 15 0.03 279 78 356.7 356.8 356.7 - 183.0 12529 -51.3 -66.3 15 0.03 278 75 360.4 360.5 360.4 - 164.0 13233 -55.3 -68.3 18 0.02 277 69 365.2 365.3 365.2 - 152.0 13716 -56.5 -69.5 18 0.02 275 65 371.1 371.2 371.1 - 150.0 13800 -57.1 -70.1 18 0.02 275 64 371.5 371.6 371.5 - 136.0 14414 -60.5 -72.5 19 0.02 268 54 376.0 376.1 376.0 - 132.0 14600 -60.1 -72.1 19 0.02 265 51 380.0 380.1 380.0 - 131.4 14630 -60.2 -72.2 19 0.02 265 51 380.3 380.4 380.3 - 128.0 14792 -60.9 -72.9 19 0.02 266 50 381.9 382.0 381.9 - 125.0 14939 -60.1 -72.1 19 0.02 268 49 385.9 386.0 385.9 - 119.0 15240 -62.2 -73.8 20 0.01 270 48 387.4 387.5 387.4 - 112.0 15616 -64.9 -75.9 21 0.01 265 53 389.3 389.3 389.3 - 108.0 15838 -64.1 -75.1 21 0.01 265 58 394.8 394.9 394.8 - 107.8 15850 -64.1 -75.1 21 0.01 265 58 395.0 395.1 395.0 - 105.0 16010 -64.7 -75.7 21 0.01 272 50 396.9 396.9 396.9 - 103.0 16128 -62.9 -73.9 21 0.02 277 45 402.5 402.6 402.5 - 100.0 16310 -62.5 -73.5 21 0.02 285 36 406.7 406.8 406.7 - ''' - - # Parse the data - sound_data = StringIO(data_txt) - p, h, T, Td = np.loadtxt(sound_data, usecols=range(0, 4), unpack=True) - - # Create a new figure. The dimensions here give a good aspect ratio - fig = plt.figure(figsize=(6.5875, 6.2125)) - ax = fig.add_subplot(111, projection='skewx') - - plt.grid(True) - - # Plot the data using normal plotting functions, in this case using - # log scaling in Y, as dicatated by the typical meteorological plot - ax.semilogy(T, p) - ax.semilogy(Td, p) - - # An example of a slanted line at constant X - l = ax.axvline(0) - - # Disables the log-formatting that comes with semilogy - ax.yaxis.set_major_formatter(ScalarFormatter()) - ax.set_yticks(np.linspace(100, 1000, 10)) - ax.set_ylim(1050, 100) - - ax.xaxis.set_major_locator(MultipleLocator(10)) - ax.set_xlim(-50, 50) - - plt.show() diff --git a/examples/api/span_regions.py b/examples/api/span_regions.py deleted file mode 100644 index bf23ffb07379..000000000000 --- a/examples/api/span_regions.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Illustrate some helper functions for shading regions where a logical -mask is True - -See :meth:`matplotlib.collections.BrokenBarHCollection.span_where` -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.collections as collections - - -t = np.arange(0.0, 2, 0.01) -s1 = np.sin(2*np.pi*t) -s2 = 1.2*np.sin(4*np.pi*t) - - -fig, ax = plt.subplots() -ax.set_title('using span_where') -ax.plot(t, s1, color='black') -ax.axhline(0, color='black', lw=2) - -collection = collections.BrokenBarHCollection.span_where( - t, ymin=0, ymax=1, where=s1 > 0, facecolor='green', alpha=0.5) -ax.add_collection(collection) - -collection = collections.BrokenBarHCollection.span_where( - t, ymin=-1, ymax=0, where=s1 < 0, facecolor='red', alpha=0.5) -ax.add_collection(collection) - - -plt.show() diff --git a/examples/api/two_scales.py b/examples/api/two_scales.py deleted file mode 100644 index 8f960b3e95c8..000000000000 --- a/examples/api/two_scales.py +++ /dev/null @@ -1,43 +0,0 @@ -""" - -Demonstrate how to do two plots on the same axes with different left -right scales. - - -The trick is to use *2 different axes*. Turn the axes rectangular -frame off on the 2nd axes to keep it from obscuring the first. -Manually set the tick locs and labels as desired. You can use -separate matplotlib.ticker formatters and locators as desired since -the two axes are independent. - -This is achieved in the following example by calling the Axes.twinx() -method, which performs this work. See the source of twinx() in -axes.py for an example of how to do it for different x scales. (Hint: -use the xaxis instance and call tick_bottom and tick_top in place of -tick_left and tick_right.) - -The twinx and twiny methods are also exposed as pyplot functions. - -""" - -import numpy as np -import matplotlib.pyplot as plt - -fig, ax1 = plt.subplots() -t = np.arange(0.01, 10.0, 0.01) -s1 = np.exp(t) -ax1.plot(t, s1, 'b-') -ax1.set_xlabel('time (s)') -# Make the y-axis label and tick labels match the line color. -ax1.set_ylabel('exp', color='b') -for tl in ax1.get_yticklabels(): - tl.set_color('b') - - -ax2 = ax1.twinx() -s2 = np.sin(2*np.pi*t) -ax2.plot(t, s2, 'r.') -ax2.set_ylabel('sin', color='r') -for tl in ax2.get_yticklabels(): - tl.set_color('r') -plt.show() diff --git a/examples/api/unicode_minus.py b/examples/api/unicode_minus.py deleted file mode 100644 index d84fb04f5b03..000000000000 --- a/examples/api/unicode_minus.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -You can use the proper typesetting unicode minus (see -http://en.wikipedia.org/wiki/Plus_sign#Plus_sign) or the ASCII hypen -for minus, which some people prefer. The matplotlibrc param -axes.unicode_minus controls the default behavior. - -The default is to use the unicode minus -""" -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - -matplotlib.rcParams['axes.unicode_minus'] = False -fig, ax = plt.subplots() -ax.plot(10*np.random.randn(100), 10*np.random.randn(100), 'o') -ax.set_title('Using hypen instead of unicode minus') -plt.show() diff --git a/examples/api/watermark_image.py b/examples/api/watermark_image.py deleted file mode 100644 index 911f97067d55..000000000000 --- a/examples/api/watermark_image.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Watermark image example - -Use a PNG file as a watermark -""" -from __future__ import print_function -import numpy as np -import matplotlib.cbook as cbook -import matplotlib.image as image -import matplotlib.pyplot as plt - -datafile = cbook.get_sample_data('logo2.png', asfileobj=False) -print('loading %s' % datafile) -im = image.imread(datafile) -im[:, :, -1] = 0.5 # set the alpha channel - -fig, ax = plt.subplots() - -ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7, mfc='orange') -ax.grid() -fig.figimage(im, 10, 10, zorder=3) - -plt.show() diff --git a/examples/api/watermark_text.py b/examples/api/watermark_text.py deleted file mode 100644 index 0d09141050a9..000000000000 --- a/examples/api/watermark_text.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Use a Text as a watermark -""" -import numpy as np -#import matplotlib -#matplotlib.use('Agg') - -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7, mfc='orange') -ax.grid() - -# position bottom right -fig.text(0.95, 0.05, 'Property of MPL', - fontsize=50, color='gray', - ha='right', va='bottom', alpha=0.5) - -plt.show() diff --git a/examples/axes_grid1/demo_axes_divider.py b/examples/axes_grid1/demo_axes_divider.py deleted file mode 100644 index d69b1d745dc2..000000000000 --- a/examples/axes_grid1/demo_axes_divider.py +++ /dev/null @@ -1,128 +0,0 @@ -import matplotlib.pyplot as plt - - -def get_demo_image(): - import numpy as np - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - - -def demo_simple_image(ax): - Z, extent = get_demo_image() - - im = ax.imshow(Z, extent=extent, interpolation="nearest") - cb = plt.colorbar(im) - plt.setp(cb.ax.get_yticklabels(), visible=False) - - -def demo_locatable_axes_hard(fig1): - - from mpl_toolkits.axes_grid1 \ - import SubplotDivider, LocatableAxes, Size - - divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) - - # axes for image - ax = LocatableAxes(fig1, divider.get_position()) - - # axes for colorbar - ax_cb = LocatableAxes(fig1, divider.get_position()) - - h = [Size.AxesX(ax), # main axes - Size.Fixed(0.05), # padding, 0.1 inch - Size.Fixed(0.2), # colorbar, 0.3 inch - ] - - v = [Size.AxesY(ax)] - - divider.set_horizontal(h) - divider.set_vertical(v) - - ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) - ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) - - fig1.add_axes(ax) - fig1.add_axes(ax_cb) - - ax_cb.axis["left"].toggle(all=False) - ax_cb.axis["right"].toggle(ticks=True) - - Z, extent = get_demo_image() - - im = ax.imshow(Z, extent=extent, interpolation="nearest") - plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) - - -def demo_locatable_axes_easy(ax): - from mpl_toolkits.axes_grid1 import make_axes_locatable - - divider = make_axes_locatable(ax) - - ax_cb = divider.new_horizontal(size="5%", pad=0.05) - fig1 = ax.get_figure() - fig1.add_axes(ax_cb) - - Z, extent = get_demo_image() - im = ax.imshow(Z, extent=extent, interpolation="nearest") - - plt.colorbar(im, cax=ax_cb) - ax_cb.yaxis.tick_right() - for tl in ax_cb.get_yticklabels(): - tl.set_visible(False) - ax_cb.yaxis.tick_right() - - -def demo_images_side_by_side(ax): - from mpl_toolkits.axes_grid1 import make_axes_locatable - - divider = make_axes_locatable(ax) - - Z, extent = get_demo_image() - ax2 = divider.new_horizontal(size="100%", pad=0.05) - fig1 = ax.get_figure() - fig1.add_axes(ax2) - - ax.imshow(Z, extent=extent, interpolation="nearest") - ax2.imshow(Z, extent=extent, interpolation="nearest") - for tl in ax2.get_yticklabels(): - tl.set_visible(False) - - -def demo(): - - fig1 = plt.figure(1, (6, 6)) - fig1.clf() - - # PLOT 1 - # simple image & colorbar - ax = fig1.add_subplot(2, 2, 1) - demo_simple_image(ax) - - # PLOT 2 - # image and colorbar whose location is adjusted in the drawing time. - # a hard way - - demo_locatable_axes_hard(fig1) - - # PLOT 3 - # image and colorbar whose location is adjusted in the drawing time. - # a easy way - - ax = fig1.add_subplot(2, 2, 3) - demo_locatable_axes_easy(ax) - - # PLOT 4 - # two images side by side with fixed padding. - - ax = fig1.add_subplot(2, 2, 4) - demo_images_side_by_side(ax) - - plt.draw() - plt.show() - - -demo() diff --git a/examples/axes_grid1/demo_axes_grid.py b/examples/axes_grid1/demo_axes_grid.py deleted file mode 100644 index 01f78edbf927..000000000000 --- a/examples/axes_grid1/demo_axes_grid.py +++ /dev/null @@ -1,132 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import ImageGrid - - -def get_demo_image(): - import numpy as np - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - - -def demo_simple_grid(fig): - """ - A grid of 2x2 images with 0.05 inch pad between images and only - the lower-left axes is labeled. - """ - grid = ImageGrid(fig, 141, # similar to subplot(141) - nrows_ncols=(2, 2), - axes_pad=0.05, - label_mode="1", - ) - - Z, extent = get_demo_image() - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest") - - # This only affects axes in first column and second row as share_all = - # False. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -def demo_grid_with_single_cbar(fig): - """ - A grid of 2x2 images with a single colorbar - """ - grid = ImageGrid(fig, 142, # similar to subplot(142) - nrows_ncols=(2, 2), - axes_pad=0.0, - share_all=True, - label_mode="L", - cbar_location="top", - cbar_mode="single", - ) - - Z, extent = get_demo_image() - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest") - grid.cbar_axes[0].colorbar(im) - - for cax in grid.cbar_axes: - cax.toggle_label(False) - - # This affects all axes as share_all = True. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -def demo_grid_with_each_cbar(fig): - """ - A grid of 2x2 images. Each image has its own colorbar. - """ - - grid = ImageGrid(fig, 143, # similar to subplot(143) - nrows_ncols=(2, 2), - axes_pad=0.1, - label_mode="1", - share_all=True, - cbar_location="top", - cbar_mode="each", - cbar_size="7%", - cbar_pad="2%", - ) - Z, extent = get_demo_image() - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest") - grid.cbar_axes[i].colorbar(im) - - for cax in grid.cbar_axes: - cax.toggle_label(False) - - # This affects all axes because we set share_all = True. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -def demo_grid_with_each_cbar_labelled(fig): - """ - A grid of 2x2 images. Each image has its own colorbar. - """ - - grid = ImageGrid(fig, 144, # similar to subplot(144) - nrows_ncols=(2, 2), - axes_pad=(0.45, 0.15), - label_mode="1", - share_all=True, - cbar_location="right", - cbar_mode="each", - cbar_size="7%", - cbar_pad="2%", - ) - Z, extent = get_demo_image() - - # Use a different colorbar range every time - limits = ((0, 1), (-2, 2), (-1.7, 1.4), (-1.5, 1)) - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest", - vmin=limits[i][0], vmax=limits[i][1]) - grid.cbar_axes[i].colorbar(im) - - for i, cax in enumerate(grid.cbar_axes): - cax.set_yticks((limits[i][0], limits[i][1])) - - # This affects all axes because we set share_all = True. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -if 1: - F = plt.figure(1, (10.5, 2.5)) - - F.subplots_adjust(left=0.05, right=0.95) - - demo_simple_grid(F) - demo_grid_with_single_cbar(F) - demo_grid_with_each_cbar(F) - demo_grid_with_each_cbar_labelled(F) - - plt.draw() - plt.show() diff --git a/examples/axes_grid1/demo_axes_grid2.py b/examples/axes_grid1/demo_axes_grid2.py deleted file mode 100644 index 438991acd144..000000000000 --- a/examples/axes_grid1/demo_axes_grid2.py +++ /dev/null @@ -1,113 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import ImageGrid -import numpy as np - - -def get_demo_image(): - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - - -def add_inner_title(ax, title, loc, size=None, **kwargs): - from matplotlib.offsetbox import AnchoredText - from matplotlib.patheffects import withStroke - if size is None: - size = dict(size=plt.rcParams['legend.fontsize']) - at = AnchoredText(title, loc=loc, prop=size, - pad=0., borderpad=0.5, - frameon=False, **kwargs) - ax.add_artist(at) - at.txt._text.set_path_effects([withStroke(foreground="w", linewidth=3)]) - return at - -if 1: - F = plt.figure(1, (6, 6)) - F.clf() - - # prepare images - Z, extent = get_demo_image() - ZS = [Z[i::3, :] for i in range(3)] - extent = extent[0], extent[1]/3., extent[2], extent[3] - - # demo 1 : colorbar at each axes - - grid = ImageGrid(F, 211, # similar to subplot(111) - nrows_ncols=(1, 3), - direction="row", - axes_pad=0.05, - add_all=True, - label_mode="1", - share_all=True, - cbar_location="top", - cbar_mode="each", - cbar_size="7%", - cbar_pad="1%", - ) - - for ax, z in zip(grid, ZS): - im = ax.imshow( - z, origin="lower", extent=extent, interpolation="nearest") - ax.cax.colorbar(im) - - for ax, im_title in zip(grid, ["Image 1", "Image 2", "Image 3"]): - t = add_inner_title(ax, im_title, loc=3) - t.patch.set_alpha(0.5) - - for ax, z in zip(grid, ZS): - ax.cax.toggle_label(True) - #axis = ax.cax.axis[ax.cax.orientation] - #axis.label.set_text("counts s$^{-1}$") - #axis.label.set_size(10) - #axis.major_ticklabels.set_size(6) - - # changing the colorbar ticks - grid[1].cax.set_xticks([-1, 0, 1]) - grid[2].cax.set_xticks([-1, 0, 1]) - - grid[0].set_xticks([-2, 0]) - grid[0].set_yticks([-2, 0, 2]) - - # demo 2 : shared colorbar - - grid2 = ImageGrid(F, 212, - nrows_ncols=(1, 3), - direction="row", - axes_pad=0.05, - add_all=True, - label_mode="1", - share_all=True, - cbar_location="right", - cbar_mode="single", - cbar_size="10%", - cbar_pad=0.05, - ) - - grid2[0].set_xlabel("X") - grid2[0].set_ylabel("Y") - - vmax, vmin = np.max(ZS), np.min(ZS) - import matplotlib.colors - norm = matplotlib.colors.Normalize(vmax=vmax, vmin=vmin) - - for ax, z in zip(grid2, ZS): - im = ax.imshow(z, norm=norm, - origin="lower", extent=extent, - interpolation="nearest") - - # With cbar_mode="single", cax attribute of all axes are identical. - ax.cax.colorbar(im) - ax.cax.toggle_label(True) - - for ax, im_title in zip(grid2, ["(a)", "(b)", "(c)"]): - t = add_inner_title(ax, im_title, loc=2) - t.patch.set_ec("none") - t.patch.set_alpha(0.5) - - grid2[0].set_xticks([-2, 0]) - grid2[0].set_yticks([-2, 0, 2]) - - plt.draw() - plt.show() diff --git a/examples/axes_grid1/demo_axes_hbox_divider.py b/examples/axes_grid1/demo_axes_hbox_divider.py deleted file mode 100644 index 692ca8454c3e..000000000000 --- a/examples/axes_grid1/demo_axes_hbox_divider.py +++ /dev/null @@ -1,49 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1.axes_divider import HBoxDivider -import mpl_toolkits.axes_grid1.axes_size as Size - - -def make_heights_equal(fig, rect, ax1, ax2, pad): - # pad in inches - - h1, v1 = Size.AxesX(ax1), Size.AxesY(ax1) - h2, v2 = Size.AxesX(ax2), Size.AxesY(ax2) - - pad_v = Size.Scaled(1) - pad_h = Size.Fixed(pad) - - my_divider = HBoxDivider(fig, rect, - horizontal=[h1, pad_h, h2], - vertical=[v1, pad_v, v2]) - - ax1.set_axes_locator(my_divider.new_locator(0)) - ax2.set_axes_locator(my_divider.new_locator(2)) - - -if __name__ == "__main__": - - arr1 = np.arange(20).reshape((4, 5)) - arr2 = np.arange(20).reshape((5, 4)) - - fig, (ax1, ax2) = plt.subplots(1, 2) - ax1.imshow(arr1, interpolation="nearest") - ax2.imshow(arr2, interpolation="nearest") - - rect = 111 # subplot param for combined axes - make_heights_equal(fig, rect, ax1, ax2, pad=0.5) # pad in inches - - for ax in [ax1, ax2]: - ax.locator_params(nbins=4) - - # annotate - ax3 = plt.axes([0.5, 0.5, 0.001, 0.001], frameon=False) - ax3.xaxis.set_visible(False) - ax3.yaxis.set_visible(False) - ax3.annotate("Location of two axes are adjusted\n" - "so that they have equal heights\n" - "while maintaining their aspect ratios", (0.5, 0.5), - xycoords="axes fraction", va="center", ha="center", - bbox=dict(boxstyle="round, pad=1", fc="w")) - - plt.show() diff --git a/examples/axes_grid1/demo_axes_rgb.py b/examples/axes_grid1/demo_axes_rgb.py deleted file mode 100644 index c5569b52de98..000000000000 --- a/examples/axes_grid1/demo_axes_rgb.py +++ /dev/null @@ -1,84 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -from mpl_toolkits.axes_grid1.axes_rgb import make_rgb_axes, RGBAxes - - -def get_demo_image(): - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - - -def get_rgb(): - Z, extent = get_demo_image() - - Z[Z < 0] = 0. - Z = Z/Z.max() - - R = Z[:13, :13] - G = Z[2:, 2:] - B = Z[:13, 2:] - - return R, G, B - - -def make_cube(r, g, b): - ny, nx = r.shape - R = np.zeros([ny, nx, 3], dtype="d") - R[:, :, 0] = r - G = np.zeros_like(R) - G[:, :, 1] = g - B = np.zeros_like(R) - B[:, :, 2] = b - - RGB = R + G + B - - return R, G, B, RGB - - -def demo_rgb(): - fig, ax = plt.subplots() - ax_r, ax_g, ax_b = make_rgb_axes(ax, pad=0.02) - #fig.add_axes(ax_r) - #fig.add_axes(ax_g) - #fig.add_axes(ax_b) - - r, g, b = get_rgb() - im_r, im_g, im_b, im_rgb = make_cube(r, g, b) - kwargs = dict(origin="lower", interpolation="nearest") - ax.imshow(im_rgb, **kwargs) - ax_r.imshow(im_r, **kwargs) - ax_g.imshow(im_g, **kwargs) - ax_b.imshow(im_b, **kwargs) - - -def demo_rgb2(): - fig = plt.figure(2) - ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8], pad=0.0) - #fig.add_axes(ax) - #ax.add_RGB_to_figure() - - r, g, b = get_rgb() - kwargs = dict(origin="lower", interpolation="nearest") - ax.imshow_rgb(r, g, b, **kwargs) - - ax.RGB.set_xlim(0., 9.5) - ax.RGB.set_ylim(0.9, 10.6) - - for ax1 in [ax.RGB, ax.R, ax.G, ax.B]: - for sp1 in ax1.spines.values(): - sp1.set_color("w") - for tick in ax1.xaxis.get_major_ticks() + ax1.yaxis.get_major_ticks(): - tick.tick1line.set_mec("w") - tick.tick2line.set_mec("w") - - return ax - - -demo_rgb() -ax = demo_rgb2() - -plt.show() diff --git a/examples/axes_grid1/demo_colorbar_with_inset_locator.py b/examples/axes_grid1/demo_colorbar_with_inset_locator.py deleted file mode 100644 index 72a920b402c5..000000000000 --- a/examples/axes_grid1/demo_colorbar_with_inset_locator.py +++ /dev/null @@ -1,33 +0,0 @@ -import matplotlib.pyplot as plt - -from mpl_toolkits.axes_grid1.inset_locator import inset_axes - -fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[6, 3]) - -axins1 = inset_axes(ax1, - width="50%", # width = 10% of parent_bbox width - height="5%", # height : 50% - loc=1) - -im1 = ax1.imshow([[1, 2], [2, 3]]) -plt.colorbar(im1, cax=axins1, orientation="horizontal", ticks=[1, 2, 3]) -axins1.xaxis.set_ticks_position("bottom") - -axins = inset_axes(ax2, - width="5%", # width = 10% of parent_bbox width - height="50%", # height : 50% - loc=3, - bbox_to_anchor=(1.05, 0., 1, 1), - bbox_transform=ax2.transAxes, - borderpad=0, - ) - -# Controlling the placement of the inset axes is basically same as that -# of the legend. you may want to play with the borderpad value and -# the bbox_to_anchor coordinate. - -im = ax2.imshow([[1, 2], [2, 3]]) -plt.colorbar(im, cax=axins, ticks=[1, 2, 3]) - -plt.draw() -plt.show() diff --git a/examples/axes_grid1/demo_edge_colorbar.py b/examples/axes_grid1/demo_edge_colorbar.py deleted file mode 100644 index 7c57bb2fe379..000000000000 --- a/examples/axes_grid1/demo_edge_colorbar.py +++ /dev/null @@ -1,88 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import AxesGrid - - -def get_demo_image(): - import numpy as np - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - - -def demo_bottom_cbar(fig): - """ - A grid of 2x2 images with a colorbar for each column. - """ - grid = AxesGrid(fig, 121, # similar to subplot(132) - nrows_ncols=(2, 2), - axes_pad=0.10, - share_all=True, - label_mode="1", - cbar_location="bottom", - cbar_mode="edge", - cbar_pad=0.25, - cbar_size="15%", - direction="column" - ) - - Z, extent = get_demo_image() - cmaps = [plt.get_cmap("autumn"), plt.get_cmap("summer")] - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest", - cmap=cmaps[i//2]) - if i % 2: - cbar = grid.cbar_axes[i//2].colorbar(im) - - for cax in grid.cbar_axes: - cax.toggle_label(True) - cax.axis[cax.orientation].set_label("Bar") - - # This affects all axes as share_all = True. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -def demo_right_cbar(fig): - """ - A grid of 2x2 images. Each row has its own colorbar. - """ - - grid = AxesGrid(F, 122, # similar to subplot(122) - nrows_ncols=(2, 2), - axes_pad=0.10, - label_mode="1", - share_all=True, - cbar_location="right", - cbar_mode="edge", - cbar_size="7%", - cbar_pad="2%", - ) - Z, extent = get_demo_image() - cmaps = [plt.get_cmap("spring"), plt.get_cmap("winter")] - for i in range(4): - im = grid[i].imshow(Z, extent=extent, interpolation="nearest", - cmap=cmaps[i//2]) - if i % 2: - grid.cbar_axes[i//2].colorbar(im) - - for cax in grid.cbar_axes: - cax.toggle_label(True) - cax.axis[cax.orientation].set_label('Foo') - - # This affects all axes because we set share_all = True. - grid.axes_llc.set_xticks([-2, 0, 2]) - grid.axes_llc.set_yticks([-2, 0, 2]) - - -if 1: - F = plt.figure(1, (5.5, 2.5)) - - F.subplots_adjust(left=0.05, right=0.93) - - demo_bottom_cbar(F) - demo_right_cbar(F) - - plt.draw() - plt.show() diff --git a/examples/axes_grid1/demo_imagegrid_aspect.py b/examples/axes_grid1/demo_imagegrid_aspect.py deleted file mode 100644 index 79f7655753eb..000000000000 --- a/examples/axes_grid1/demo_imagegrid_aspect.py +++ /dev/null @@ -1,20 +0,0 @@ -import matplotlib.pyplot as plt - -from mpl_toolkits.axes_grid1 import ImageGrid -fig = plt.figure(1) - -grid1 = ImageGrid(fig, 121, (2, 2), axes_pad=0.1, - aspect=True, share_all=True) - -for i in [0, 1]: - grid1[i].set_aspect(2) - - -grid2 = ImageGrid(fig, 122, (2, 2), axes_pad=0.1, - aspect=True, share_all=True) - - -for i in [1, 3]: - grid2[i].set_aspect(2) - -plt.show() diff --git a/examples/axes_grid1/inset_locator_demo.py b/examples/axes_grid1/inset_locator_demo.py deleted file mode 100644 index 6b7582b3f5bc..000000000000 --- a/examples/axes_grid1/inset_locator_demo.py +++ /dev/null @@ -1,46 +0,0 @@ -import matplotlib.pyplot as plt - -from mpl_toolkits.axes_grid1.inset_locator import inset_axes, zoomed_inset_axes -from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar - - -def add_sizebar(ax, size): - asb = AnchoredSizeBar(ax.transData, - size, - str(size), - loc=8, - pad=0.1, borderpad=0.5, sep=5, - frameon=False) - ax.add_artist(asb) - - -fig, (ax, ax2) = plt.subplots(1, 2, figsize=[5.5, 3]) - -# first subplot -ax.set_aspect(1.) - -axins = inset_axes(ax, - width="30%", # width = 30% of parent_bbox - height=1., # height : 1 inch - loc=3) - -plt.xticks(visible=False) -plt.yticks(visible=False) - - -# second subplot -ax2.set_aspect(1.) - -axins = zoomed_inset_axes(ax2, 0.5, loc=1) # zoom = 0.5 -# fix the number of ticks on the inset axes -axins.yaxis.get_major_locator().set_params(nbins=7) -axins.xaxis.get_major_locator().set_params(nbins=7) - -plt.xticks(visible=False) -plt.yticks(visible=False) - -add_sizebar(ax2, 0.5) -add_sizebar(axins, 0.5) - -plt.draw() -plt.show() diff --git a/examples/axes_grid1/inset_locator_demo2.py b/examples/axes_grid1/inset_locator_demo2.py deleted file mode 100644 index 51374cbbc7c8..000000000000 --- a/examples/axes_grid1/inset_locator_demo2.py +++ /dev/null @@ -1,49 +0,0 @@ -import matplotlib.pyplot as plt - -from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes -from mpl_toolkits.axes_grid1.inset_locator import mark_inset - -import numpy as np - - -def get_demo_image(): - from matplotlib.cbook import get_sample_data - import numpy as np - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - -fig, ax = plt.subplots(figsize=[5, 4]) - -# prepare the demo image -Z, extent = get_demo_image() -Z2 = np.zeros([150, 150], dtype="d") -ny, nx = Z.shape -Z2[30:30 + ny, 30:30 + nx] = Z - -# extent = [-3, 4, -4, 3] -ax.imshow(Z2, extent=extent, interpolation="nearest", - origin="lower") - -axins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6 -axins.imshow(Z2, extent=extent, interpolation="nearest", - origin="lower") - -# sub region of the original image -x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 -axins.set_xlim(x1, x2) -axins.set_ylim(y1, y2) -# fix the number of ticks on the inset axes -axins.yaxis.get_major_locator().set_params(nbins=7) -axins.xaxis.get_major_locator().set_params(nbins=7) - -plt.xticks(visible=False) -plt.yticks(visible=False) - -# draw a bbox of the region of the inset axes in the parent axes and -# connecting lines between the bbox and the inset axes area -mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5") - -plt.draw() -plt.show() diff --git a/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py b/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py deleted file mode 100644 index fb609105a9da..000000000000 --- a/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py +++ /dev/null @@ -1,61 +0,0 @@ -from mpl_toolkits.axes_grid1 import make_axes_locatable -from mpl_toolkits.axes_grid1.axes_divider import make_axes_area_auto_adjustable - - -if __name__ == "__main__": - - import matplotlib.pyplot as plt - - def ex1(): - plt.figure(1) - ax = plt.axes([0, 0, 1, 1]) - #ax = plt.subplot(111) - - ax.set_yticks([0.5]) - ax.set_yticklabels(["very long label"]) - - make_axes_area_auto_adjustable(ax) - - def ex2(): - - plt.figure(2) - ax1 = plt.axes([0, 0, 1, 0.5]) - ax2 = plt.axes([0, 0.5, 1, 0.5]) - - ax1.set_yticks([0.5]) - ax1.set_yticklabels(["very long label"]) - ax1.set_ylabel("Y label") - - ax2.set_title("Title") - - make_axes_area_auto_adjustable(ax1, pad=0.1, use_axes=[ax1, ax2]) - make_axes_area_auto_adjustable(ax2, pad=0.1, use_axes=[ax1, ax2]) - - def ex3(): - - fig = plt.figure(3) - ax1 = plt.axes([0, 0, 1, 1]) - divider = make_axes_locatable(ax1) - - ax2 = divider.new_horizontal("100%", pad=0.3, sharey=ax1) - ax2.tick_params(labelleft="off") - fig.add_axes(ax2) - - divider.add_auto_adjustable_area(use_axes=[ax1], pad=0.1, - adjust_dirs=["left"]) - divider.add_auto_adjustable_area(use_axes=[ax2], pad=0.1, - adjust_dirs=["right"]) - divider.add_auto_adjustable_area(use_axes=[ax1, ax2], pad=0.1, - adjust_dirs=["top", "bottom"]) - - ax1.set_yticks([0.5]) - ax1.set_yticklabels(["very long label"]) - - ax2.set_title("Title") - ax2.set_xlabel("X - Label") - - ex1() - ex2() - ex3() - - plt.show() diff --git a/examples/axes_grid1/scatter_hist.py b/examples/axes_grid1/scatter_hist.py deleted file mode 100644 index ff6e1412c1fc..000000000000 --- a/examples/axes_grid1/scatter_hist.py +++ /dev/null @@ -1,51 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import make_axes_locatable - -# the random data -x = np.random.randn(1000) -y = np.random.randn(1000) - - -fig, axScatter = plt.subplots(figsize=(5.5, 5.5)) - -# the scatter plot: -axScatter.scatter(x, y) -axScatter.set_aspect(1.) - -# create new axes on the right and on the top of the current axes -# The first argument of the new_vertical(new_horizontal) method is -# the height (width) of the axes to be created in inches. -divider = make_axes_locatable(axScatter) -axHistx = divider.append_axes("top", 1.2, pad=0.1, sharex=axScatter) -axHisty = divider.append_axes("right", 1.2, pad=0.1, sharey=axScatter) - -# make some labels invisible -plt.setp(axHistx.get_xticklabels() + axHisty.get_yticklabels(), - visible=False) - -# now determine nice limits by hand: -binwidth = 0.25 -xymax = np.max([np.max(np.fabs(x)), np.max(np.fabs(y))]) -lim = (int(xymax/binwidth) + 1)*binwidth - -bins = np.arange(-lim, lim + binwidth, binwidth) -axHistx.hist(x, bins=bins) -axHisty.hist(y, bins=bins, orientation='horizontal') - -# the xaxis of axHistx and yaxis of axHisty are shared with axScatter, -# thus there is no need to manually adjust the xlim and ylim of these -# axis. - -#axHistx.axis["bottom"].major_ticklabels.set_visible(False) -for tl in axHistx.get_xticklabels(): - tl.set_visible(False) -axHistx.set_yticks([0, 50, 100]) - -#axHisty.axis["left"].major_ticklabels.set_visible(False) -for tl in axHisty.get_yticklabels(): - tl.set_visible(False) -axHisty.set_xticks([0, 50, 100]) - -plt.draw() -plt.show() diff --git a/examples/axes_grid1/simple_anchored_artists.py b/examples/axes_grid1/simple_anchored_artists.py deleted file mode 100644 index 26ef0f6a26a8..000000000000 --- a/examples/axes_grid1/simple_anchored_artists.py +++ /dev/null @@ -1,62 +0,0 @@ -import matplotlib.pyplot as plt - - -def draw_text(ax): - from matplotlib.offsetbox import AnchoredText - at = AnchoredText("Figure 1a", - loc=2, prop=dict(size=8), frameon=True, - ) - at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") - ax.add_artist(at) - - at2 = AnchoredText("Figure 1(b)", - loc=3, prop=dict(size=8), frameon=True, - bbox_to_anchor=(0., 1.), - bbox_transform=ax.transAxes - ) - at2.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") - ax.add_artist(at2) - - -def draw_circle(ax): # circle in the canvas coordinate - from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea - from matplotlib.patches import Circle - ada = AnchoredDrawingArea(20, 20, 0, 0, - loc=1, pad=0., frameon=False) - p = Circle((10, 10), 10) - ada.da.add_artist(p) - ax.add_artist(ada) - - -def draw_ellipse(ax): - from mpl_toolkits.axes_grid1.anchored_artists import AnchoredEllipse - # draw an ellipse of width=0.1, height=0.15 in the data coordinate - ae = AnchoredEllipse(ax.transData, width=0.1, height=0.15, angle=0., - loc=3, pad=0.5, borderpad=0.4, frameon=True) - - ax.add_artist(ae) - - -def draw_sizebar(ax): - from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar - # draw a horizontal bar with length of 0.1 in Data coordinate - # (ax.transData) with a label underneath. - asb = AnchoredSizeBar(ax.transData, - 0.1, - r"1$^{\prime}$", - loc=8, - pad=0.1, borderpad=0.5, sep=5, - frameon=False) - ax.add_artist(asb) - - -if 1: - ax = plt.gca() - ax.set_aspect(1.) - - draw_text(ax) - draw_circle(ax) - draw_ellipse(ax) - draw_sizebar(ax) - - plt.show() diff --git a/examples/axes_grid1/simple_axesgrid.py b/examples/axes_grid1/simple_axesgrid.py deleted file mode 100644 index b8c45f2aae6d..000000000000 --- a/examples/axes_grid1/simple_axesgrid.py +++ /dev/null @@ -1,17 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import ImageGrid -import numpy as np - -im = np.arange(100) -im.shape = 10, 10 - -fig = plt.figure(1, (4., 4.)) -grid = ImageGrid(fig, 111, # similar to subplot(111) - nrows_ncols=(2, 2), # creates 2x2 grid of axes - axes_pad=0.1, # pad between axes in inch. - ) - -for i in range(4): - grid[i].imshow(im) # The AxesGrid object work as a list of axes. - -plt.show() diff --git a/examples/axes_grid1/simple_axesgrid2.py b/examples/axes_grid1/simple_axesgrid2.py deleted file mode 100644 index 0eac5461613a..000000000000 --- a/examples/axes_grid1/simple_axesgrid2.py +++ /dev/null @@ -1,33 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import ImageGrid - - -def get_demo_image(): - import numpy as np - from matplotlib.cbook import get_sample_data - f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False) - z = np.load(f) - # z is a numpy array of 15x15 - return z, (-3, 4, -4, 3) - -F = plt.figure(1, (5.5, 3.5)) -grid = ImageGrid(F, 111, # similar to subplot(111) - nrows_ncols=(1, 3), - axes_pad=0.1, - add_all=True, - label_mode="L", - ) - -Z, extent = get_demo_image() # demo image - -im1 = Z -im2 = Z[:, :10] -im3 = Z[:, 10:] -vmin, vmax = Z.min(), Z.max() -for i, im in enumerate([im1, im2, im3]): - ax = grid[i] - ax.imshow(im, origin="lower", vmin=vmin, - vmax=vmax, interpolation="nearest") - -plt.draw() -plt.show() diff --git a/examples/axes_grid1/simple_axisline4.py b/examples/axes_grid1/simple_axisline4.py deleted file mode 100644 index b3bc3b4cd69a..000000000000 --- a/examples/axes_grid1/simple_axisline4.py +++ /dev/null @@ -1,19 +0,0 @@ -import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import host_subplot -import mpl_toolkits.axisartist as AA -import numpy as np - -ax = host_subplot(111) -xx = np.arange(0, 2*np.pi, 0.01) -ax.plot(xx, np.sin(xx)) - -ax2 = ax.twin() # ax2 is responsible for "top" axis and "right" axis -ax2.set_xticks([0., .5*np.pi, np.pi, 1.5*np.pi, 2*np.pi]) -ax2.set_xticklabels(["$0$", r"$\frac{1}{2}\pi$", - r"$\pi$", r"$\frac{3}{2}\pi$", r"$2\pi$"]) - -ax2.axis["right"].major_ticklabels.set_visible(False) -ax2.axis["top"].major_ticklabels.set_visible(True) - -plt.draw() -plt.show() diff --git a/examples/axisartist/demo_axisline_style.py b/examples/axisartist/demo_axisline_style.py deleted file mode 100644 index a8e2c7ada5d7..000000000000 --- a/examples/axisartist/demo_axisline_style.py +++ /dev/null @@ -1,21 +0,0 @@ - -from mpl_toolkits.axisartist.axislines import SubplotZero -import matplotlib.pyplot as plt -import numpy as np - -if 1: - fig = plt.figure(1) - ax = SubplotZero(fig, 111) - fig.add_subplot(ax) - - for direction in ["xzero", "yzero"]: - ax.axis[direction].set_axisline_style("-|>") - ax.axis[direction].set_visible(True) - - for direction in ["left", "right", "bottom", "top"]: - ax.axis[direction].set_visible(False) - - x = np.linspace(-0.5, 1., 100) - ax.plot(x, np.sin(x*np.pi)) - - plt.show() diff --git a/examples/axisartist/demo_curvelinear_grid.py b/examples/axisartist/demo_curvelinear_grid.py deleted file mode 100644 index 433ec46f2d39..000000000000 --- a/examples/axisartist/demo_curvelinear_grid.py +++ /dev/null @@ -1,137 +0,0 @@ -""" -Custom grid and ticklines. - -This example demonstrates how to use GridHelperCurveLinear to define -custom grids and ticklines by applying a transformation on the grid. -This can be used, as showcase on the second plot, to create polar -projections in a rectangular box. -""" - -import numpy as np - -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -from mpl_toolkits.axisartist import Subplot -from mpl_toolkits.axisartist import SubplotHost, \ - ParasiteAxesAuxTrans -from mpl_toolkits.axisartist.grid_helper_curvelinear import \ - GridHelperCurveLinear - - -def curvelinear_test1(fig): - """ - grid for custom transform. - """ - - def tr(x, y): - x, y = np.asarray(x), np.asarray(y) - return x, y - x - - def inv_tr(x, y): - x, y = np.asarray(x), np.asarray(y) - return x, y + x - - grid_helper = GridHelperCurveLinear((tr, inv_tr)) - - ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) - # ax1 will have a ticks and gridlines defined by the given - # transform (+ transData of the Axes). Note that the transform of - # the Axes itself (i.e., transData) is not affected by the given - # transform. - - fig.add_subplot(ax1) - - xx, yy = tr([3, 6], [5.0, 10.]) - ax1.plot(xx, yy, linewidth=2.0) - - ax1.set_aspect(1.) - ax1.set_xlim(0, 10.) - ax1.set_ylim(0, 10.) - - ax1.axis["t"] = ax1.new_floating_axis(0, 3.) - ax1.axis["t2"] = ax1.new_floating_axis(1, 7.) - ax1.grid(True, zorder=0) - - -import mpl_toolkits.axisartist.angle_helper as angle_helper - -from matplotlib.projections import PolarAxes -from matplotlib.transforms import Affine2D - - -def curvelinear_test2(fig): - """ - polar projection, but in a rectangular box. - """ - - # PolarAxes.PolarTransform takes radian. However, we want our coordinate - # system in degree - tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform() - - # polar projection, which involves cycle, and also has limits in - # its coordinates, needs a special method to find the extremes - # (min, max of the coordinate within the view). - - # 20, 20 : number of sampling points along x, y direction - extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, - lon_cycle=360, - lat_cycle=None, - lon_minmax=None, - lat_minmax=(0, np.inf), - ) - - grid_locator1 = angle_helper.LocatorDMS(12) - # Find a grid values appropriate for the coordinate (degree, - # minute, second). - - tick_formatter1 = angle_helper.FormatterDMS() - # And also uses an appropriate formatter. Note that,the - # acceptable Locator and Formatter class is a bit different than - # that of mpl's, and you cannot directly use mpl's Locator and - # Formatter here (but may be possible in the future). - - grid_helper = GridHelperCurveLinear(tr, - extreme_finder=extreme_finder, - grid_locator1=grid_locator1, - tick_formatter1=tick_formatter1 - ) - - ax1 = SubplotHost(fig, 1, 2, 2, grid_helper=grid_helper) - - # make ticklabels of right and top axis visible. - ax1.axis["right"].major_ticklabels.set_visible(True) - ax1.axis["top"].major_ticklabels.set_visible(True) - - # let right axis shows ticklabels for 1st coordinate (angle) - ax1.axis["right"].get_helper().nth_coord_ticks = 0 - # let bottom axis shows ticklabels for 2nd coordinate (radius) - ax1.axis["bottom"].get_helper().nth_coord_ticks = 1 - - fig.add_subplot(ax1) - - # A parasite axes with given transform - ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal") - # note that ax2.transData == tr + ax1.transData - # Anthing you draw in ax2 will match the ticks and grids of ax1. - ax1.parasites.append(ax2) - intp = cbook.simple_linear_interpolation - ax2.plot(intp(np.array([0, 30]), 50), - intp(np.array([10., 10.]), 50), - linewidth=2.0) - - ax1.set_aspect(1.) - ax1.set_xlim(-5, 12) - ax1.set_ylim(-5, 10) - - ax1.grid(True, zorder=0) - -if 1: - fig = plt.figure(1, figsize=(7, 4)) - fig.clf() - - curvelinear_test1(fig) - curvelinear_test2(fig) - - plt.draw() - plt.show() diff --git a/examples/axisartist/demo_curvelinear_grid2.py b/examples/axisartist/demo_curvelinear_grid2.py deleted file mode 100644 index c7cc87c24654..000000000000 --- a/examples/axisartist/demo_curvelinear_grid2.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Custom grid and ticklines. - -This example demonstrates how to use GridHelperCurveLinear to define -custom grids and ticklines by applying a transformation on the grid. -As showcase on the plot, a 5x5 matrix is displayed on the axes. -""" - -import numpy as np -import matplotlib.pyplot as plt - -from mpl_toolkits.axisartist.grid_helper_curvelinear import \ - GridHelperCurveLinear -from mpl_toolkits.axisartist.axislines import Subplot - -import mpl_toolkits.axisartist.angle_helper as angle_helper - - -def curvelinear_test1(fig): - """ - grid for custom transform. - """ - - def tr(x, y): - sgn = np.sign(x) - x, y = np.abs(np.asarray(x)), np.asarray(y) - return sgn*x**.5, y - - def inv_tr(x, y): - sgn = np.sign(x) - x, y = np.asarray(x), np.asarray(y) - return sgn*x**2, y - - extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, - lon_cycle=None, - lat_cycle=None, - # (0, np.inf), - lon_minmax=None, - lat_minmax=None, - ) - - grid_helper = GridHelperCurveLinear((tr, inv_tr), - extreme_finder=extreme_finder) - - ax1 = Subplot(fig, 111, grid_helper=grid_helper) - # ax1 will have a ticks and gridlines defined by the given - # transform (+ transData of the Axes). Note that the transform of - # the Axes itself (i.e., transData) is not affected by the given - # transform. - - fig.add_subplot(ax1) - - ax1.imshow(np.arange(25).reshape(5, 5), - vmax=50, cmap=plt.cm.gray_r, - interpolation="nearest", - origin="lower") - - # tick density - grid_helper.grid_finder.grid_locator1._nbins = 6 - grid_helper.grid_finder.grid_locator2._nbins = 6 - - -if 1: - fig = plt.figure(1, figsize=(7, 4)) - fig.clf() - - curvelinear_test1(fig) - plt.show() diff --git a/examples/axisartist/demo_floating_axes.py b/examples/axisartist/demo_floating_axes.py deleted file mode 100644 index 1a2a770e7e9d..000000000000 --- a/examples/axisartist/demo_floating_axes.py +++ /dev/null @@ -1,156 +0,0 @@ -""" -Demo of the floating axes. - -This demo shows features of functions in floating_axes: - * Using scatter function and bar function with changing the - shape of the plot. - * Using GridHelperCurveLinear to rotate the plot and set the - boundary of the plot. - * Using FloatingSubplot to create a subplot using the return - value from GridHelperCurveLinear. - * Making sector plot by adding more features to GridHelperCurveLinear. -""" -from matplotlib.transforms import Affine2D -import mpl_toolkits.axisartist.floating_axes as floating_axes -import numpy as np -import mpl_toolkits.axisartist.angle_helper as angle_helper -from matplotlib.projections import PolarAxes -from mpl_toolkits.axisartist.grid_finder import (FixedLocator, MaxNLocator, - DictFormatter) -import matplotlib.pyplot as plt - - -def setup_axes1(fig, rect): - """ - A simple one. - """ - tr = Affine2D().scale(2, 1).rotate_deg(30) - - grid_helper = floating_axes.GridHelperCurveLinear( - tr, extremes=(0, 4, 0, 4)) - - ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper) - fig.add_subplot(ax1) - - aux_ax = ax1.get_aux_axes(tr) - - grid_helper.grid_finder.grid_locator1._nbins = 4 - grid_helper.grid_finder.grid_locator2._nbins = 4 - - return ax1, aux_ax - - -def setup_axes2(fig, rect): - """ - With custom locator and formatter. - Note that the extreme values are swapped. - """ - tr = PolarAxes.PolarTransform() - - pi = np.pi - angle_ticks = [(0, r"$0$"), - (.25*pi, r"$\frac{1}{4}\pi$"), - (.5*pi, r"$\frac{1}{2}\pi$")] - grid_locator1 = FixedLocator([v for v, s in angle_ticks]) - tick_formatter1 = DictFormatter(dict(angle_ticks)) - - grid_locator2 = MaxNLocator(2) - - grid_helper = floating_axes.GridHelperCurveLinear( - tr, extremes=(.5*pi, 0, 2, 1), - grid_locator1=grid_locator1, - grid_locator2=grid_locator2, - tick_formatter1=tick_formatter1, - tick_formatter2=None) - - ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper) - fig.add_subplot(ax1) - - # create a parasite axes whose transData in RA, cz - aux_ax = ax1.get_aux_axes(tr) - - aux_ax.patch = ax1.patch # for aux_ax to have a clip path as in ax - ax1.patch.zorder = 0.9 # but this has a side effect that the patch is - # drawn twice, and possibly over some other - # artists. So, we decrease the zorder a bit to - # prevent this. - - return ax1, aux_ax - - -def setup_axes3(fig, rect): - """ - Sometimes, things like axis_direction need to be adjusted. - """ - - # rotate a bit for better orientation - tr_rotate = Affine2D().translate(-95, 0) - - # scale degree to radians - tr_scale = Affine2D().scale(np.pi/180., 1.) - - tr = tr_rotate + tr_scale + PolarAxes.PolarTransform() - - grid_locator1 = angle_helper.LocatorHMS(4) - tick_formatter1 = angle_helper.FormatterHMS() - - grid_locator2 = MaxNLocator(3) - - # Specify theta limits in degrees - ra0, ra1 = 8.*15, 14.*15 - # Specify radial limits - cz0, cz1 = 0, 14000 - grid_helper = floating_axes.GridHelperCurveLinear( - tr, extremes=(ra0, ra1, cz0, cz1), - grid_locator1=grid_locator1, - grid_locator2=grid_locator2, - tick_formatter1=tick_formatter1, - tick_formatter2=None) - - ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper) - fig.add_subplot(ax1) - - # adjust axis - ax1.axis["left"].set_axis_direction("bottom") - ax1.axis["right"].set_axis_direction("top") - - ax1.axis["bottom"].set_visible(False) - ax1.axis["top"].set_axis_direction("bottom") - ax1.axis["top"].toggle(ticklabels=True, label=True) - ax1.axis["top"].major_ticklabels.set_axis_direction("top") - ax1.axis["top"].label.set_axis_direction("top") - - ax1.axis["left"].label.set_text(r"cz [km$^{-1}$]") - ax1.axis["top"].label.set_text(r"$\alpha_{1950}$") - - # create a parasite axes whose transData in RA, cz - aux_ax = ax1.get_aux_axes(tr) - - aux_ax.patch = ax1.patch # for aux_ax to have a clip path as in ax - ax1.patch.zorder = 0.9 # but this has a side effect that the patch is - # drawn twice, and possibly over some other - # artists. So, we decrease the zorder a bit to - # prevent this. - - return ax1, aux_ax - - -########################################################## -fig = plt.figure(1, figsize=(8, 4)) -fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95) - -ax1, aux_ax1 = setup_axes1(fig, 131) -aux_ax1.bar([0, 1, 2, 3], [3, 2, 1, 3]) - -ax2, aux_ax2 = setup_axes2(fig, 132) -theta = np.random.rand(10)*.5*np.pi -radius = np.random.rand(10) + 1. -aux_ax2.scatter(theta, radius) - -ax3, aux_ax3 = setup_axes3(fig, 133) - -theta = (8 + np.random.rand(10)*(14 - 8))*15. # in degrees -radius = np.random.rand(10)*14000. -aux_ax3.scatter(theta, radius) - -plt.show() diff --git a/examples/axisartist/demo_floating_axis.py b/examples/axisartist/demo_floating_axis.py deleted file mode 100644 index 44eca5ad877f..000000000000 --- a/examples/axisartist/demo_floating_axis.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Axis within rectangular frame - -The following code demonstrates how to put a floating polar curve within a -rectangular box. In order to get a better sense of polar curves, please look at -demo_curvelinear_grid.py. -""" -import numpy as np -import matplotlib.pyplot as plt -import mpl_toolkits.axisartist.angle_helper as angle_helper -from matplotlib.projections import PolarAxes -from matplotlib.transforms import Affine2D -from mpl_toolkits.axisartist import SubplotHost -from mpl_toolkits.axisartist import GridHelperCurveLinear - - -def curvelinear_test2(fig): - """ - polar projection, but in a rectangular box. - """ - global ax1 - # see demo_curvelinear_grid.py for details - tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() - - extreme_finder = angle_helper.ExtremeFinderCycle(20, - 20, - lon_cycle=360, - lat_cycle=None, - lon_minmax=None, - lat_minmax=(0, - np.inf), - ) - - grid_locator1 = angle_helper.LocatorDMS(12) - - tick_formatter1 = angle_helper.FormatterDMS() - - grid_helper = GridHelperCurveLinear(tr, - extreme_finder=extreme_finder, - grid_locator1=grid_locator1, - tick_formatter1=tick_formatter1 - ) - - ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper) - - fig.add_subplot(ax1) - - # Now creates floating axis - - # floating axis whose first coordinate (theta) is fixed at 60 - ax1.axis["lat"] = axis = ax1.new_floating_axis(0, 60) - axis.label.set_text(r"$\theta = 60^{\circ}$") - axis.label.set_visible(True) - - # floating axis whose second coordinate (r) is fixed at 6 - ax1.axis["lon"] = axis = ax1.new_floating_axis(1, 6) - axis.label.set_text(r"$r = 6$") - - ax1.set_aspect(1.) - ax1.set_xlim(-5, 12) - ax1.set_ylim(-5, 10) - - ax1.grid(True) - -fig = plt.figure(1, figsize=(5, 5)) -fig.clf() - -curvelinear_test2(fig) - -plt.show() diff --git a/examples/axisartist/demo_parasite_axes2.py b/examples/axisartist/demo_parasite_axes2.py deleted file mode 100644 index 2cd377187b53..000000000000 --- a/examples/axisartist/demo_parasite_axes2.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Parasite axis demo - -The following code is an example of a parasite axis. It aims to show a user how -to plot multiple different values onto one single plot. Notice how in this -example, par1 and par2 are both calling twinx meaning both are tied directly to -the x-axis. From there, each of those two axis can behave separately from the -each other, meaning they can take on seperate values from themselves as well as -the x-axis. -""" -from mpl_toolkits.axes_grid1 import host_subplot -import mpl_toolkits.axisartist as AA -import matplotlib.pyplot as plt - -host = host_subplot(111, axes_class=AA.Axes) -plt.subplots_adjust(right=0.75) - -par1 = host.twinx() -par2 = host.twinx() - -offset = 60 -new_fixed_axis = par2.get_grid_helper().new_fixed_axis -par2.axis["right"] = new_fixed_axis(loc="right", - axes=par2, - offset=(offset, 0)) - -par2.axis["right"].toggle(all=True) - -host.set_xlim(0, 2) -host.set_ylim(0, 2) - -host.set_xlabel("Distance") -host.set_ylabel("Density") -par1.set_ylabel("Temperature") -par2.set_ylabel("Velocity") - -p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density") -p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature") -p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity") - -par1.set_ylim(0, 4) -par2.set_ylim(1, 65) - -host.legend() - -host.axis["left"].label.set_color(p1.get_color()) -par1.axis["right"].label.set_color(p2.get_color()) -par2.axis["right"].label.set_color(p3.get_color()) - -plt.draw() -plt.show() diff --git a/examples/color/color_cycle_default.py b/examples/color/color_cycle_default.py deleted file mode 100644 index dff1b4ada0a2..000000000000 --- a/examples/color/color_cycle_default.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Display the colors from the default prop_cycle. -""" - -import numpy as np -import matplotlib.pyplot as plt - -prop_cycle = plt.rcParams['axes.prop_cycle'] -colors = prop_cycle.by_key()['color'] - -lwbase = plt.rcParams['lines.linewidth'] -thin = float('%.1f' % (lwbase / 2)) -thick = lwbase * 3 - -fig, axs = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True) -for icol in range(2): - if icol == 0: - lwx, lwy = thin, lwbase - else: - lwx, lwy = lwbase, thick - for irow in range(2): - for i, color in enumerate(colors): - axs[irow, icol].axhline(i, color=color, lw=lwx) - axs[irow, icol].axvline(i, color=color, lw=lwy) - - axs[1, icol].set_facecolor('k') - axs[1, icol].xaxis.set_ticks(np.arange(0, 10, 2)) - axs[0, icol].set_title('line widths (pts): %.1f, %.1f' % (lwx, lwy), - fontsize='medium') - -for irow in range(2): - axs[irow, 0].yaxis.set_ticks(np.arange(0, 10, 2)) - -fig.suptitle('Colors in the default prop_cycle', fontsize='large') - -plt.show() diff --git a/examples/color/color_cycle_demo.py b/examples/color/color_cycle_demo.py deleted file mode 100644 index 84203febc10d..000000000000 --- a/examples/color/color_cycle_demo.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Demo of custom property-cycle settings to control colors and such -for multi-line plots. - -This example demonstrates two different APIs: - - 1. Setting the default rc-parameter specifying the property cycle. - This affects all subsequent axes (but not axes already created). - 2. Setting the property cycle for a specific axes. This only - affects a single axes. -""" -from cycler import cycler -import numpy as np -import matplotlib.pyplot as plt - -x = np.linspace(0, 2 * np.pi) -offsets = np.linspace(0, 2*np.pi, 4, endpoint=False) -# Create array with shifted-sine curve along each column -yy = np.transpose([np.sin(x + phi) for phi in offsets]) - -plt.rc('lines', linewidth=4) -plt.rc('axes', prop_cycle=(cycler('color', ['r', 'g', 'b', 'y']) + - cycler('linestyle', ['-', '--', ':', '-.']))) -fig, (ax0, ax1) = plt.subplots(nrows=2) -ax0.plot(yy) -ax0.set_title('Set default color cycle to rgby') - -ax1.set_prop_cycle(cycler('color', ['c', 'm', 'y', 'k']) + - cycler('lw', [1, 2, 3, 4])) -ax1.plot(yy) -ax1.set_title('Set axes color cycle to cmyk') - -# Tweak spacing between subplots to prevent labels from overlapping -fig.subplots_adjust(hspace=0.3) -plt.show() diff --git a/examples/color/colormaps_reference.py b/examples/color/colormaps_reference.py deleted file mode 100644 index 9f2c87e4a661..000000000000 --- a/examples/color/colormaps_reference.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Reference for colormaps included with Matplotlib. - -This reference example shows all colormaps included with Matplotlib. Note that -any colormap listed here can be reversed by appending "_r" (e.g., "pink_r"). -These colormaps are divided into the following categories: - -Sequential: - These colormaps are approximately monochromatic colormaps varying smoothly - between two color tones---usually from low saturation (e.g. white) to high - saturation (e.g. a bright blue). Sequential colormaps are ideal for - representing most scientific data since they show a clear progression from - low-to-high values. - -Diverging: - These colormaps have a median value (usually light in color) and vary - smoothly to two different color tones at high and low values. Diverging - colormaps are ideal when your data has a median value that is significant - (e.g. 0, such that positive and negative values are represented by - different colors of the colormap). - -Qualitative: - These colormaps vary rapidly in color. Qualitative colormaps are useful for - choosing a set of discrete colors. For example:: - - color_list = plt.cm.Set3(np.linspace(0, 1, 12)) - - gives a list of RGB colors that are good for plotting a series of lines on - a dark background. - -Miscellaneous: - Colormaps that don't fit into the categories above. - -""" -import numpy as np -import matplotlib.pyplot as plt - -# Have colormaps separated into categories: -# http://matplotlib.org/examples/color/colormaps_reference.html - -cmaps = [('Perceptually Uniform Sequential', - ['viridis', 'inferno', 'plasma', 'magma']), - ('Sequential', ['Blues', 'BuGn', 'BuPu', - 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', - 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', - 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']), - ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', - 'copper', 'gist_heat', 'gray', 'hot', - 'pink', 'spring', 'summer', 'winter']), - ('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', - 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', - 'seismic']), - ('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', - 'Pastel2', 'Set1', 'Set2', 'Set3', 'Vega10', - 'Vega20', 'Vega20b', 'Vega20c']), - ('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern', - 'brg', 'CMRmap', 'cubehelix', - 'gnuplot', 'gnuplot2', 'gist_ncar', - 'nipy_spectral', 'jet', 'rainbow', - 'gist_rainbow', 'hsv', 'flag', 'prism'])] - - -nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps) -gradient = np.linspace(0, 1, 256) -gradient = np.vstack((gradient, gradient)) - - -def plot_color_gradients(cmap_category, cmap_list): - fig, axes = plt.subplots(nrows=nrows) - fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99) - axes[0].set_title(cmap_category + ' colormaps', fontsize=14) - - for ax, name in zip(axes, cmap_list): - ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) - pos = list(ax.get_position().bounds) - x_text = pos[0] - 0.01 - y_text = pos[1] + pos[3]/2. - fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10) - - # Turn off *all* ticks & spines, not just the ones with colormaps. - for ax in axes: - ax.set_axis_off() - -for cmap_category, cmap_list in cmaps: - plot_color_gradients(cmap_category, cmap_list) - -plt.show() diff --git a/examples/color/named_colors.py b/examples/color/named_colors.py deleted file mode 100644 index 2280e6ea18bb..000000000000 --- a/examples/color/named_colors.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Visualization of named colors. - -Simple plot example with the named colors and its visual representation. -""" - -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import six - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import colors as mcolors - - -colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS) - -# Sort by hue, saturation, value and name. -by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name) - for name, color in colors.items()) - -# Get the sorted color names. -sorted_names = [name for hsv, name in by_hsv] - -n = len(sorted_names) -ncols = 4 -nrows = int(np.ceil(1. * n / ncols)) - -fig, ax = plt.subplots(figsize=(8, 5)) - -X, Y = fig.get_dpi() * fig.get_size_inches() - -# row height -h = Y / (nrows + 1) -# col width -w = X / ncols - -for i, name in enumerate(sorted_names): - col = i % ncols - row = int(i / ncols) - y = Y - (row * h) - h - - xi_line = w * (col + 0.05) - xf_line = w * (col + 0.25) - xi_text = w * (col + 0.3) - - ax.text(xi_text, y, name, fontsize=(h * 0.8), - horizontalalignment='left', - verticalalignment='center') - - ax.hlines( - y + h * 0.1, xi_line, xf_line, color=colors[name], linewidth=(h * 0.6)) - -ax.set_xlim(0, X) -ax.set_ylim(0, Y) -ax.set_axis_off() - -fig.subplots_adjust(left=0, right=1, - top=1, bottom=0, - hspace=0, wspace=0) -plt.show() diff --git a/examples/event_handling/README.txt b/examples/event_handling/README.txt deleted file mode 100644 index 66068d6ce41a..000000000000 --- a/examples/event_handling/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -matplotlib event handling -========================= - -matplotlib supports event handling with a GUI neutral event model. So -you can connect to matplotlib events w/o knowledge of what user -interface matplotlib will ultimately be plugged in to. This has two -advantages: the code you write will be more portable, and matplotlib -events are aware of things like data coordinate space and whih axes -the event occurs in so you don't have to mess with low level -transformation details to go from canvas space to data space. Object -picking examples are also included. diff --git a/examples/event_handling/close_event.py b/examples/event_handling/close_event.py deleted file mode 100644 index cffb6157cc36..000000000000 --- a/examples/event_handling/close_event.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import print_function -import matplotlib.pyplot as plt - - -def handle_close(evt): - print('Closed Figure!') - -fig = plt.figure() -fig.canvas.mpl_connect('close_event', handle_close) - -plt.text(0.35, 0.5, 'Close Me!', dict(size=30)) -plt.show() diff --git a/examples/event_handling/data_browser.py b/examples/event_handling/data_browser.py deleted file mode 100644 index 7bafd58f9708..000000000000 --- a/examples/event_handling/data_browser.py +++ /dev/null @@ -1,88 +0,0 @@ -import numpy as np - - -class PointBrowser(object): - """ - Click on a point to select and highlight it -- the data that - generated the point will be shown in the lower axes. Use the 'n' - and 'p' keys to browse through the next and previous points - """ - - def __init__(self): - self.lastind = 0 - - self.text = ax.text(0.05, 0.95, 'selected: none', - transform=ax.transAxes, va='top') - self.selected, = ax.plot([xs[0]], [ys[0]], 'o', ms=12, alpha=0.4, - color='yellow', visible=False) - - def onpress(self, event): - if self.lastind is None: - return - if event.key not in ('n', 'p'): - return - if event.key == 'n': - inc = 1 - else: - inc = -1 - - self.lastind += inc - self.lastind = np.clip(self.lastind, 0, len(xs) - 1) - self.update() - - def onpick(self, event): - - if event.artist != line: - return True - - N = len(event.ind) - if not N: - return True - - # the click locations - x = event.mouseevent.xdata - y = event.mouseevent.ydata - - distances = np.hypot(x - xs[event.ind], y - ys[event.ind]) - indmin = distances.argmin() - dataind = event.ind[indmin] - - self.lastind = dataind - self.update() - - def update(self): - if self.lastind is None: - return - - dataind = self.lastind - - ax2.cla() - ax2.plot(X[dataind]) - - ax2.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f' % (xs[dataind], ys[dataind]), - transform=ax2.transAxes, va='top') - ax2.set_ylim(-0.5, 1.5) - self.selected.set_visible(True) - self.selected.set_data(xs[dataind], ys[dataind]) - - self.text.set_text('selected: %d' % dataind) - fig.canvas.draw() - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - - X = np.random.rand(100, 200) - xs = np.mean(X, axis=1) - ys = np.std(X, axis=1) - - fig, (ax, ax2) = plt.subplots(2, 1) - ax.set_title('click on point to plot time series') - line, = ax.plot(xs, ys, 'o', picker=5) # 5 points tolerance - - browser = PointBrowser() - - fig.canvas.mpl_connect('pick_event', browser.onpick) - fig.canvas.mpl_connect('key_press_event', browser.onpress) - - plt.show() diff --git a/examples/event_handling/figure_axes_enter_leave.py b/examples/event_handling/figure_axes_enter_leave.py deleted file mode 100644 index 7ca0ee39d522..000000000000 --- a/examples/event_handling/figure_axes_enter_leave.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Illustrate the figure and axes enter and leave events by changing the -frame colors on enter and leave -""" -from __future__ import print_function -import matplotlib.pyplot as plt - - -def enter_axes(event): - print('enter_axes', event.inaxes) - event.inaxes.patch.set_facecolor('yellow') - event.canvas.draw() - - -def leave_axes(event): - print('leave_axes', event.inaxes) - event.inaxes.patch.set_facecolor('white') - event.canvas.draw() - - -def enter_figure(event): - print('enter_figure', event.canvas.figure) - event.canvas.figure.patch.set_facecolor('red') - event.canvas.draw() - - -def leave_figure(event): - print('leave_figure', event.canvas.figure) - event.canvas.figure.patch.set_facecolor('grey') - event.canvas.draw() - -fig1, (ax, ax2) = plt.subplots(2, 1) -fig1.suptitle('mouse hover over figure or axes to trigger events') - -fig1.canvas.mpl_connect('figure_enter_event', enter_figure) -fig1.canvas.mpl_connect('figure_leave_event', leave_figure) -fig1.canvas.mpl_connect('axes_enter_event', enter_axes) -fig1.canvas.mpl_connect('axes_leave_event', leave_axes) - -fig2, (ax, ax2) = plt.subplots(2, 1) -fig2.suptitle('mouse hover over figure or axes to trigger events') - -fig2.canvas.mpl_connect('figure_enter_event', enter_figure) -fig2.canvas.mpl_connect('figure_leave_event', leave_figure) -fig2.canvas.mpl_connect('axes_enter_event', enter_axes) -fig2.canvas.mpl_connect('axes_leave_event', leave_axes) - -plt.show() diff --git a/examples/event_handling/idle_and_timeout.py b/examples/event_handling/idle_and_timeout.py deleted file mode 100644 index e89d0b696141..000000000000 --- a/examples/event_handling/idle_and_timeout.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import print_function -""" -Demonstrate/test the idle and timeout API - -WARNING: idle_event is deprecated. Use the animations module instead. - -This is only tested on gtk so far and is a prototype implementation -""" -import numpy as np -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() - -t = np.arange(0.0, 2.0, 0.01) -y1 = np.sin(2*np.pi*t) -y2 = np.cos(2*np.pi*t) -line1, = ax.plot(y1) -line2, = ax.plot(y2) - -N = 100 - - -def on_idle(event): - on_idle.count += 1 - print('idle', on_idle.count) - line1.set_ydata(np.sin(2*np.pi*t*(N - on_idle.count)/float(N))) - event.canvas.draw() - # test boolean return removal - if on_idle.count == N: - return False - return True -on_idle.cid = None -on_idle.count = 0 - -fig.canvas.mpl_connect('idle_event', on_idle) - -plt.show() diff --git a/examples/event_handling/keypress_demo.py b/examples/event_handling/keypress_demo.py deleted file mode 100755 index db8c71c96918..000000000000 --- a/examples/event_handling/keypress_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Show how to connect to keypress events -""" -from __future__ import print_function -import sys -import numpy as np -import matplotlib.pyplot as plt - - -def press(event): - print('press', event.key) - sys.stdout.flush() - if event.key == 'x': - visible = xl.get_visible() - xl.set_visible(not visible) - fig.canvas.draw() - -fig, ax = plt.subplots() - -fig.canvas.mpl_connect('key_press_event', press) - -ax.plot(np.random.rand(12), np.random.rand(12), 'go') -xl = ax.set_xlabel('easy come, easy go') -ax.set_title('Press a key') -plt.show() diff --git a/examples/event_handling/lasso_demo.py b/examples/event_handling/lasso_demo.py deleted file mode 100644 index 8cc8d662043d..000000000000 --- a/examples/event_handling/lasso_demo.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Show how to use a lasso to select a set of points and get the indices -of the selected points. A callback is used to change the color of the -selected points - -This is currently a proof-of-concept implementation (though it is -usable as is). There will be some refinement of the API. -""" -from matplotlib.widgets import Lasso -from matplotlib.collections import RegularPolyCollection -from matplotlib import colors as mcolors, path - -import matplotlib.pyplot as plt -from numpy import nonzero -from numpy.random import rand - - -class Datum(object): - colorin = mcolors.to_rgba("red") - colorout = mcolors.to_rgba("blue") - - def __init__(self, x, y, include=False): - self.x = x - self.y = y - if include: - self.color = self.colorin - else: - self.color = self.colorout - - -class LassoManager(object): - def __init__(self, ax, data): - self.axes = ax - self.canvas = ax.figure.canvas - self.data = data - - self.Nxy = len(data) - - facecolors = [d.color for d in data] - self.xys = [(d.x, d.y) for d in data] - fig = ax.figure - self.collection = RegularPolyCollection( - fig.dpi, 6, sizes=(100,), - facecolors=facecolors, - offsets=self.xys, - transOffset=ax.transData) - - ax.add_collection(self.collection) - - self.cid = self.canvas.mpl_connect('button_press_event', self.onpress) - - def callback(self, verts): - facecolors = self.collection.get_facecolors() - p = path.Path(verts) - ind = p.contains_points(self.xys) - for i in range(len(self.xys)): - if ind[i]: - facecolors[i] = Datum.colorin - else: - facecolors[i] = Datum.colorout - - self.canvas.draw_idle() - self.canvas.widgetlock.release(self.lasso) - del self.lasso - - def onpress(self, event): - if self.canvas.widgetlock.locked(): - return - if event.inaxes is None: - return - self.lasso = Lasso(event.inaxes, - (event.xdata, event.ydata), - self.callback) - # acquire a lock on the widget drawing - self.canvas.widgetlock(self.lasso) - -if __name__ == '__main__': - - data = [Datum(*xy) for xy in rand(100, 2)] - ax = plt.axes(xlim=(0, 1), ylim=(0, 1), autoscale_on=False) - ax.set_title('Lasso points using left mouse button') - - lman = LassoManager(ax, data) - - plt.show() diff --git a/examples/event_handling/legend_picking.py b/examples/event_handling/legend_picking.py deleted file mode 100644 index 1c37ad3c6e85..000000000000 --- a/examples/event_handling/legend_picking.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Enable picking on the legend to toggle the legended line on and off -""" -import numpy as np -import matplotlib.pyplot as plt - -t = np.arange(0.0, 0.2, 0.1) -y1 = 2*np.sin(2*np.pi*t) -y2 = 4*np.sin(2*np.pi*2*t) - -fig, ax = plt.subplots() -ax.set_title('Click on legend line to toggle line on/off') -line1, = ax.plot(t, y1, lw=2, color='red', label='1 HZ') -line2, = ax.plot(t, y2, lw=2, color='blue', label='2 HZ') -leg = ax.legend(loc='upper left', fancybox=True, shadow=True) -leg.get_frame().set_alpha(0.4) - - -# we will set up a dict mapping legend line to orig line, and enable -# picking on the legend line -lines = [line1, line2] -lined = dict() -for legline, origline in zip(leg.get_lines(), lines): - legline.set_picker(5) # 5 pts tolerance - lined[legline] = origline - - -def onpick(event): - # on the pick event, find the orig line corresponding to the - # legend proxy line, and toggle the visibility - legline = event.artist - origline = lined[legline] - vis = not origline.get_visible() - origline.set_visible(vis) - # Change the alpha on the line in the legend so we can see what lines - # have been toggled - if vis: - legline.set_alpha(1.0) - else: - legline.set_alpha(0.2) - fig.canvas.draw() - -fig.canvas.mpl_connect('pick_event', onpick) - -plt.show() diff --git a/examples/event_handling/looking_glass.py b/examples/event_handling/looking_glass.py deleted file mode 100644 index 8a5c0d0979b0..000000000000 --- a/examples/event_handling/looking_glass.py +++ /dev/null @@ -1,48 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.patches as patches -x, y = np.random.rand(2, 200) - -fig, ax = plt.subplots() -circ = patches.Circle((0.5, 0.5), 0.25, alpha=0.8, fc='yellow') -ax.add_patch(circ) - - -ax.plot(x, y, alpha=0.2) -line, = ax.plot(x, y, alpha=1.0, clip_path=circ) -ax.set_title("Left click and drag to move looking glass") - - -class EventHandler(object): - def __init__(self): - fig.canvas.mpl_connect('button_press_event', self.onpress) - fig.canvas.mpl_connect('button_release_event', self.onrelease) - fig.canvas.mpl_connect('motion_notify_event', self.onmove) - self.x0, self.y0 = circ.center - self.pressevent = None - - def onpress(self, event): - if event.inaxes != ax: - return - - if not circ.contains(event)[0]: - return - - self.pressevent = event - - def onrelease(self, event): - self.pressevent = None - self.x0, self.y0 = circ.center - - def onmove(self, event): - if self.pressevent is None or event.inaxes != self.pressevent.inaxes: - return - - dx = event.xdata - self.pressevent.xdata - dy = event.ydata - self.pressevent.ydata - circ.center = self.x0 + dx, self.y0 + dy - line.set_clip_path(circ) - fig.canvas.draw() - -handler = EventHandler() -plt.show() diff --git a/examples/event_handling/path_editor.py b/examples/event_handling/path_editor.py deleted file mode 100644 index 3921bf71c8cf..000000000000 --- a/examples/event_handling/path_editor.py +++ /dev/null @@ -1,149 +0,0 @@ -import numpy as np -import matplotlib.path as mpath -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - -Path = mpath.Path - -fig, ax = plt.subplots() - -pathdata = [ - (Path.MOVETO, (1.58, -2.57)), - (Path.CURVE4, (0.35, -1.1)), - (Path.CURVE4, (-1.75, 2.0)), - (Path.CURVE4, (0.375, 2.0)), - (Path.LINETO, (0.85, 1.15)), - (Path.CURVE4, (2.2, 3.2)), - (Path.CURVE4, (3, 0.05)), - (Path.CURVE4, (2.0, -0.5)), - (Path.CLOSEPOLY, (1.58, -2.57)), - ] - -codes, verts = zip(*pathdata) -path = mpath.Path(verts, codes) -patch = mpatches.PathPatch(path, facecolor='green', edgecolor='yellow', alpha=0.5) -ax.add_patch(patch) - - -class PathInteractor(object): - """ - An path editor. - - Key-bindings - - 't' toggle vertex markers on and off. When vertex markers are on, - you can move them, delete them - - - """ - - showverts = True - epsilon = 5 # max pixel distance to count as a vertex hit - - def __init__(self, pathpatch): - - self.ax = pathpatch.axes - canvas = self.ax.figure.canvas - self.pathpatch = pathpatch - self.pathpatch.set_animated(True) - - x, y = zip(*self.pathpatch.get_path().vertices) - - self.line, = ax.plot(x, y, marker='o', markerfacecolor='r', animated=True) - - self._ind = None # the active vert - - canvas.mpl_connect('draw_event', self.draw_callback) - canvas.mpl_connect('button_press_event', self.button_press_callback) - canvas.mpl_connect('key_press_event', self.key_press_callback) - canvas.mpl_connect('button_release_event', self.button_release_callback) - canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) - self.canvas = canvas - - def draw_callback(self, event): - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - self.ax.draw_artist(self.pathpatch) - self.ax.draw_artist(self.line) - self.canvas.blit(self.ax.bbox) - - def pathpatch_changed(self, pathpatch): - 'this method is called whenever the pathpatchgon object is called' - # only copy the artist props to the line (except visibility) - vis = self.line.get_visible() - plt.Artist.update_from(self.line, pathpatch) - self.line.set_visible(vis) # don't use the pathpatch visibility state - - def get_ind_under_point(self, event): - 'get the index of the vertex under point if within epsilon tolerance' - - # display coords - xy = np.asarray(self.pathpatch.get_path().vertices) - xyt = self.pathpatch.get_transform().transform(xy) - xt, yt = xyt[:, 0], xyt[:, 1] - d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) - ind = d.argmin() - - if d[ind] >= self.epsilon: - ind = None - - return ind - - def button_press_callback(self, event): - 'whenever a mouse button is pressed' - if not self.showverts: - return - if event.inaxes is None: - return - if event.button != 1: - return - self._ind = self.get_ind_under_point(event) - - def button_release_callback(self, event): - 'whenever a mouse button is released' - if not self.showverts: - return - if event.button != 1: - return - self._ind = None - - def key_press_callback(self, event): - 'whenever a key is pressed' - if not event.inaxes: - return - if event.key == 't': - self.showverts = not self.showverts - self.line.set_visible(self.showverts) - if not self.showverts: - self._ind = None - - self.canvas.draw() - - def motion_notify_callback(self, event): - 'on mouse movement' - if not self.showverts: - return - if self._ind is None: - return - if event.inaxes is None: - return - if event.button != 1: - return - x, y = event.xdata, event.ydata - - vertices = self.pathpatch.get_path().vertices - - vertices[self._ind] = x, y - self.line.set_data(zip(*vertices)) - - self.canvas.restore_region(self.background) - self.ax.draw_artist(self.pathpatch) - self.ax.draw_artist(self.line) - self.canvas.blit(self.ax.bbox) - - -interactor = PathInteractor(patch) -ax.set_title('drag vertices to update path') -ax.set_xlim(-3, 4) -ax.set_ylim(-3, 4) - -plt.show() diff --git a/examples/event_handling/pick_event_demo.py b/examples/event_handling/pick_event_demo.py deleted file mode 100755 index e6282da88afb..000000000000 --- a/examples/event_handling/pick_event_demo.py +++ /dev/null @@ -1,173 +0,0 @@ -""" - -You can enable picking by setting the "picker" property of an artist -(for example, a matplotlib Line2D, Text, Patch, Polygon, AxesImage, -etc...) - -There are a variety of meanings of the picker property - - None - picking is disabled for this artist (default) - - boolean - if True then picking will be enabled and the - artist will fire a pick event if the mouse event is over - the artist - - float - if picker is a number it is interpreted as an - epsilon tolerance in points and the artist will fire - off an event if it's data is within epsilon of the mouse - event. For some artists like lines and patch collections, - the artist may provide additional data to the pick event - that is generated, for example, the indices of the data within - epsilon of the pick event - - function - if picker is callable, it is a user supplied - function which determines whether the artist is hit by the - mouse event. - - hit, props = picker(artist, mouseevent) - - to determine the hit test. If the mouse event is over the - artist, return hit=True and props is a dictionary of properties - you want added to the PickEvent attributes - - -After you have enabled an artist for picking by setting the "picker" -property, you need to connect to the figure canvas pick_event to get -pick callbacks on mouse press events. For example, - - def pick_handler(event): - mouseevent = event.mouseevent - artist = event.artist - # now do something with this... - - -The pick event (matplotlib.backend_bases.PickEvent) which is passed to -your callback is always fired with two attributes: - - mouseevent - the mouse event that generate the pick event. The - mouse event in turn has attributes like x and y (the coordinates in - display space, such as pixels from left, bottom) and xdata, ydata (the - coords in data space). Additionally, you can get information about - which buttons were pressed, which keys were pressed, which Axes - the mouse is over, etc. See matplotlib.backend_bases.MouseEvent - for details. - - artist - the matplotlib.artist that generated the pick event. - -Additionally, certain artists like Line2D and PatchCollection may -attach additional meta data like the indices into the data that meet -the picker criteria (for example, all the points in the line that are within -the specified epsilon tolerance) - -The examples below illustrate each of these methods. -""" - -from __future__ import print_function -import matplotlib.pyplot as plt -from matplotlib.lines import Line2D -from matplotlib.patches import Rectangle -from matplotlib.text import Text -from matplotlib.image import AxesImage -import numpy as np -from numpy.random import rand - -if 1: # simple picking, lines, rectangles and text - fig, (ax1, ax2) = plt.subplots(2, 1) - ax1.set_title('click on points, rectangles or text', picker=True) - ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red')) - line, = ax1.plot(rand(100), 'o', picker=5) # 5 points tolerance - - # pick the rectangle - bars = ax2.bar(range(10), rand(10), picker=True) - for label in ax2.get_xticklabels(): # make the xtick labels pickable - label.set_picker(True) - - def onpick1(event): - if isinstance(event.artist, Line2D): - thisline = event.artist - xdata = thisline.get_xdata() - ydata = thisline.get_ydata() - ind = event.ind - print('onpick1 line:', zip(np.take(xdata, ind), np.take(ydata, ind))) - elif isinstance(event.artist, Rectangle): - patch = event.artist - print('onpick1 patch:', patch.get_path()) - elif isinstance(event.artist, Text): - text = event.artist - print('onpick1 text:', text.get_text()) - - fig.canvas.mpl_connect('pick_event', onpick1) - -if 1: # picking with a custom hit test function - # you can define custom pickers by setting picker to a callable - # function. The function has the signature - # - # hit, props = func(artist, mouseevent) - # - # to determine the hit test. if the mouse event is over the artist, - # return hit=True and props is a dictionary of - # properties you want added to the PickEvent attributes - - def line_picker(line, mouseevent): - """ - find the points within a certain distance from the mouseclick in - data coords and attach some extra attributes, pickx and picky - which are the data points that were picked - """ - if mouseevent.xdata is None: - return False, dict() - xdata = line.get_xdata() - ydata = line.get_ydata() - maxd = 0.05 - d = np.sqrt((xdata - mouseevent.xdata)**2. + (ydata - mouseevent.ydata)**2.) - - ind = np.nonzero(np.less_equal(d, maxd)) - if len(ind): - pickx = np.take(xdata, ind) - picky = np.take(ydata, ind) - props = dict(ind=ind, pickx=pickx, picky=picky) - return True, props - else: - return False, dict() - - def onpick2(event): - print('onpick2 line:', event.pickx, event.picky) - - fig, ax = plt.subplots() - ax.set_title('custom picker for line data') - line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker) - fig.canvas.mpl_connect('pick_event', onpick2) - - -if 1: # picking on a scatter plot (matplotlib.collections.RegularPolyCollection) - - x, y, c, s = rand(4, 100) - - def onpick3(event): - ind = event.ind - print('onpick3 scatter:', ind, np.take(x, ind), np.take(y, ind)) - - fig, ax = plt.subplots() - col = ax.scatter(x, y, 100*s, c, picker=True) - #fig.savefig('pscoll.eps') - fig.canvas.mpl_connect('pick_event', onpick3) - -if 1: # picking images (matplotlib.image.AxesImage) - fig, ax = plt.subplots() - im1 = ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True) - im2 = ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True) - im3 = ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True) - im4 = ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True) - ax.axis([0, 5, 0, 5]) - - def onpick4(event): - artist = event.artist - if isinstance(artist, AxesImage): - im = artist - A = im.get_array() - print('onpick4 image', A.shape) - - fig.canvas.mpl_connect('pick_event', onpick4) - - -plt.show() diff --git a/examples/event_handling/pick_event_demo2.py b/examples/event_handling/pick_event_demo2.py deleted file mode 100644 index 0e103ebbad8c..000000000000 --- a/examples/event_handling/pick_event_demo2.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -compute the mean and standard deviation (stddev) of 100 data sets and plot -mean vs stddev. When you click on one of the mu, sigma points, plot the raw -data from the dataset that generated the mean and stddev. -""" -import numpy -import matplotlib.pyplot as plt - - -X = numpy.random.rand(100, 1000) -xs = numpy.mean(X, axis=1) -ys = numpy.std(X, axis=1) - -fig, ax = plt.subplots() -ax.set_title('click on point to plot time series') -line, = ax.plot(xs, ys, 'o', picker=5) # 5 points tolerance - - -def onpick(event): - - if event.artist != line: - return True - - N = len(event.ind) - if not N: - return True - - figi = plt.figure() - for subplotnum, dataind in enumerate(event.ind): - ax = figi.add_subplot(N, 1, subplotnum + 1) - ax.plot(X[dataind]) - ax.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f' % (xs[dataind], ys[dataind]), - transform=ax.transAxes, va='top') - ax.set_ylim(-0.5, 1.5) - figi.show() - return True - -fig.canvas.mpl_connect('pick_event', onpick) - -plt.show() diff --git a/examples/event_handling/pipong.py b/examples/event_handling/pipong.py deleted file mode 100755 index 89ab36ef9b36..000000000000 --- a/examples/event_handling/pipong.py +++ /dev/null @@ -1,265 +0,0 @@ -# A matplotlib based game of Pong illustrating one way to write interactive -# animation which are easily ported to multiple backends -# pipong.py was written by Paul Ivanov - -from __future__ import print_function - -import numpy as np -import matplotlib.pyplot as plt -from numpy.random import randn, randint - -instructions = """ -Player A: Player B: - 'e' up 'i' - 'd' down 'k' - -press 't' -- close these instructions - (animation will be much faster) -press 'a' -- add a puck -press 'A' -- remove a puck -press '1' -- slow down all pucks -press '2' -- speed up all pucks -press '3' -- slow down distractors -press '4' -- speed up distractors -press ' ' -- reset the first puck -press 'n' -- toggle distractors on/off -press 'g' -- toggle the game on/off - - """ - - -class Pad(object): - def __init__(self, disp, x, y, type='l'): - self.disp = disp - self.x = x - self.y = y - self.w = .3 - self.score = 0 - self.xoffset = 0.3 - self.yoffset = 0.1 - if type == 'r': - self.xoffset *= -1.0 - - if type == 'l' or type == 'r': - self.signx = -1.0 - self.signy = 1.0 - else: - self.signx = 1.0 - self.signy = -1.0 - - def contains(self, loc): - return self.disp.get_bbox().contains(loc.x, loc.y) - - -class Puck(object): - def __init__(self, disp, pad, field): - self.vmax = .2 - self.disp = disp - self.field = field - self._reset(pad) - - def _reset(self, pad): - self.x = pad.x + pad.xoffset - if pad.y < 0: - self.y = pad.y + pad.yoffset - else: - self.y = pad.y - pad.yoffset - self.vx = pad.x - self.x - self.vy = pad.y + pad.w/2 - self.y - self._speedlimit() - self._slower() - self._slower() - - def update(self, pads): - self.x += self.vx - self.y += self.vy - for pad in pads: - if pad.contains(self): - self.vx *= 1.2 * pad.signx - self.vy *= 1.2 * pad.signy - fudge = .001 - # probably cleaner with something like... - #if not self.field.contains(self.x, self.y): - if self.x < fudge: - #print("player A loses") - pads[1].score += 1 - self._reset(pads[0]) - return True - if self.x > 7 - fudge: - #print("player B loses") - pads[0].score += 1 - self._reset(pads[1]) - return True - if self.y < -1 + fudge or self.y > 1 - fudge: - self.vy *= -1.0 - # add some randomness, just to make it interesting - self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy) - self._speedlimit() - return False - - def _slower(self): - self.vx /= 5.0 - self.vy /= 5.0 - - def _faster(self): - self.vx *= 5.0 - self.vy *= 5.0 - - def _speedlimit(self): - if self.vx > self.vmax: - self.vx = self.vmax - if self.vx < -self.vmax: - self.vx = -self.vmax - - if self.vy > self.vmax: - self.vy = self.vmax - if self.vy < -self.vmax: - self.vy = -self.vmax - - -class Game(object): - def __init__(self, ax): - # create the initial line - self.ax = ax - padAx = padBx = .50 - padAy = padBy = .30 - padBx += 6.3 - pA, = self.ax.barh(padAy, .2, height=.3, color='k', alpha=.5, edgecolor='b', lw=2, label="Player B", animated=True) - pB, = self.ax.barh(padBy, .2, height=.3, left=padBx, color='k', alpha=.5, edgecolor='r', lw=2, label="Player A", animated=True) - - # distractors - self.x = np.arange(0, 2.22*np.pi, 0.01) - self.line, = self.ax.plot(self.x, np.sin(self.x), "r", animated=True, lw=4) - self.line2, = self.ax.plot(self.x, np.cos(self.x), "g", animated=True, lw=4) - self.line3, = self.ax.plot(self.x, np.cos(self.x), "g", animated=True, lw=4) - self.line4, = self.ax.plot(self.x, np.cos(self.x), "r", animated=True, lw=4) - self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k', alpha=.5, animated=True, lw=8) - self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_', s=200, c='g', alpha=.9, animated=True) - - self.canvas = self.ax.figure.canvas - self.background = None - self.cnt = 0 - self.distract = True - self.res = 100.0 - self.on = False - self.inst = True # show instructions from the beginning - self.background = None - self.pads = [] - self.pads.append(Pad(pA, 0, padAy)) - self.pads.append(Pad(pB, padBx, padBy, 'r')) - self.pucks = [] - self.i = self.ax.annotate(instructions, (.5, 0.5), - name='monospace', - verticalalignment='center', - horizontalalignment='center', - multialignment='left', - textcoords='axes fraction', animated=True) - self.canvas.mpl_connect('key_press_event', self.key_press) - - def draw(self, evt): - draw_artist = self.ax.draw_artist - if self.background is None: - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - - # restore the clean slate background - self.canvas.restore_region(self.background) - - # show the distractors - if self.distract: - self.line.set_ydata(np.sin(self.x + self.cnt/self.res)) - self.line2.set_ydata(np.cos(self.x - self.cnt/self.res)) - self.line3.set_ydata(np.tan(self.x + self.cnt/self.res)) - self.line4.set_ydata(np.tan(self.x - self.cnt/self.res)) - draw_artist(self.line) - draw_artist(self.line2) - draw_artist(self.line3) - draw_artist(self.line4) - - # show the instructions - this is very slow - if self.inst: - self.ax.draw_artist(self.i) - - # pucks and pads - if self.on: - self.ax.draw_artist(self.centerline) - for pad in self.pads: - pad.disp.set_y(pad.y) - pad.disp.set_x(pad.x) - self.ax.draw_artist(pad.disp) - - for puck in self.pucks: - if puck.update(self.pads): - # we only get here if someone scored - self.pads[0].disp.set_label(" " + str(self.pads[0].score)) - self.pads[1].disp.set_label(" " + str(self.pads[1].score)) - self.ax.legend(loc='center') - self.leg = self.ax.get_legend() - #self.leg.draw_frame(False) #don't draw the legend border - self.leg.get_frame().set_alpha(.2) - plt.setp(self.leg.get_texts(), fontweight='bold', fontsize='xx-large') - self.leg.get_frame().set_facecolor('0.2') - self.background = None - self.ax.figure.canvas.draw() - return True - puck.disp.set_offsets([puck.x, puck.y]) - self.ax.draw_artist(puck.disp) - - # just redraw the axes rectangle - self.canvas.blit(self.ax.bbox) - - if self.cnt == 50000: - # just so we don't get carried away - print("...and you've been playing for too long!!!") - plt.close() - - self.cnt += 1 - return True - - def key_press(self, event): - if event.key == '3': - self.res *= 5.0 - if event.key == '4': - self.res /= 5.0 - - if event.key == 'e': - self.pads[0].y += .1 - if self.pads[0].y > 1 - .3: - self.pads[0].y = 1 - .3 - if event.key == 'd': - self.pads[0].y -= .1 - if self.pads[0].y < -1: - self.pads[0].y = -1 - - if event.key == 'i': - self.pads[1].y += .1 - if self.pads[1].y > 1 - .3: - self.pads[1].y = 1 - .3 - if event.key == 'k': - self.pads[1].y -= .1 - if self.pads[1].y < -1: - self.pads[1].y = -1 - - if event.key == 'a': - self.pucks.append(Puck(self.puckdisp, self.pads[randint(2)], self.ax.bbox)) - if event.key == 'A' and len(self.pucks): - self.pucks.pop() - if event.key == ' ' and len(self.pucks): - self.pucks[0]._reset(self.pads[randint(2)]) - if event.key == '1': - for p in self.pucks: - p._slower() - if event.key == '2': - for p in self.pucks: - p._faster() - - if event.key == 'n': - self.distract = not self.distract - - if event.key == 'g': - #self.ax.clear() - self.on = not self.on - if event.key == 't': - self.inst = not self.inst - self.i.set_visible(self.i.get_visible()) - if event.key == 'q': - plt.close() diff --git a/examples/event_handling/poly_editor.py b/examples/event_handling/poly_editor.py deleted file mode 100644 index 98d65d0021c5..000000000000 --- a/examples/event_handling/poly_editor.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -This is an example to show how to build cross-GUI applications using -matplotlib event handling to interact with objects on the canvas - -""" -import numpy as np -from matplotlib.lines import Line2D -from matplotlib.artist import Artist -from matplotlib.mlab import dist_point_to_segment - - -class PolygonInteractor(object): - """ - An polygon editor. - - Key-bindings - - 't' toggle vertex markers on and off. When vertex markers are on, - you can move them, delete them - - 'd' delete the vertex under point - - 'i' insert a vertex at point. You must be within epsilon of the - line connecting two existing vertices - - """ - - showverts = True - epsilon = 5 # max pixel distance to count as a vertex hit - - def __init__(self, ax, poly): - if poly.figure is None: - raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor') - self.ax = ax - canvas = poly.figure.canvas - self.poly = poly - - x, y = zip(*self.poly.xy) - self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True) - self.ax.add_line(self.line) - #self._update_line(poly) - - cid = self.poly.add_callback(self.poly_changed) - self._ind = None # the active vert - - canvas.mpl_connect('draw_event', self.draw_callback) - canvas.mpl_connect('button_press_event', self.button_press_callback) - canvas.mpl_connect('key_press_event', self.key_press_callback) - canvas.mpl_connect('button_release_event', self.button_release_callback) - canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) - self.canvas = canvas - - def draw_callback(self, event): - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - self.ax.draw_artist(self.poly) - self.ax.draw_artist(self.line) - self.canvas.blit(self.ax.bbox) - - def poly_changed(self, poly): - 'this method is called whenever the polygon object is called' - # only copy the artist props to the line (except visibility) - vis = self.line.get_visible() - Artist.update_from(self.line, poly) - self.line.set_visible(vis) # don't use the poly visibility state - - def get_ind_under_point(self, event): - 'get the index of the vertex under point if within epsilon tolerance' - - # display coords - xy = np.asarray(self.poly.xy) - xyt = self.poly.get_transform().transform(xy) - xt, yt = xyt[:, 0], xyt[:, 1] - d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) - indseq = np.nonzero(np.equal(d, np.amin(d)))[0] - ind = indseq[0] - - if d[ind] >= self.epsilon: - ind = None - - return ind - - def button_press_callback(self, event): - 'whenever a mouse button is pressed' - if not self.showverts: - return - if event.inaxes is None: - return - if event.button != 1: - return - self._ind = self.get_ind_under_point(event) - - def button_release_callback(self, event): - 'whenever a mouse button is released' - if not self.showverts: - return - if event.button != 1: - return - self._ind = None - - def key_press_callback(self, event): - 'whenever a key is pressed' - if not event.inaxes: - return - if event.key == 't': - self.showverts = not self.showverts - self.line.set_visible(self.showverts) - if not self.showverts: - self._ind = None - elif event.key == 'd': - ind = self.get_ind_under_point(event) - if ind is not None: - self.poly.xy = [tup for i, tup in enumerate(self.poly.xy) if i != ind] - self.line.set_data(zip(*self.poly.xy)) - elif event.key == 'i': - xys = self.poly.get_transform().transform(self.poly.xy) - p = event.x, event.y # display coords - for i in range(len(xys) - 1): - s0 = xys[i] - s1 = xys[i + 1] - d = dist_point_to_segment(p, s0, s1) - if d <= self.epsilon: - self.poly.xy = np.array( - list(self.poly.xy[:i]) + - [(event.xdata, event.ydata)] + - list(self.poly.xy[i:])) - self.line.set_data(zip(*self.poly.xy)) - break - - self.canvas.draw() - - def motion_notify_callback(self, event): - 'on mouse movement' - if not self.showverts: - return - if self._ind is None: - return - if event.inaxes is None: - return - if event.button != 1: - return - x, y = event.xdata, event.ydata - - self.poly.xy[self._ind] = x, y - if self._ind == 0: - self.poly.xy[-1] = x, y - elif self._ind == len(self.poly.xy) - 1: - self.poly.xy[0] = x, y - self.line.set_data(zip(*self.poly.xy)) - - self.canvas.restore_region(self.background) - self.ax.draw_artist(self.poly) - self.ax.draw_artist(self.line) - self.canvas.blit(self.ax.bbox) - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - from matplotlib.patches import Polygon - - theta = np.arange(0, 2*np.pi, 0.1) - r = 1.5 - - xs = r*np.cos(theta) - ys = r*np.sin(theta) - - poly = Polygon(list(zip(xs, ys)), animated=True) - - fig, ax = plt.subplots() - ax.add_patch(poly) - p = PolygonInteractor(ax, poly) - - #ax.add_line(p.line) - ax.set_title('Click and drag a point to move it') - ax.set_xlim((-2, 2)) - ax.set_ylim((-2, 2)) - plt.show() diff --git a/examples/event_handling/pong_gtk.py b/examples/event_handling/pong_gtk.py deleted file mode 100755 index 48290a1b9046..000000000000 --- a/examples/event_handling/pong_gtk.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import print_function - -# For detailed comments on animation and the techniques used here, see -# the wiki entry -# http://www.scipy.org/wikis/topical_software/MatplotlibAnimation -import time - -import gobject - -import matplotlib -matplotlib.use('GTKAgg') - -import matplotlib.pyplot as plt -import pipong - - -fig, ax = plt.subplots() -canvas = ax.figure.canvas - - -def start_anim(event): - # gobject.idle_add(animation.draw,animation) - gobject.timeout_add(10, animation.draw, animation) - canvas.mpl_disconnect(start_anim.cid) - -animation = pipong.Game(ax) -start_anim.cid = canvas.mpl_connect('draw_event', start_anim) - - -tstart = time.time() -plt.grid() # to ensure proper background restore -plt.show() -print('FPS: %f' % animation.cnt/(time.time() - tstart)) diff --git a/examples/event_handling/resample.py b/examples/event_handling/resample.py deleted file mode 100644 index 1b380a06cb96..000000000000 --- a/examples/event_handling/resample.py +++ /dev/null @@ -1,49 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - - -# A class that will downsample the data and recompute when zoomed. -class DataDisplayDownsampler(object): - def __init__(self, xdata, ydata): - self.origYData = ydata - self.origXData = xdata - self.ratio = 5 - self.delta = xdata[-1] - xdata[0] - - def downsample(self, xstart, xend): - # Very simple downsampling that takes the points within the range - # and picks every Nth point - mask = (self.origXData > xstart) & (self.origXData < xend) - xdata = self.origXData[mask] - xdata = xdata[::self.ratio] - - ydata = self.origYData[mask] - ydata = ydata[::self.ratio] - - return xdata, ydata - - def update(self, ax): - # Update the line - lims = ax.viewLim - if np.abs(lims.width - self.delta) > 1e-8: - self.delta = lims.width - xstart, xend = lims.intervalx - self.line.set_data(*self.downsample(xstart, xend)) - ax.figure.canvas.draw_idle() - -# Create a signal -xdata = np.linspace(16, 365, 365-16) -ydata = np.sin(2*np.pi*xdata/153) + np.cos(2*np.pi*xdata/127) - -d = DataDisplayDownsampler(xdata, ydata) - -fig, ax = plt.subplots() - -# Hook up the line -d.line, = ax.plot(xdata, ydata, 'o-') -ax.set_autoscale_on(False) # Otherwise, infinite loop - -# Connect for changing the view limits -ax.callbacks.connect('xlim_changed', d.update) - -plt.show() diff --git a/examples/event_handling/test_mouseclicks.py b/examples/event_handling/test_mouseclicks.py deleted file mode 100755 index bfd26ab494cf..000000000000 --- a/examples/event_handling/test_mouseclicks.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import print_function - -import matplotlib -#matplotlib.use("WxAgg") -#matplotlib.use("TkAgg") -#matplotlib.use("GTKAgg") -#matplotlib.use("Qt4Agg") -#matplotlib.use("MacOSX") -import matplotlib.pyplot as plt - -#print("***** TESTING WITH BACKEND: %s"%matplotlib.get_backend() + " *****") - - -def OnClick(event): - if event.dblclick: - print("DBLCLICK", event) - else: - print("DOWN ", event) - - -def OnRelease(event): - print("UP ", event) - - -fig = plt.gcf() -cid_up = fig.canvas.mpl_connect('button_press_event', OnClick) -cid_down = fig.canvas.mpl_connect('button_release_event', OnRelease) - -plt.gca().text(0.5, 0.5, "Click on the canvas to test mouse events.", - ha="center", va="center") - -plt.show() diff --git a/examples/event_handling/timers.py b/examples/event_handling/timers.py deleted file mode 100644 index b9e67e22348c..000000000000 --- a/examples/event_handling/timers.py +++ /dev/null @@ -1,29 +0,0 @@ -# Simple example of using general timer objects. This is used to update -# the time placed in the title of the figure. -import matplotlib.pyplot as plt -import numpy as np -from datetime import datetime - - -def update_title(axes): - axes.set_title(datetime.now()) - axes.figure.canvas.draw() - -fig, ax = plt.subplots() - -x = np.linspace(-3, 3) -ax.plot(x, x*x) - -# Create a new timer object. Set the interval to 100 milliseconds -# (1000 is default) and tell the timer what function should be called. -timer = fig.canvas.new_timer(interval=100) -timer.add_callback(update_title, ax) -timer.start() - -# Or could start the timer on first figure draw -#def start_timer(evt): -# timer.start() -# fig.canvas.mpl_disconnect(drawid) -#drawid = fig.canvas.mpl_connect('draw_event', start_timer) - -plt.show() diff --git a/examples/event_handling/trifinder_event_demo.py b/examples/event_handling/trifinder_event_demo.py deleted file mode 100644 index 0c45c78c4df5..000000000000 --- a/examples/event_handling/trifinder_event_demo.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Example showing the use of a TriFinder object. As the mouse is moved over the -triangulation, the triangle under the cursor is highlighted and the index of -the triangle is displayed in the plot title. -""" -import matplotlib.pyplot as plt -from matplotlib.tri import Triangulation -from matplotlib.patches import Polygon -import numpy as np -import math - - -def update_polygon(tri): - if tri == -1: - points = [0, 0, 0] - else: - points = triangulation.triangles[tri] - xs = triangulation.x[points] - ys = triangulation.y[points] - polygon.set_xy(list(zip(xs, ys))) - - -def motion_notify(event): - if event.inaxes is None: - tri = -1 - else: - tri = trifinder(event.xdata, event.ydata) - update_polygon(tri) - plt.title('In triangle %i' % tri) - event.canvas.draw() - - -# Create a Triangulation. -n_angles = 16 -n_radii = 5 -min_radius = 0.25 -radii = np.linspace(min_radius, 0.95, n_radii) -angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += math.pi / n_angles -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -triangulation = Triangulation(x, y) -xmid = x[triangulation.triangles].mean(axis=1) -ymid = y[triangulation.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triangulation.set_mask(mask) - -# Use the triangulation's default TriFinder object. -trifinder = triangulation.get_trifinder() - -# Setup plot and callbacks. -plt.subplot(111, aspect='equal') -plt.triplot(triangulation, 'bo-') -polygon = Polygon([[0, 0], [0, 0]], facecolor='y') # dummy data for xs,ys -update_polygon(-1) -plt.gca().add_patch(polygon) -plt.gcf().canvas.mpl_connect('motion_notify_event', motion_notify) -plt.show() diff --git a/examples/event_handling/viewlims.py b/examples/event_handling/viewlims.py deleted file mode 100644 index 18dba5e21b08..000000000000 --- a/examples/event_handling/viewlims.py +++ /dev/null @@ -1,79 +0,0 @@ -# Creates two identical panels. Zooming in on the right panel will show -# a rectangle in the first panel, denoting the zoomed region. -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.patches import Rectangle - - -# We just subclass Rectangle so that it can be called with an Axes -# instance, causing the rectangle to update its shape to match the -# bounds of the Axes -class UpdatingRect(Rectangle): - def __call__(self, ax): - self.set_bounds(*ax.viewLim.bounds) - ax.figure.canvas.draw_idle() - - -# A class that will regenerate a fractal set as we zoom in, so that you -# can actually see the increasing detail. A box in the left panel will show -# the area to which we are zoomed. -class MandlebrotDisplay(object): - def __init__(self, h=500, w=500, niter=50, radius=2., power=2): - self.height = h - self.width = w - self.niter = niter - self.radius = radius - self.power = power - - def __call__(self, xstart, xend, ystart, yend): - self.x = np.linspace(xstart, xend, self.width) - self.y = np.linspace(ystart, yend, self.height).reshape(-1, 1) - c = self.x + 1.0j * self.y - threshold_time = np.zeros((self.height, self.width)) - z = np.zeros(threshold_time.shape, dtype=np.complex) - mask = np.ones(threshold_time.shape, dtype=np.bool) - for i in range(self.niter): - z[mask] = z[mask]**self.power + c[mask] - mask = (np.abs(z) < self.radius) - threshold_time += mask - return threshold_time - - def ax_update(self, ax): - ax.set_autoscale_on(False) # Otherwise, infinite loop - - # Get the number of points from the number of pixels in the window - dims = ax.axesPatch.get_window_extent().bounds - self.width = int(dims[2] + 0.5) - self.height = int(dims[2] + 0.5) - - # Get the range for the new area - xstart, ystart, xdelta, ydelta = ax.viewLim.bounds - xend = xstart + xdelta - yend = ystart + ydelta - - # Update the image object with our new data and extent - im = ax.images[-1] - im.set_data(self.__call__(xstart, xend, ystart, yend)) - im.set_extent((xstart, xend, ystart, yend)) - ax.figure.canvas.draw_idle() - -md = MandlebrotDisplay() -Z = md(-2., 0.5, -1.25, 1.25) - -fig1, (ax1, ax2) = plt.subplots(1, 2) -ax1.imshow(Z, origin='lower', extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) -ax2.imshow(Z, origin='lower', extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) - -rect = UpdatingRect([0, 0], 0, 0, facecolor='None', edgecolor='black', linewidth=1.0) -rect.set_bounds(*ax2.viewLim.bounds) -ax1.add_patch(rect) - -# Connect for changing the view limits -ax2.callbacks.connect('xlim_changed', rect) -ax2.callbacks.connect('ylim_changed', rect) - -ax2.callbacks.connect('xlim_changed', md.ax_update) -ax2.callbacks.connect('ylim_changed', md.ax_update) -ax2.set_title("Zoom here") - -plt.show() diff --git a/examples/event_handling/zoom_window.py b/examples/event_handling/zoom_window.py deleted file mode 100644 index 130074aaa1bb..000000000000 --- a/examples/event_handling/zoom_window.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -This example shows how to connect events in one window, for example, a mouse -press, to another figure window. - -If you click on a point in the first window, the z and y limits of the -second will be adjusted so that the center of the zoom in the second -window will be the x,y coordinates of the clicked point. - -Note the diameter of the circles in the scatter are defined in -points**2, so their size is independent of the zoom -""" -from matplotlib.pyplot import figure, show -import numpy -figsrc = figure() -figzoom = figure() - -axsrc = figsrc.add_subplot(111, xlim=(0, 1), ylim=(0, 1), autoscale_on=False) -axzoom = figzoom.add_subplot(111, xlim=(0.45, 0.55), ylim=(0.4, .6), - autoscale_on=False) -axsrc.set_title('Click to zoom') -axzoom.set_title('zoom window') -x, y, s, c = numpy.random.rand(4, 200) -s *= 200 - - -axsrc.scatter(x, y, s, c) -axzoom.scatter(x, y, s, c) - - -def onpress(event): - if event.button != 1: - return - x, y = event.xdata, event.ydata - axzoom.set_xlim(x - 0.1, x + 0.1) - axzoom.set_ylim(y - 0.1, y + 0.1) - figzoom.canvas.draw() - -figsrc.canvas.mpl_connect('button_press_event', onpress) -show() diff --git a/examples/images_contours_and_fields/contourf_log.py b/examples/images_contours_and_fields/contourf_log.py deleted file mode 100644 index 1cf354fb2e39..000000000000 --- a/examples/images_contours_and_fields/contourf_log.py +++ /dev/null @@ -1,47 +0,0 @@ -''' -Demonstrate use of a log color scale in contourf -''' - -import matplotlib.pyplot as plt -import numpy as np -from numpy import ma -from matplotlib import colors, ticker, cm -from matplotlib.mlab import bivariate_normal - -N = 100 -x = np.linspace(-3.0, 3.0, N) -y = np.linspace(-2.0, 2.0, N) - -X, Y = np.meshgrid(x, y) - -# A low hump with a spike coming out of the top right. -# Needs to have z/colour axis on a log scale so we see both hump and spike. -# linear scale only shows the spike. -z = (bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) - + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)) - -# Put in some negative values (lower left corner) to cause trouble with logs: -z[:5, :5] = -1 - -# The following is not strictly essential, but it will eliminate -# a warning. Comment it out to see the warning. -z = ma.masked_where(z <= 0, z) - - -# Automatic selection of levels works; setting the -# log locator tells contourf to use a log scale: -fig, ax = plt.subplots() -cs = ax.contourf(X, Y, z, locator=ticker.LogLocator(), cmap=cm.PuBu_r) - -# Alternatively, you can manually set the levels -# and the norm: -#lev_exp = np.arange(np.floor(np.log10(z.min())-1), -# np.ceil(np.log10(z.max())+1)) -#levs = np.power(10, lev_exp) -#cs = P.contourf(X, Y, z, levs, norm=colors.LogNorm()) - -# The 'extend' kwarg does not work yet with a log scale. - -cbar = fig.colorbar(cs) - -plt.show() diff --git a/examples/images_contours_and_fields/image_demo.py b/examples/images_contours_and_fields/image_demo.py deleted file mode 100644 index a4d709b2ebc0..000000000000 --- a/examples/images_contours_and_fields/image_demo.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Simple demo of the imshow function. -""" -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -image_file = cbook.get_sample_data('ada.png') -image = plt.imread(image_file) - -fig, ax = plt.subplots() -ax.imshow(image) -ax.axis('off') # clear x- and y-axes -plt.show() diff --git a/examples/images_contours_and_fields/image_demo_clip_path.py b/examples/images_contours_and_fields/image_demo_clip_path.py deleted file mode 100644 index de1d9599c83a..000000000000 --- a/examples/images_contours_and_fields/image_demo_clip_path.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Demo of image that's been clipped by a circular patch. -""" -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import matplotlib.cbook as cbook - - -image_file = cbook.get_sample_data('grace_hopper.png') -image = plt.imread(image_file) - -fig, ax = plt.subplots() -im = ax.imshow(image) -patch = patches.Circle((260, 200), radius=200, transform=ax.transData) -im.set_clip_path(patch) - -ax.axis('off') -plt.show() diff --git a/examples/images_contours_and_fields/interpolation_methods.py b/examples/images_contours_and_fields/interpolation_methods.py deleted file mode 100644 index 4977368b49ae..000000000000 --- a/examples/images_contours_and_fields/interpolation_methods.py +++ /dev/null @@ -1,33 +0,0 @@ -''' -Show all different interpolation methods for imshow -''' - -import matplotlib.pyplot as plt -import numpy as np - -# from the docs: - -# If interpolation is None, default to rc image.interpolation. See also -# the filternorm and filterrad parameters. If interpolation is 'none', then -# no interpolation is performed on the Agg, ps and pdf backends. Other -# backends will fall back to 'nearest'. -# -# http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow - -methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16', - 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', - 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos'] - -np.random.seed(0) -grid = np.random.rand(4, 4) - -fig, axes = plt.subplots(3, 6, figsize=(12, 6), - subplot_kw={'xticks': [], 'yticks': []}) - -fig.subplots_adjust(hspace=0.3, wspace=0.05) - -for ax, interp_method in zip(axes.flat, methods): - ax.imshow(grid, interpolation=interp_method, cmap='viridis') - ax.set_title(interp_method) - -plt.show() diff --git a/examples/images_contours_and_fields/interpolation_none_vs_nearest.py b/examples/images_contours_and_fields/interpolation_none_vs_nearest.py deleted file mode 100644 index 487b4017471d..000000000000 --- a/examples/images_contours_and_fields/interpolation_none_vs_nearest.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Displays the difference between interpolation = 'none' and -interpolation = 'nearest'. - -Interpolation = 'none' and interpolation = 'nearest' are equivalent when -converting a figure to an image file, such as a PNG. -Interpolation = 'none' and interpolation = 'nearest' behave quite -differently, however, when converting a figure to a vector graphics file, -such as a PDF. As shown, Interpolation = 'none' works well when a big -image is scaled down, while interpolation = 'nearest' works well when a -small image is blown up. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -# Load big image -big_im_path = cbook.get_sample_data('necked_tensile_specimen.png') -big_im = plt.imread(big_im_path) -# Define small image -small_im = np.array([[0.25, 0.75, 1.0, 0.75], [0.1, 0.65, 0.5, 0.4], - [0.6, 0.3, 0.0, 0.2], [0.7, 0.9, 0.4, 0.6]]) - -# Create a 2x2 table of plots -fig, axes = plt.subplots(figsize=[8.0, 7.5], ncols=2, nrows=2) - -axes[0, 0].imshow(big_im, interpolation='none') -axes[0, 1].imshow(big_im, interpolation='nearest') -axes[1, 0].imshow(small_im, interpolation='none') -axes[1, 1].imshow(small_im, interpolation='nearest') -fig.subplots_adjust(left=0.24, wspace=0.2, hspace=0.1, - bottom=0.05, top=0.86) - -# Label the rows and columns of the table -fig.text(0.03, 0.645, 'Big Image\nScaled Down', ha='left') -fig.text(0.03, 0.225, 'Small Image\nBlown Up', ha='left') -fig.text(0.383, 0.90, "Interpolation = 'none'", ha='center') -fig.text(0.75, 0.90, "Interpolation = 'nearest'", ha='center') - -# If you were going to run this example on your local machine, you -# would save the figure as a PNG, save the same figure as a PDF, and -# then compare them. The following code would suffice. -txt = fig.text(0.452, 0.95, 'Saved as a PNG', fontsize=18) -# plt.savefig('None_vs_nearest-png.png') -# txt.set_text('Saved as a PDF') -# plt.savefig('None_vs_nearest-pdf.pdf') - -# Here, however, we need to display the PDF on a webpage, which means -# the PDF must be converted into an image. For the purposes of this -# example, the 'Nearest_vs_none-pdf.pdf' has been pre-converted into -#'Nearest_vs_none-pdf.png' at 80 dpi. We simply need to load and -# display it. -# The conversion is done with: -# gs -dNOPAUSE -dBATCH -dDOINTERPOLATE -sDEVICE=pngalpha \ -# -sOutputFile=None_vs_nearest-pdf.png -r80 None_vs_nearest-pdf.pdf -pdf_im_path = cbook.get_sample_data('None_vs_nearest-pdf.png') -pdf_im = plt.imread(pdf_im_path) -fig2 = plt.figure(figsize=[8.0, 7.5]) -fig2.figimage(pdf_im) - -plt.show() diff --git a/examples/images_contours_and_fields/pcolormesh_levels.py b/examples/images_contours_and_fields/pcolormesh_levels.py deleted file mode 100644 index 8e5015576399..000000000000 --- a/examples/images_contours_and_fields/pcolormesh_levels.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Shows how to combine Normalization and Colormap instances to draw -"levels" in pcolor, pcolormesh and imshow type plots in a similar -way to the levels keyword argument to contour/contourf. - -""" - -import matplotlib.pyplot as plt -from matplotlib.colors import BoundaryNorm -from matplotlib.ticker import MaxNLocator -import numpy as np - - -# make these smaller to increase the resolution -dx, dy = 0.05, 0.05 - -# generate 2 2d grids for the x & y bounds -y, x = np.mgrid[slice(1, 5 + dy, dy), - slice(1, 5 + dx, dx)] - -z = np.sin(x)**10 + np.cos(10 + y*x) * np.cos(x) - -# x and y are bounds, so z should be the value *inside* those bounds. -# Therefore, remove the last value from the z array. -z = z[:-1, :-1] -levels = MaxNLocator(nbins=15).tick_values(z.min(), z.max()) - - -# pick the desired colormap, sensible levels, and define a normalization -# instance which takes data values and translates those into levels. -cmap = plt.get_cmap('PiYG') -norm = BoundaryNorm(levels, ncolors=cmap.N, clip=True) - -fig, (ax0, ax1) = plt.subplots(nrows=2) - -im = ax0.pcolormesh(x, y, z, cmap=cmap, norm=norm) -fig.colorbar(im, ax=ax0) -ax0.set_title('pcolormesh with levels') - - -# contours are *point* based plots, so convert our bound into point -# centers -cf = ax1.contourf(x[:-1, :-1] + dx/2., - y[:-1, :-1] + dy/2., z, levels=levels, - cmap=cmap) -fig.colorbar(cf, ax=ax1) -ax1.set_title('contourf with levels') - -# adjust spacing between subplots so `ax1` title and `ax0` tick labels -# don't overlap -fig.tight_layout() - -plt.show() diff --git a/examples/images_contours_and_fields/streamplot_demo_features.py b/examples/images_contours_and_fields/streamplot_demo_features.py deleted file mode 100644 index 5c48527fa232..000000000000 --- a/examples/images_contours_and_fields/streamplot_demo_features.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Demo of the `streamplot` function. - -A streamplot, or streamline plot, is used to display 2D vector fields. This -example shows a few features of the stream plot function: - - * Varying the color along a streamline. - * Varying the density of streamlines. - * Varying the line width along a stream line. -""" -import numpy as np -import matplotlib.pyplot as plt - -Y, X = np.mgrid[-3:3:100j, -3:3:100j] -U = -1 - X**2 + Y -V = 1 + X - Y**2 -speed = np.sqrt(U*U + V*V) - -fig0, ax0 = plt.subplots() -strm = ax0.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=plt.cm.autumn) -fig0.colorbar(strm.lines) - -fig1, (ax1, ax2) = plt.subplots(ncols=2) -ax1.streamplot(X, Y, U, V, density=[0.5, 1]) - -lw = 5*speed / speed.max() -ax2.streamplot(X, Y, U, V, density=0.6, color='k', linewidth=lw) - -plt.show() diff --git a/examples/images_contours_and_fields/streamplot_demo_masking.py b/examples/images_contours_and_fields/streamplot_demo_masking.py deleted file mode 100644 index 809d13b002fa..000000000000 --- a/examples/images_contours_and_fields/streamplot_demo_masking.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Demo of the streamplot function with masking. - -This example shows how streamlines created by the streamplot function skips -masked regions and NaN values. -""" -import numpy as np -import matplotlib.pyplot as plt - -w = 3 -Y, X = np.mgrid[-w:w:100j, -w:w:100j] -U = -1 - X**2 + Y -V = 1 + X - Y**2 -speed = np.sqrt(U*U + V*V) - -mask = np.zeros(U.shape, dtype=bool) -mask[40:60, 40:60] = 1 -U = np.ma.array(U, mask=mask) -U[:20, :20] = np.nan - -fig, ax = plt.subplots() -ax.streamplot(X, Y, U, V, color='r') - -ax.imshow(~mask, extent=(-w, w, -w, w), alpha=0.5, - interpolation='nearest', cmap=plt.cm.gray) - -plt.show() diff --git a/examples/images_contours_and_fields/streamplot_demo_start_points.py b/examples/images_contours_and_fields/streamplot_demo_start_points.py deleted file mode 100644 index 0868ecdfd09e..000000000000 --- a/examples/images_contours_and_fields/streamplot_demo_start_points.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Demo of the streamplot function with starting points. - -This example shows how to fix the streamlines that are plotted, by passing -an array of seed points to the `start_points` keyword argument. -""" -import numpy as np -import matplotlib.pyplot as plt - -Y, X = np.mgrid[-3:3:100j, -3:3:100j] -U = -1 - X**2 + Y -V = 1 + X - Y**2 - -# 5 points along the first diagonal and a point in the left upper quadrant -seed_points = np.array([[-2, -1, 0, 1, 2, -1], [-2, -1, 0, 1, 2, 2]]) - -fig, ax = plt.subplots() -strm = ax.streamplot(X, Y, U, V, color=U, linewidth=2, - cmap=plt.cm.autumn, start_points=seed_points.T) -fig.colorbar(strm.lines) - -ax.plot(seed_points[0], seed_points[1], 'bo') - -ax.axis((-3, 3, -3, 3)) - -plt.show() diff --git a/examples/lines_bars_and_markers/barh_demo.py b/examples/lines_bars_and_markers/barh_demo.py deleted file mode 100644 index 64601cc9d060..000000000000 --- a/examples/lines_bars_and_markers/barh_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Simple demo of a horizontal bar chart. -""" -import matplotlib.pyplot as plt -import numpy as np - - -plt.rcdefaults() -fig, ax = plt.subplots() - -# Example data -people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim') -y_pos = np.arange(len(people)) -performance = 3 + 10 * np.random.rand(len(people)) -error = np.random.rand(len(people)) - -ax.barh(y_pos, performance, xerr=error, align='center', - color='green', ecolor='black') -ax.set_yticks(y_pos) -ax.set_yticklabels(people) -ax.invert_yaxis() # labels read top-to-bottom -ax.set_xlabel('Performance') -ax.set_title('How fast do you want to go today?') - -plt.show() diff --git a/examples/lines_bars_and_markers/fill_demo.py b/examples/lines_bars_and_markers/fill_demo.py deleted file mode 100644 index 8e1d1fe292f9..000000000000 --- a/examples/lines_bars_and_markers/fill_demo.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Simple demo of the fill function. -""" -import numpy as np -import matplotlib.pyplot as plt - -x = np.linspace(0, 1, 500) -y = np.sin(4 * np.pi * x) * np.exp(-5 * x) - -fig, ax = plt.subplots() - -ax.fill(x, y, zorder=10) -ax.grid(True, zorder=5) -plt.show() diff --git a/examples/lines_bars_and_markers/fill_demo_features.py b/examples/lines_bars_and_markers/fill_demo_features.py deleted file mode 100644 index ddeb8ecba098..000000000000 --- a/examples/lines_bars_and_markers/fill_demo_features.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Demo of the fill function with a few features. - -In addition to the basic fill plot, this demo shows a few optional features: - - * Multiple curves with a single command. - * Setting the fill color. - * Setting the opacity (alpha value). -""" -import numpy as np -import matplotlib.pyplot as plt - -x = np.linspace(0, 2 * np.pi, 500) -y1 = np.sin(x) -y2 = np.sin(3 * x) - -fig, ax = plt.subplots() -ax.fill(x, y1, 'b', x, y2, 'r', alpha=0.3) -plt.show() diff --git a/examples/lines_bars_and_markers/line_demo_dash_control.py b/examples/lines_bars_and_markers/line_demo_dash_control.py deleted file mode 100644 index 432962db58ec..000000000000 --- a/examples/lines_bars_and_markers/line_demo_dash_control.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Demo of a simple plot with a custom dashed line. - -A Line object's ``set_dashes`` method allows you to specify dashes with -a series of on/off lengths (in points). -""" -import numpy as np -import matplotlib.pyplot as plt - - -x = np.linspace(0, 10, 500) -dashes = [10, 5, 100, 5] # 10 points on, 5 off, 100 on, 5 off - -fig, ax = plt.subplots() -line1, = ax.plot(x, np.sin(x), '--', linewidth=2, - label='Dashes set retroactively') -line1.set_dashes(dashes) - -line2, = ax.plot(x, -1 * np.sin(x), dashes=[30, 5, 10, 5], - label='Dashes set proactively') - -ax.legend(loc='lower right') -plt.show() diff --git a/examples/lines_bars_and_markers/line_styles_reference.py b/examples/lines_bars_and_markers/line_styles_reference.py deleted file mode 100644 index cde61ba20426..000000000000 --- a/examples/lines_bars_and_markers/line_styles_reference.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Reference for line-styles included with Matplotlib. -""" -import numpy as np -import matplotlib.pyplot as plt - - -color = 'cornflowerblue' -points = np.ones(5) # Draw 5 points for each line -text_style = dict(horizontalalignment='right', verticalalignment='center', - fontsize=12, fontdict={'family': 'monospace'}) - - -def format_axes(ax): - ax.margins(0.2) - ax.set_axis_off() - - -def nice_repr(text): - return repr(text).lstrip('u') - - -# Plot all line styles. -fig, ax = plt.subplots() - -linestyles = ['-', '--', '-.', ':'] -for y, linestyle in enumerate(linestyles): - ax.text(-0.1, y, nice_repr(linestyle), **text_style) - ax.plot(y * points, linestyle=linestyle, color=color, linewidth=3) - format_axes(ax) - ax.set_title('line styles') - -plt.show() diff --git a/examples/lines_bars_and_markers/linestyles.py b/examples/lines_bars_and_markers/linestyles.py deleted file mode 100644 index 524134dab6fd..000000000000 --- a/examples/lines_bars_and_markers/linestyles.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Different linestyles copying those of Tikz/PGF -""" -import numpy as np -import matplotlib.pyplot as plt -from collections import OrderedDict -from matplotlib.transforms import blended_transform_factory - -linestyles = OrderedDict( - [('solid', (0, ())), - ('loosely dotted', (0, (1, 10))), - ('dotted', (0, (1, 5))), - ('densely dotted', (0, (1, 1))), - - ('loosely dashed', (0, (5, 10))), - ('dashed', (0, (5, 5))), - ('densely dashed', (0, (5, 1))), - - ('loosely dashdotted', (0, (3, 10, 1, 10))), - ('dashdotted', (0, (3, 5, 1, 5))), - ('densely dashdotted', (0, (3, 1, 1, 1))), - - ('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))), - ('dashdotdotted', (0, (3, 5, 1, 5, 1, 5))), - ('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]) - - -plt.figure(figsize=(10, 6)) -ax = plt.subplot(1, 1, 1) - -X, Y = np.linspace(0, 100, 10), np.zeros(10) -for i, (name, linestyle) in enumerate(linestyles.items()): - ax.plot(X, Y+i, linestyle=linestyle, linewidth=1.5, color='black') - -ax.set_ylim(-0.5, len(linestyles)-0.5) -plt.yticks(np.arange(len(linestyles)), linestyles.keys()) -plt.xticks([]) - -# For each line style, add a text annotation with a small offset from -# the reference point (0 in Axes coords, y tick value in Data coords). -reference_transform = blended_transform_factory(ax.transAxes, ax.transData) -for i, (name, linestyle) in enumerate(linestyles.items()): - ax.annotate(str(linestyle), xy=(0.0, i), xycoords=reference_transform, - xytext=(-6, -12), textcoords='offset points', color="blue", - fontsize=8, ha="right", family="monospace") - -plt.tight_layout() -plt.show() diff --git a/examples/lines_bars_and_markers/marker_fillstyle_reference.py b/examples/lines_bars_and_markers/marker_fillstyle_reference.py deleted file mode 100644 index e7b36e605519..000000000000 --- a/examples/lines_bars_and_markers/marker_fillstyle_reference.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Reference for marker fill-styles included with Matplotlib. -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.lines import Line2D - - -points = np.ones(5) # Draw 3 points for each line -text_style = dict(horizontalalignment='right', verticalalignment='center', - fontsize=12, fontdict={'family': 'monospace'}) -marker_style = dict(color='cornflowerblue', linestyle=':', marker='o', - markersize=15, markerfacecoloralt='gray') - - -def format_axes(ax): - ax.margins(0.2) - ax.set_axis_off() - - -def nice_repr(text): - return repr(text).lstrip('u') - - -fig, ax = plt.subplots() - -# Plot all fill styles. -for y, fill_style in enumerate(Line2D.fillStyles): - ax.text(-0.5, y, nice_repr(fill_style), **text_style) - ax.plot(y * points, fillstyle=fill_style, **marker_style) - format_axes(ax) - ax.set_title('fill style') - -plt.show() diff --git a/examples/lines_bars_and_markers/marker_reference.py b/examples/lines_bars_and_markers/marker_reference.py deleted file mode 100644 index ad7db0e89023..000000000000 --- a/examples/lines_bars_and_markers/marker_reference.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -Reference for filled- and unfilled-marker types included with Matplotlib. -""" -from six import iteritems -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.lines import Line2D - - -points = np.ones(3) # Draw 3 points for each line -text_style = dict(horizontalalignment='right', verticalalignment='center', - fontsize=12, fontdict={'family': 'monospace'}) -marker_style = dict(linestyle=':', color='cornflowerblue', markersize=10) - - -def format_axes(ax): - ax.margins(0.2) - ax.set_axis_off() - - -def nice_repr(text): - return repr(text).lstrip('u') - - -def split_list(a_list): - i_half = len(a_list) // 2 - return (a_list[:i_half], a_list[i_half:]) - - -# Plot all un-filled markers -# -------------------------- - -fig, axes = plt.subplots(ncols=2) - -# Filter out filled markers and marker settings that do nothing. -# We use iteritems from six to make sure that we get an iterator -# in both python 2 and 3 -unfilled_markers = [m for m, func in iteritems(Line2D.markers) - if func != 'nothing' and m not in Line2D.filled_markers] -# Reverse-sort for pretty. We use our own sort key which is essentially -# a python3 compatible reimplementation of python2 sort. -unfilled_markers = sorted(unfilled_markers, - key=lambda x: (str(type(x)), str(x)))[::-1] -for ax, markers in zip(axes, split_list(unfilled_markers)): - for y, marker in enumerate(markers): - ax.text(-0.5, y, nice_repr(marker), **text_style) - ax.plot(y * points, marker=marker, **marker_style) - format_axes(ax) -fig.suptitle('un-filled markers', fontsize=14) - - -# Plot all filled markers. -# ------------------------ - -fig, axes = plt.subplots(ncols=2) -for ax, markers in zip(axes, split_list(Line2D.filled_markers)): - for y, marker in enumerate(markers): - ax.text(-0.5, y, nice_repr(marker), **text_style) - ax.plot(y * points, marker=marker, **marker_style) - format_axes(ax) -fig.suptitle('filled markers', fontsize=14) - -plt.show() diff --git a/examples/lines_bars_and_markers/scatter_with_legend.py b/examples/lines_bars_and_markers/scatter_with_legend.py deleted file mode 100644 index 8f86bc51f8ed..000000000000 --- a/examples/lines_bars_and_markers/scatter_with_legend.py +++ /dev/null @@ -1,24 +0,0 @@ -''' -Demo of a scatter plot (scatter) with a legend. - -Also demonstrates how transparency of the markers -can be adjusted by giving ``alpha`` a value between -0 and 1. -''' - -import matplotlib.pyplot as plt -from numpy.random import rand - - -fig, ax = plt.subplots() -for color in ['red', 'green', 'blue']: - n = 750 - x, y = rand(2, n) - scale = 200.0 * rand(n) - ax.scatter(x, y, c=color, s=scale, label=color, - alpha=0.3, edgecolors='none') - -ax.legend() -ax.grid(True) - -plt.show() diff --git a/examples/misc/contour_manual.py b/examples/misc/contour_manual.py deleted file mode 100644 index 37d06d0b17e8..000000000000 --- a/examples/misc/contour_manual.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Example of displaying your own contour lines and polygons using ContourSet. -""" -import matplotlib.pyplot as plt -from matplotlib.contour import ContourSet -import matplotlib.cm as cm - -# Contour lines for each level are a list/tuple of polygons. -lines0 = [[[0, 0], [0, 4]]] -lines1 = [[[2, 0], [1, 2], [1, 3]]] -lines2 = [[[3, 0], [3, 2]], [[3, 3], [3, 4]]] # Note two lines. - -# Filled contours between two levels are also a list/tuple of polygons. -# Points can be ordered clockwise or anticlockwise. -filled01 = [[[0, 0], [0, 4], [1, 3], [1, 2], [2, 0]]] -filled12 = [[[2, 0], [3, 0], [3, 2], [1, 3], [1, 2]], # Note two polygons. - [[1, 4], [3, 4], [3, 3]]] - - -plt.figure() - -# Filled contours using filled=True. -cs = ContourSet(plt.gca(), [0, 1, 2], [filled01, filled12], filled=True, cmap=cm.bone) -cbar = plt.colorbar(cs) - -# Contour lines (non-filled). -lines = ContourSet(plt.gca(), [0, 1, 2], [lines0, lines1, lines2], cmap=cm.cool, - linewidths=3) -cbar.add_lines(lines) - -plt.axis([-0.5, 3.5, -0.5, 4.5]) -plt.title('User-specified contours') - - -# Multiple filled contour lines can be specified in a single list of polygon -# vertices along with a list of vertex kinds (code types) as described in the -# Path class. This is particularly useful for polygons with holes. -# Here a code type of 1 is a MOVETO, and 2 is a LINETO. - -plt.figure() -filled01 = [[[0, 0], [3, 0], [3, 3], [0, 3], [1, 1], [1, 2], [2, 2], [2, 1]]] -kinds01 = [[1, 2, 2, 2, 1, 2, 2, 2]] -cs = ContourSet(plt.gca(), [0, 1], [filled01], [kinds01], filled=True) -cbar = plt.colorbar(cs) - -plt.axis([-0.5, 3.5, -0.5, 3.5]) -plt.title('User specified filled contours with holes') - -plt.show() diff --git a/examples/misc/font_indexing.py b/examples/misc/font_indexing.py deleted file mode 100644 index 164573f4527f..000000000000 --- a/examples/misc/font_indexing.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -A little example that shows how the various indexing into the font -tables relate to one another. Mainly for mpl developers.... - -""" -from __future__ import print_function -import matplotlib -from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, KERNING_UNFITTED, KERNING_UNSCALED - - -#fname = '/usr/share/fonts/sfd/FreeSans.ttf' -fname = matplotlib.get_data_path() + '/fonts/ttf/DejaVuSans.ttf' -font = FT2Font(fname) -font.set_charmap(0) - -codes = font.get_charmap().items() -#dsu = [(ccode, glyphind) for ccode, glyphind in codes] -#dsu.sort() -#for ccode, glyphind in dsu: -# try: name = font.get_glyph_name(glyphind) -# except RuntimeError: pass -# else: print('% 4d % 4d %s %s' % (glyphind, ccode, hex(int(ccode)), name)) - - -# make a charname to charcode and glyphind dictionary -coded = {} -glyphd = {} -for ccode, glyphind in codes: - name = font.get_glyph_name(glyphind) - coded[name] = ccode - glyphd[name] = glyphind - -code = coded['A'] -glyph = font.load_char(code) -#print(glyph.bbox) -print(glyphd['A'], glyphd['V'], coded['A'], coded['V']) -print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_DEFAULT)) -print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNFITTED)) -print('AV', font.get_kerning(glyphd['A'], glyphd['V'], KERNING_UNSCALED)) -print('AV', font.get_kerning(glyphd['A'], glyphd['T'], KERNING_UNSCALED)) diff --git a/examples/misc/ftface_props.py b/examples/misc/ftface_props.py deleted file mode 100755 index c39ca5d69814..000000000000 --- a/examples/misc/ftface_props.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import print_function -""" -This is a demo script to show you how to use all the properties of an -FT2Font object. These describe global font properties. For -individual character metrices, use the Glyp object, as returned by -load_char -""" -import matplotlib -import matplotlib.ft2font as ft - - -#fname = '/usr/local/share/matplotlib/VeraIt.ttf' -fname = matplotlib.get_data_path() + '/fonts/ttf/DejaVuSans-Oblique.ttf' -#fname = '/usr/local/share/matplotlib/cmr10.ttf' - -font = ft.FT2Font(fname) - -print('Num faces :', font.num_faces) # number of faces in file -print('Num glyphs :', font.num_glyphs) # number of glyphs in the face -print('Family name :', font.family_name) # face family name -print('Syle name :', font.style_name) # face syle name -print('PS name :', font.postscript_name) # the postscript name -print('Num fixed :', font.num_fixed_sizes) # number of embedded bitmap in face - -# the following are only available if face.scalable -if font.scalable: - # the face global bounding box (xmin, ymin, xmax, ymax) - print('Bbox :', font.bbox) - # number of font units covered by the EM - print('EM :', font.units_per_EM) - # the ascender in 26.6 units - print('Ascender :', font.ascender) - # the descender in 26.6 units - print('Descender :', font.descender) - # the height in 26.6 units - print('Height :', font.height) - # maximum horizontal cursor advance - print('Max adv width :', font.max_advance_width) - # same for vertical layout - print('Max adv height :', font.max_advance_height) - # vertical position of the underline bar - print('Underline pos :', font.underline_position) - # vertical thickness of the underline - print('Underline thickness :', font.underline_thickness) - -for style in ('Italic', - 'Bold', - 'Scalable', - 'Fixed sizes', - 'Fixed width', - 'SFNT', - 'Horizontal', - 'Vertical', - 'Kerning', - 'Fast glyphs', - 'Multiple masters', - 'Glyph names', - 'External stream'): - bitpos = getattr(ft, style.replace(' ', '_').upper()) - 1 - print('%-17s:' % style, bool(font.style_flags & (1 << bitpos))) - -print(dir(font)) - -print(font.get_kerning) diff --git a/examples/misc/image_thumbnail.py b/examples/misc/image_thumbnail.py deleted file mode 100644 index 89a87ecd563e..000000000000 --- a/examples/misc/image_thumbnail.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- noplot -*- -""" -You can use matplotlib to generate thumbnails from existing images. -matplotlib natively supports PNG files on the input side, and other -image types transparently if your have PIL installed -""" - -from __future__ import print_function -# build thumbnails of all images in a directory -import sys -import os -import glob -import matplotlib.image as image - - -if len(sys.argv) != 2: - print('Usage: python %s IMAGEDIR' % __file__) - raise SystemExit -indir = sys.argv[1] -if not os.path.isdir(indir): - print('Could not find input directory "%s"' % indir) - raise SystemExit - -outdir = 'thumbs' -if not os.path.exists(outdir): - os.makedirs(outdir) - -for fname in glob.glob(os.path.join(indir, '*.png')): - basedir, basename = os.path.split(fname) - outfile = os.path.join(outdir, basename) - fig = image.thumbnail(fname, outfile, scale=0.15) - print('saved thumbnail of %s to %s' % (fname, outfile)) diff --git a/examples/misc/multiprocess.py b/examples/misc/multiprocess.py deleted file mode 100644 index 1ac93d646355..000000000000 --- a/examples/misc/multiprocess.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- noplot -*- -# Demo of using multiprocessing for generating data in one process and plotting -# in another. -# Written by Robert Cimrman - -from __future__ import print_function -import time -from multiprocessing import Process, Pipe -import numpy as np - -import matplotlib -matplotlib.use('GtkAgg') -import matplotlib.pyplot as plt -import gobject - - -class ProcessPlotter(object): - def __init__(self): - self.x = [] - self.y = [] - - def terminate(self): - plt.close('all') - - def poll_draw(self): - - def call_back(): - while 1: - if not self.pipe.poll(): - break - - command = self.pipe.recv() - - if command is None: - self.terminate() - return False - - else: - self.x.append(command[0]) - self.y.append(command[1]) - self.ax.plot(self.x, self.y, 'ro') - - self.fig.canvas.draw() - return True - - return call_back - - def __call__(self, pipe): - print('starting plotter...') - - self.pipe = pipe - self.fig, self.ax = plt.subplots() - self.gid = gobject.timeout_add(1000, self.poll_draw()) - - print('...done') - plt.show() - - -class NBPlot(object): - def __init__(self): - self.plot_pipe, plotter_pipe = Pipe() - self.plotter = ProcessPlotter() - self.plot_process = Process(target=self.plotter, - args=(plotter_pipe,)) - self.plot_process.daemon = True - self.plot_process.start() - - def plot(self, finished=False): - send = self.plot_pipe.send - if finished: - send(None) - else: - data = np.random.random(2) - send(data) - - -def main(): - pl = NBPlot() - for ii in range(10): - pl.plot() - time.sleep(0.5) - raw_input('press Enter...') - pl.plot(finished=True) - -if __name__ == '__main__': - main() diff --git a/examples/misc/rasterization_demo.py b/examples/misc/rasterization_demo.py deleted file mode 100644 index 067099c56a28..000000000000 --- a/examples/misc/rasterization_demo.py +++ /dev/null @@ -1,50 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -d = np.arange(100).reshape(10, 10) -x, y = np.meshgrid(np.arange(11), np.arange(11)) - -theta = 0.25*np.pi -xx = x*np.cos(theta) - y*np.sin(theta) -yy = x*np.sin(theta) + y*np.cos(theta) - -fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2) -ax1.set_aspect(1) -ax1.pcolormesh(xx, yy, d) -ax1.set_title("No Rasterization") - -ax2.set_aspect(1) -ax2.set_title("Rasterization") - -m = ax2.pcolormesh(xx, yy, d) -m.set_rasterized(True) - -ax3.set_aspect(1) -ax3.pcolormesh(xx, yy, d) -ax3.text(0.5, 0.5, "Text", alpha=0.2, - va="center", ha="center", size=50, transform=ax3.transAxes) - -ax3.set_title("No Rasterization") - - -ax4.set_aspect(1) -m = ax4.pcolormesh(xx, yy, d) -m.set_zorder(-20) - -ax4.text(0.5, 0.5, "Text", alpha=0.2, - zorder=-15, - va="center", ha="center", size=50, transform=ax4.transAxes) - -ax4.set_rasterization_zorder(-10) - -ax4.set_title("Rasterization z$<-10$") - - -# ax2.title.set_rasterized(True) # should display a warning - -plt.savefig("test_rasterization.pdf", dpi=150) -plt.savefig("test_rasterization.eps", dpi=150) - -if not plt.rcParams["text.usetex"]: - plt.savefig("test_rasterization.svg", dpi=150) - # svg backend currently ignores the dpi diff --git a/examples/misc/rc_traits.py b/examples/misc/rc_traits.py deleted file mode 100644 index e5aa417f4cd3..000000000000 --- a/examples/misc/rc_traits.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- noplot -*- -# Here is some example code showing how to define some representative -# rc properties and construct a matplotlib artist using traits. -# matplotlib does not ship with enthought.traits, so you will need to -# install it separately. - -from __future__ import print_function - -import sys -import os -import re -import traits.api as traits -from matplotlib.cbook import is_string_like -from matplotlib.artist import Artist - -doprint = True -flexible_true_trait = traits.Trait( - True, - {'true': True, 't': True, 'yes': True, 'y': True, 'on': True, True: True, - 'false': False, 'f': False, 'no': False, 'n': False, 'off': False, False: False - }) -flexible_false_trait = traits.Trait(False, flexible_true_trait) - -colors = { - 'c': '#00bfbf', - 'b': '#0000ff', - 'g': '#008000', - 'k': '#000000', - 'm': '#bf00bf', - 'r': '#ff0000', - 'w': '#ffffff', - 'y': '#bfbf00', - 'gold': '#FFD700', - 'peachpuff': '#FFDAB9', - 'navajowhite': '#FFDEAD', - } - - -def hex2color(s): - "Convert hex string (like html uses, eg, #efefef) to a r,g,b tuple" - return tuple([int(n, 16)/255.0 for n in (s[1:3], s[3:5], s[5:7])]) - - -class RGBA(traits.HasTraits): - # r,g,b,a in the range 0-1 with default color 0,0,0,1 (black) - r = traits.Range(0., 1., 0.) - g = traits.Range(0., 1., 0.) - b = traits.Range(0., 1., 0.) - a = traits.Range(0., 1., 1.) - - def __init__(self, r=0., g=0., b=0., a=1.): - self.r = r - self.g = g - self.b = b - self.a = a - - def __repr__(self): - return 'r,g,b,a = (%1.2f, %1.2f, %1.2f, %1.2f)' %\ - (self.r, self.g, self.b, self.a) - - -def tuple_to_rgba(ob, name, val): - tup = [float(x) for x in val] - if len(tup) == 3: - r, g, b = tup - return RGBA(r, g, b) - elif len(tup) == 4: - r, g, b, a = tup - return RGBA(r, g, b, a) - else: - raise ValueError -tuple_to_rgba.info = 'a RGB or RGBA tuple of floats' - - -def hex_to_rgba(ob, name, val): - rgx = re.compile('^#[0-9A-Fa-f]{6}$') - - if not is_string_like(val): - raise TypeError - if rgx.match(val) is None: - raise ValueError - r, g, b = hex2color(val) - return RGBA(r, g, b, 1.0) -hex_to_rgba.info = 'a hex color string' - - -def colorname_to_rgba(ob, name, val): - hex = colors[val.lower()] - r, g, b = hex2color(hex) - return RGBA(r, g, b, 1.0) -colorname_to_rgba.info = 'a named color' - - -def float_to_rgba(ob, name, val): - val = float(val) - return RGBA(val, val, val, 1.) -float_to_rgba.info = 'a grayscale intensity' - - -Color = traits.Trait(RGBA(), float_to_rgba, colorname_to_rgba, RGBA, - hex_to_rgba, tuple_to_rgba) - - -def file_exists(ob, name, val): - fh = file(val, 'r') - return val - -linestyles = ('-', '--', '-.', ':', 'steps', 'None') -TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN = range(4) -linemarkers = (None, '.', ',', 'o', '^', 'v', '<', '>', 's', - '+', 'x', 'd', 'D', '|', '_', 'h', 'H', - 'p', '1', '2', '3', '4', - TICKLEFT, - TICKRIGHT, - TICKUP, - TICKDOWN, - 'None' - ) - - -class LineRC(traits.HasTraits): - linewidth = traits.Float(0.5) - linestyle = traits.Trait(*linestyles) - color = Color - marker = traits.Trait(*linemarkers) - markerfacecolor = Color - markeredgecolor = Color - markeredgewidth = traits.Float(0.5) - markersize = traits.Float(6) - antialiased = flexible_true_trait - data_clipping = flexible_false_trait - - -class PatchRC(traits.HasTraits): - linewidth = traits.Float(1.0) - facecolor = Color - edgecolor = Color - antialiased = flexible_true_trait - -timezones = 'UTC', 'US/Central', 'ES/Eastern' # fixme: and many more -backends = ('GTKAgg', 'Cairo', 'GDK', 'GTK', 'Agg', - 'GTKCairo', 'PS', 'SVG', 'Template', 'TkAgg', - 'WX') - - -class RC(traits.HasTraits): - backend = traits.Trait(*backends) - interactive = flexible_false_trait - toolbar = traits.Trait('toolbar2', 'classic', None) - timezone = traits.Trait(*timezones) - lines = traits.Trait(LineRC()) - patch = traits.Trait(PatchRC()) - -rc = RC() -rc.lines.color = 'r' -if doprint: - print('RC') - rc.print_traits() - print('RC lines') - rc.lines.print_traits() - print('RC patches') - rc.patch.print_traits() - - -class Patch(Artist, traits.HasTraits): - linewidth = traits.Float(0.5) - facecolor = Color - fc = facecolor - edgecolor = Color - fill = flexible_true_trait - - def __init__(self, - edgecolor=None, - facecolor=None, - linewidth=None, - antialiased=None, - fill=1, - **kwargs - ): - Artist.__init__(self) - - if edgecolor is None: - edgecolor = rc.patch.edgecolor - if facecolor is None: - facecolor = rc.patch.facecolor - if linewidth is None: - linewidth = rc.patch.linewidth - if antialiased is None: - antialiased = rc.patch.antialiased - - self.edgecolor = edgecolor - self.facecolor = facecolor - self.linewidth = linewidth - self.antialiased = antialiased - self.fill = fill - - -p = Patch() -p.facecolor = '#bfbf00' -p.edgecolor = 'gold' -p.facecolor = (1, .5, .5, .25) -p.facecolor = 0.25 -p.fill = 'f' -print('p.facecolor', type(p.facecolor), p.facecolor) -print('p.fill', type(p.fill), p.fill) -if p.fill_: - print('fill') -else: - print('no fill') -if doprint: - print() - print('Patch') - p.print_traits() diff --git a/examples/misc/rec_groupby_demo.py b/examples/misc/rec_groupby_demo.py deleted file mode 100644 index 06f2a1cca160..000000000000 --- a/examples/misc/rec_groupby_demo.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import print_function -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.cbook as cbook - -datafile = cbook.get_sample_data('aapl.csv', asfileobj=False) -print('loading', datafile) -r = mlab.csv2rec(datafile) -r.sort() - - -def daily_return(prices): - 'an array of daily returns from price array' - g = np.zeros_like(prices) - g[1:] = (prices[1:] - prices[:-1])/prices[:-1] - return g - - -def volume_code(volume): - 'code the continuous volume data categorically' - ind = np.searchsorted([1e5, 1e6, 5e6, 10e6, 1e7], volume) - return ind - -# a list of (dtype_name, summary_function, output_dtype_name). -# rec_summarize will call on each function on the indicated recarray -# attribute, and the result assigned to output name in the return -# record array. -summaryfuncs = ( - ('date', lambda x: [thisdate.year for thisdate in x], 'years'), - ('date', lambda x: [thisdate.month for thisdate in x], 'months'), - ('date', lambda x: [thisdate.weekday() for thisdate in x], 'weekday'), - ('adj_close', daily_return, 'dreturn'), - ('volume', volume_code, 'volcode'), - ) - -rsum = mlab.rec_summarize(r, summaryfuncs) - -# stats is a list of (dtype_name, function, output_dtype_name). -# rec_groupby will summarize the attribute identified by the -# dtype_name over the groups in the groupby list, and assign the -# result to the output_dtype_name -stats = ( - ('dreturn', len, 'rcnt'), - ('dreturn', np.mean, 'rmean'), - ('dreturn', np.median, 'rmedian'), - ('dreturn', np.std, 'rsigma'), - ) - -# you can summarize over a single variable, like years or months -print('summary by years') -ry = mlab.rec_groupby(rsum, ('years',), stats) -print(mlab. rec2txt(ry)) - -print('summary by months') -rm = mlab.rec_groupby(rsum, ('months',), stats) -print(mlab.rec2txt(rm)) - -# or over multiple variables like years and months -print('summary by year and month') -rym = mlab.rec_groupby(rsum, ('years', 'months'), stats) -print(mlab.rec2txt(rym)) - -print('summary by volume') -rv = mlab.rec_groupby(rsum, ('volcode',), stats) -print(mlab.rec2txt(rv)) diff --git a/examples/misc/rec_join_demo.py b/examples/misc/rec_join_demo.py deleted file mode 100644 index 27dd8d775bb6..000000000000 --- a/examples/misc/rec_join_demo.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import print_function -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.cbook as cbook - -datafile = cbook.get_sample_data('aapl.csv', asfileobj=False) -print('loading', datafile) -r = mlab.csv2rec(datafile) - -r.sort() -r1 = r[-10:] - -# Create a new array -r2 = np.empty(12, dtype=[('date', '|O4'), ('high', float), ('marker', float)]) -r2 = r2.view(np.recarray) -r2.date = r.date[-17:-5] -r2.high = r.high[-17:-5] -r2.marker = np.arange(12) - -print("r1:") -print(mlab.rec2txt(r1)) -print("r2:") -print(mlab.rec2txt(r2)) - -defaults = {'marker': -1, 'close': np.NaN, 'low': -4444.} - -for s in ('inner', 'outer', 'leftouter'): - rec = mlab.rec_join(['date', 'high'], r1, r2, - jointype=s, defaults=defaults) - print("\n%sjoin :\n%s" % (s, mlab.rec2txt(rec))) diff --git a/examples/misc/sample_data_demo.py b/examples/misc/sample_data_demo.py deleted file mode 100644 index 1e60b1e90854..000000000000 --- a/examples/misc/sample_data_demo.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Grab mpl data from the ~/.matplotlib/sample_data cache if it exists, else -fetch it from github and cache it -""" -from __future__ import print_function -import matplotlib.cbook as cbook -import matplotlib.pyplot as plt -fname = cbook.get_sample_data('ada.png', asfileobj=False) - -print('fname', fname) -im = plt.imread(fname) -plt.imshow(im) -plt.show() diff --git a/examples/misc/svg_filter_line.py b/examples/misc/svg_filter_line.py deleted file mode 100644 index 006ca04b6a85..000000000000 --- a/examples/misc/svg_filter_line.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Demonstrate SVG filtering effects which might be used with mpl. - -Note that the filtering effects are only effective if your svg rederer -support it. -""" - -from __future__ import print_function -import matplotlib - -matplotlib.use("Svg") - -import matplotlib.pyplot as plt -import matplotlib.transforms as mtransforms - -fig1 = plt.figure() -ax = fig1.add_axes([0.1, 0.1, 0.8, 0.8]) - -# draw lines -l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-", - mec="b", lw=5, ms=10, label="Line 1") -l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "rs-", - mec="r", lw=5, ms=10, color="r", label="Line 2") - - -for l in [l1, l2]: - - # draw shadows with same lines with slight offset and gray colors. - - xx = l.get_xdata() - yy = l.get_ydata() - shadow, = ax.plot(xx, yy) - shadow.update_from(l) - - # adjust color - shadow.set_color("0.2") - # adjust zorder of the shadow lines so that it is drawn below the - # original lines - shadow.set_zorder(l.get_zorder() - 0.5) - - # offset transform - ot = mtransforms.offset_copy(l.get_transform(), fig1, - x=4.0, y=-6.0, units='points') - - shadow.set_transform(ot) - - # set the id for a later use - shadow.set_gid(l.get_label() + "_shadow") - - -ax.set_xlim(0., 1.) -ax.set_ylim(0., 1.) - -# save the figure as a bytes string in the svg format. -from io import BytesIO -f = BytesIO() -plt.savefig(f, format="svg") - - -import xml.etree.cElementTree as ET - -# filter definition for a gaussian blur -filter_def = """ - - - - - -""" - - -# read in the saved svg -tree, xmlid = ET.XMLID(f.getvalue()) - -# insert the filter definition in the svg dom tree. -tree.insert(0, ET.XML(filter_def)) - -for l in [l1, l2]: - # pick up the svg element with given id - shadow = xmlid[l.get_label() + "_shadow"] - # apply shdow filter - shadow.set("filter", 'url(#dropshadow)') - -fn = "svg_filter_line.svg" -print("Saving '%s'" % fn) -ET.ElementTree(tree).write(fn) diff --git a/examples/misc/svg_filter_pie.py b/examples/misc/svg_filter_pie.py deleted file mode 100644 index 61a2719fce42..000000000000 --- a/examples/misc/svg_filter_pie.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Demonstrate SVG filtering effects which might be used with mpl. -The pie chart drawing code is borrowed from pie_demo.py - -Note that the filtering effects are only effective if your svg rederer -support it. -""" - - -import matplotlib -matplotlib.use("Svg") - -import matplotlib.pyplot as plt -from matplotlib.patches import Shadow - -# make a square figure and axes -fig1 = plt.figure(1, figsize=(6, 6)) -ax = fig1.add_axes([0.1, 0.1, 0.8, 0.8]) - -labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' -fracs = [15, 30, 45, 10] - -explode = (0, 0.05, 0, 0) - -# We want to draw the shadow for each pie but we will not use "shadow" -# option as it does'n save the references to the shadow patches. -pies = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%') - -for w in pies[0]: - # set the id with the label. - w.set_gid(w.get_label()) - - # we don't want to draw the edge of the pie - w.set_ec("none") - -for w in pies[0]: - # create shadow patch - s = Shadow(w, -0.01, -0.01) - s.set_gid(w.get_gid() + "_shadow") - s.set_zorder(w.get_zorder() - 0.1) - ax.add_patch(s) - - -# save -from io import BytesIO -f = BytesIO() -plt.savefig(f, format="svg") - -import xml.etree.cElementTree as ET - - -# filter definition for shadow using a gaussian blur -# and lighteneing effect. -# The lightnening filter is copied from http://www.w3.org/TR/SVG/filters.html - -# I tested it with Inkscape and Firefox3. "Gaussian blur" is supported -# in both, but the lightnening effect only in the inkscape. Also note -# that, inkscape's exporting also may not support it. - -filter_def = """ - - - - - - - - - - - - - - - -""" - - -tree, xmlid = ET.XMLID(f.getvalue()) - -# insert the filter definition in the svg dom tree. -tree.insert(0, ET.XML(filter_def)) - -for i, pie_name in enumerate(labels): - pie = xmlid[pie_name] - pie.set("filter", 'url(#MyFilter)') - - shadow = xmlid[pie_name + "_shadow"] - shadow.set("filter", 'url(#dropshadow)') - -fn = "svg_filter_pie.svg" -print("Saving '%s'" % fn) -ET.ElementTree(tree).write(fn) diff --git a/examples/misc/tight_bbox_test.py b/examples/misc/tight_bbox_test.py deleted file mode 100644 index 2e966e286667..000000000000 --- a/examples/misc/tight_bbox_test.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import print_function -import matplotlib.pyplot as plt -import numpy as np - -ax = plt.axes([0.1, 0.3, 0.5, 0.5]) - -ax.pcolormesh(np.array([[1, 2], [3, 4]])) -plt.yticks([0.5, 1.5], ["long long tick label", - "tick label"]) -plt.ylabel("My y-label") -plt.title("Check saved figures for their bboxes") -for ext in ["png", "pdf", "svg", "svgz", "eps"]: - print("saving tight_bbox_test.%s" % (ext,)) - plt.savefig("tight_bbox_test.%s" % (ext,), bbox_inches="tight") -plt.show() diff --git a/examples/mplot3d/2dcollections3d_demo.py b/examples/mplot3d/2dcollections3d_demo.py deleted file mode 100644 index f1f733ae147e..000000000000 --- a/examples/mplot3d/2dcollections3d_demo.py +++ /dev/null @@ -1,42 +0,0 @@ -''' -Demonstrates using ax.plot's zdir keyword to plot 2D scatterplot data on -selective axes of a 3D plot. -''' - -from mpl_toolkits.mplot3d import Axes3D -import numpy as np -import matplotlib.pyplot as plt - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Plot a sin curve using the x and y axes. -x = np.linspace(0, 1, 100) -y = np.sin(x * 2 * np.pi) / 2 + 0.5 -ax.plot(x, y, zs=0, zdir='z', label='curve in (x,y)') - -# Plot scatterplot data (20 2D points per colour) on the x and z axes. -colors = ('r', 'g', 'b', 'k') -x = np.random.sample(20*len(colors)) -y = np.random.sample(20*len(colors)) -c_list = [] -for c in colors: - c_list.append([c]*20) -# By using zdir='y', the y value of these points is fixed to the zs value 0 -# and the (x,y) points are plotted on the x and z axes. -ax.scatter(x, y, zs=0, zdir='y', c=c_list, label='points in (x,z)') - -# Make legend, set axes limits and labels -ax.legend() -ax.set_xlim(0, 1) -ax.set_ylim(0, 1) -ax.set_zlim(0, 1) -ax.set_xlabel('X') -ax.set_ylabel('Y') -ax.set_zlabel('Z') - -# Customize the view angle so it's easier to see that the scatter points lie -# on the plane y=0 -ax.view_init(elev=20., azim=-35) - -plt.show() diff --git a/examples/mplot3d/bars3d_demo.py b/examples/mplot3d/bars3d_demo.py deleted file mode 100644 index 90bd58c6c9fa..000000000000 --- a/examples/mplot3d/bars3d_demo.py +++ /dev/null @@ -1,35 +0,0 @@ -''' -Demonstrates making a 3D plot which has 2D bar graphs projected onto -planes y=0, y=1, etc. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -colors = ['r', 'g', 'b', 'y'] -yticks = [3, 2, 1, 0] -for c, k in zip(colors, yticks): - # Generate the random data for the y=k 'layer'. - xs = np.arange(20) - ys = np.random.rand(20) - - # You can provide either a single color or an array with the same length as - # xs and ys. To demonstrate this, we color the first bar of each set cyan. - cs = [c] * len(xs) - cs[0] = 'c' - - # Plot the bar graph given by xs and ys on the plane y=k with 80% opacity. - ax.bar(xs, ys, zs=k, zdir='y', color=cs, alpha=0.8) - -ax.set_xlabel('X') -ax.set_ylabel('Y') -ax.set_zlabel('Z') - -# On the y axis let's only label the discrete values that we have data for. -ax.set_yticks(yticks) - -plt.show() diff --git a/examples/mplot3d/contour3d_demo.py b/examples/mplot3d/contour3d_demo.py deleted file mode 100644 index d41f8e8bc937..000000000000 --- a/examples/mplot3d/contour3d_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -''' -Demonstrates plotting contour (level) curves in 3D. - -This is like a contour plot in 2D except that the f(x,y)=c curve is plotted -on the plane z=c. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -from matplotlib import cm - -fig = plt.figure() -ax = fig.gca(projection='3d') -X, Y, Z = axes3d.get_test_data(0.05) - -# Plot contour curves -cset = ax.contour(X, Y, Z, cmap=cm.coolwarm) - -ax.clabel(cset, fontsize=9, inline=1) - -plt.show() diff --git a/examples/mplot3d/contour3d_demo2.py b/examples/mplot3d/contour3d_demo2.py deleted file mode 100644 index e1f408294e3c..000000000000 --- a/examples/mplot3d/contour3d_demo2.py +++ /dev/null @@ -1,20 +0,0 @@ -''' -Demonstrates plotting contour (level) curves in 3D using the extend3d option. - -This modification of the contour3d_demo example uses extend3d=True to -extend the curves vertically into 'ribbons'. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -from matplotlib import cm - -fig = plt.figure() -ax = fig.gca(projection='3d') -X, Y, Z = axes3d.get_test_data(0.05) - -cset = ax.contour(X, Y, Z, extend3d=True, cmap=cm.coolwarm) - -ax.clabel(cset, fontsize=9, inline=1) - -plt.show() diff --git a/examples/mplot3d/contour3d_demo3.py b/examples/mplot3d/contour3d_demo3.py deleted file mode 100644 index ed2474400bd8..000000000000 --- a/examples/mplot3d/contour3d_demo3.py +++ /dev/null @@ -1,34 +0,0 @@ -''' -Demonstrates displaying a 3D surface while also projecting contour 'profiles' -onto the 'walls' of the graph. - -See contourf3d_demo2 for the filled version. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -from matplotlib import cm - -fig = plt.figure() -ax = fig.gca(projection='3d') -X, Y, Z = axes3d.get_test_data(0.05) - -# Plot the 3D surface -ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3) - -# Plot projections of the contours for each dimension. By choosing offsets -# that match the appropriate axes limits, the projected contours will sit on -# the 'walls' of the graph -cset = ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm) -cset = ax.contour(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm) -cset = ax.contour(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm) - -ax.set_xlim(-40, 40) -ax.set_ylim(-40, 40) -ax.set_zlim(-100, 100) - -ax.set_xlabel('X') -ax.set_ylabel('Y') -ax.set_zlabel('Z') - -plt.show() diff --git a/examples/mplot3d/contourf3d_demo.py b/examples/mplot3d/contourf3d_demo.py deleted file mode 100644 index fc1178b9ff69..000000000000 --- a/examples/mplot3d/contourf3d_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -''' -contourf differs from contour in that it creates filled contours, ie. -a discrete number of colours are used to shade the domain. - -This is like a contourf plot in 2D except that the shaded region corresponding -to the level c is graphed on the plane z=c. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -from matplotlib import cm - -fig = plt.figure() -ax = fig.gca(projection='3d') -X, Y, Z = axes3d.get_test_data(0.05) - -cset = ax.contourf(X, Y, Z, cmap=cm.coolwarm) - -ax.clabel(cset, fontsize=9, inline=1) - -plt.show() diff --git a/examples/mplot3d/contourf3d_demo2.py b/examples/mplot3d/contourf3d_demo2.py deleted file mode 100644 index 62e3db785979..000000000000 --- a/examples/mplot3d/contourf3d_demo2.py +++ /dev/null @@ -1,34 +0,0 @@ -''' -Demonstrates displaying a 3D surface while also projecting filled contour -'profiles' onto the 'walls' of the graph. - -See contour3d_demo2 for the unfilled version. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -from matplotlib import cm - -fig = plt.figure() -ax = fig.gca(projection='3d') -X, Y, Z = axes3d.get_test_data(0.05) - -# Plot the 3D surface -ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3) - -# Plot projections of the contours for each dimension. By choosing offsets -# that match the appropriate axes limits, the projected contours will sit on -# the 'walls' of the graph -cset = ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm) -cset = ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm) -cset = ax.contourf(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm) - -ax.set_xlim(-40, 40) -ax.set_ylim(-40, 40) -ax.set_zlim(-100, 100) - -ax.set_xlabel('X') -ax.set_ylabel('Y') -ax.set_zlabel('Z') - -plt.show() diff --git a/examples/mplot3d/custom_shaded_3d_surface.py b/examples/mplot3d/custom_shaded_3d_surface.py deleted file mode 100644 index dc35b235a161..000000000000 --- a/examples/mplot3d/custom_shaded_3d_surface.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Demonstrates using custom hillshading in a 3D surface plot. -""" - -from mpl_toolkits.mplot3d import Axes3D -from matplotlib import cbook -from matplotlib import cm -from matplotlib.colors import LightSource -import matplotlib.pyplot as plt -import numpy as np - -# Load and format data -filename = cbook.get_sample_data('jacksboro_fault_dem.npz', asfileobj=False) -with np.load(filename) as dem: - z = dem['elevation'] - nrows, ncols = z.shape - x = np.linspace(dem['xmin'], dem['xmax'], ncols) - y = np.linspace(dem['ymin'], dem['ymax'], nrows) - x, y = np.meshgrid(x, y) - -region = np.s_[5:50, 5:50] -x, y, z = x[region], y[region], z[region] - -# Set up plot -fig, ax = plt.subplots(subplot_kw=dict(projection='3d')) - -ls = LightSource(270, 45) -# To use a custom hillshading mode, override the built-in shading and pass -# in the rgb colors of the shaded surface calculated from "shade". -rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft') -surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb, - linewidth=0, antialiased=False, shade=False) - -plt.show() diff --git a/examples/mplot3d/hist3d_demo.py b/examples/mplot3d/hist3d_demo.py deleted file mode 100644 index 6d6210de4315..000000000000 --- a/examples/mplot3d/hist3d_demo.py +++ /dev/null @@ -1,30 +0,0 @@ -''' -Demo of a histogram for 2 dimensional data as a bar graph in 3D. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') -x, y = np.random.rand(2, 100) * 4 -hist, xedges, yedges = np.histogram2d(x, y, bins=4, range=[[0, 4], [0, 4]]) - -# Construct arrays for the anchor positions of the 16 bars. -# Note: np.meshgrid gives arrays in (ny, nx) so we use 'F' to flatten xpos, -# ypos in column-major order. For numpy >= 1.7, we could instead call meshgrid -# with indexing='ij'. -xpos, ypos = np.meshgrid(xedges[:-1] + 0.25, yedges[:-1] + 0.25) -xpos = xpos.flatten('F') -ypos = ypos.flatten('F') -zpos = np.zeros_like(xpos) - -# Construct arrays with the dimensions for the 16 bars. -dx = 0.5 * np.ones_like(zpos) -dy = dx.copy() -dz = hist.flatten() - -ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average') - -plt.show() diff --git a/examples/mplot3d/lines3d_demo.py b/examples/mplot3d/lines3d_demo.py deleted file mode 100644 index 8e73a301b39c..000000000000 --- a/examples/mplot3d/lines3d_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -''' -Demonstrating plotting a parametric curve in 3D. -''' - -import matplotlib as mpl -from mpl_toolkits.mplot3d import Axes3D -import numpy as np -import matplotlib.pyplot as plt - -mpl.rcParams['legend.fontsize'] = 10 - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Prepare arrays x, y, z -theta = np.linspace(-4 * np.pi, 4 * np.pi, 100) -z = np.linspace(-2, 2, 100) -r = z**2 + 1 -x = r * np.sin(theta) -y = r * np.cos(theta) - -ax.plot(x, y, z, label='parametric curve') -ax.legend() - -plt.show() diff --git a/examples/mplot3d/lorenz_attractor.py b/examples/mplot3d/lorenz_attractor.py deleted file mode 100644 index 9fceb0af149d..000000000000 --- a/examples/mplot3d/lorenz_attractor.py +++ /dev/null @@ -1,61 +0,0 @@ -''' -Plot of the Lorenz Attractor based on Edward Lorenz's 1963 "Deterministic -Nonperiodic Flow" publication. -http://journals.ametsoc.org/doi/abs/10.1175/1520-0469%281963%29020%3C0130%3ADNF%3E2.0.CO%3B2 - -Note: Because this is a simple non-linear ODE, it would be more easily - done using SciPy's ode solver, but this approach depends only - upon NumPy. -''' - -import numpy as np -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D - - -def lorenz(x, y, z, s=10, r=28, b=2.667): - ''' - Given: - x, y, z: a point of interest in three dimensional space - s, r, b: parameters defining the lorenz attractor - Returns: - x_dot, y_dot, z_dot: values of the lorenz attractor's partial - derivatives at the point x, y, z - ''' - x_dot = s*(y - x) - y_dot = r*x - y - x*z - z_dot = x*y - b*z - return x_dot, y_dot, z_dot - - -dt = 0.01 -num_steps = 10000 - -# Need one more for the initial values -xs = np.empty((num_steps + 1,)) -ys = np.empty((num_steps + 1,)) -zs = np.empty((num_steps + 1,)) - -# Set initial values -xs[0], ys[0], zs[0] = (0., 1., 1.05) - -# Step through "time", calculating the partial derivatives at the current point -# and using them to estimate the next point -for i in range(num_steps): - x_dot, y_dot, z_dot = lorenz(xs[i], ys[i], zs[i]) - xs[i + 1] = xs[i] + (x_dot * dt) - ys[i + 1] = ys[i] + (y_dot * dt) - zs[i + 1] = zs[i] + (z_dot * dt) - - -# Plot -fig = plt.figure() -ax = fig.gca(projection='3d') - -ax.plot(xs, ys, zs, lw=0.5) -ax.set_xlabel("X Axis") -ax.set_ylabel("Y Axis") -ax.set_zlabel("Z Axis") -ax.set_title("Lorenz Attractor") - -plt.show() diff --git a/examples/mplot3d/mixed_subplots_demo.py b/examples/mplot3d/mixed_subplots_demo.py deleted file mode 100644 index f7788e0add78..000000000000 --- a/examples/mplot3d/mixed_subplots_demo.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Demonstrate the mixing of 2d and 3d subplots. -""" - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - - -def f(t): - s1 = np.cos(2*np.pi*t) - e1 = np.exp(-t) - return np.multiply(s1, e1) - - -############################################# -# Set up a figure twice as tall as it is wide -############################################# -fig = plt.figure(figsize=plt.figaspect(2.)) -fig.suptitle('A tale of 2 subplots') - - -############################################# -# First subplot -############################################# -ax = fig.add_subplot(2, 1, 1) - -t1 = np.arange(0.0, 5.0, 0.1) -t2 = np.arange(0.0, 5.0, 0.02) -t3 = np.arange(0.0, 2.0, 0.01) - -ax.plot(t1, f(t1), 'bo', - t2, f(t2), 'k--', markerfacecolor='green') -ax.grid(True) -ax.set_ylabel('Damped oscillation') - - -############################################# -# Second subplot -############################################# -ax = fig.add_subplot(2, 1, 2, projection='3d') - -X = np.arange(-5, 5, 0.25) -Y = np.arange(-5, 5, 0.25) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) - -surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, - linewidth=0, antialiased=False) -ax.set_zlim(-1, 1) - - -plt.show() diff --git a/examples/mplot3d/offset_demo.py b/examples/mplot3d/offset_demo.py deleted file mode 100644 index c4f6d35e021a..000000000000 --- a/examples/mplot3d/offset_demo.py +++ /dev/null @@ -1,30 +0,0 @@ -''' -This example demonstrates mplot3d's offset text display. -As one rotates the 3D figure, the offsets should remain oriented the -same way as the axis label, and should also be located "away" -from the center of the plot. - -This demo triggers the display of the offset text for the x and -y axis by adding 1e5 to X and Y. Anything less would not -automatically trigger it. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - - -fig = plt.figure() -ax = fig.gca(projection='3d') - -X, Y = np.mgrid[0:6*np.pi:0.25, 0:4*np.pi:0.25] -Z = np.sqrt(np.abs(np.cos(X) + np.cos(Y))) - -ax.plot_surface(X + 1e5, Y + 1e5, Z, cmap='autumn', cstride=2, rstride=2) - -ax.set_xlabel("X label") -ax.set_ylabel("Y label") -ax.set_zlabel("Z label") -ax.set_zlim(0, 2) - -plt.show() diff --git a/examples/mplot3d/pathpatch3d_demo.py b/examples/mplot3d/pathpatch3d_demo.py deleted file mode 100644 index efc99bb9303e..000000000000 --- a/examples/mplot3d/pathpatch3d_demo.py +++ /dev/null @@ -1,67 +0,0 @@ -''' -Demonstrate using pathpatch_2d_to_3d to 'draw' shapes and text on a 3D plot. -''' - -import matplotlib.pyplot as plt -from matplotlib.patches import Circle, PathPatch -# register Axes3D class with matplotlib by importing Axes3D -from mpl_toolkits.mplot3d import Axes3D -import mpl_toolkits.mplot3d.art3d as art3d -from matplotlib.text import TextPath -from matplotlib.transforms import Affine2D - - -def text3d(ax, xyz, s, zdir="z", size=None, angle=0, usetex=False, **kwargs): - ''' - Plots the string 's' on the axes 'ax', with position 'xyz', size 'size', - and rotation angle 'angle'. 'zdir' gives the axis which is to be treated - as the third dimension. usetex is a boolean indicating whether the string - should be interpreted as latex or not. Any additional keyword arguments - are passed on to transform_path. - - Note: zdir affects the interpretation of xyz. - ''' - x, y, z = xyz - if zdir == "y": - xy1, z1 = (x, z), y - elif zdir == "y": - xy1, z1 = (y, z), x - else: - xy1, z1 = (x, y), z - - text_path = TextPath((0, 0), s, size=size, usetex=usetex) - trans = Affine2D().rotate(angle).translate(xy1[0], xy1[1]) - - p1 = PathPatch(trans.transform_path(text_path), **kwargs) - ax.add_patch(p1) - art3d.pathpatch_2d_to_3d(p1, z=z1, zdir=zdir) - - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# Draw a circle on the x=0 'wall' -p = Circle((5, 5), 3) -ax.add_patch(p) -art3d.pathpatch_2d_to_3d(p, z=0, zdir="x") - -# Manually label the axes -text3d(ax, (4, -2, 0), "X-axis", zdir="z", size=.5, usetex=False, - ec="none", fc="k") -text3d(ax, (12, 4, 0), "Y-axis", zdir="z", size=.5, usetex=False, - angle=.5*3.14159, ec="none", fc="k") -text3d(ax, (12, 10, 4), "Z-axis", zdir="y", size=.5, usetex=False, - angle=.5*3.14159, ec="none", fc="k") - -# Write a Latex formula on the z=0 'floor' -text3d(ax, (1, 5, 0), - r"$\displaystyle G_{\mu\nu} + \Lambda g_{\mu\nu} = " - r"\frac{8\pi G}{c^4} T_{\mu\nu} $", - zdir="z", size=1, usetex=True, - ec="none", fc="k") - -ax.set_xlim(0, 10) -ax.set_ylim(0, 10) -ax.set_zlim(0, 10) - -plt.show() diff --git a/examples/mplot3d/polys3d_demo.py b/examples/mplot3d/polys3d_demo.py deleted file mode 100644 index 7b94a19d04cb..000000000000 --- a/examples/mplot3d/polys3d_demo.py +++ /dev/null @@ -1,54 +0,0 @@ -''' -Demonstrate how to create semi-transparent polygons which fill the space -under a line graph, creating a sort of 'jagged stained glass' effect. -''' - -from mpl_toolkits.mplot3d import Axes3D -from matplotlib.collections import PolyCollection -import matplotlib.pyplot as plt -from matplotlib import colors as mcolors -import numpy as np - - -def cc(arg): - ''' - Shorthand to convert 'named' colors to rgba format at 60% opacity. - ''' - return mcolors.to_rgba(arg, alpha=0.6) - - -def polygon_under_graph(xlist, ylist): - ''' - Construct the vertex list which defines the polygon filling the space under - the (xlist, ylist) line graph. Assumes the xs are in ascending order. - ''' - return [(xlist[0], 0.)] + list(zip(xlist, ylist)) + [(xlist[-1], 0.)] - - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Make verts a list, verts[i] will be a list of (x,y) pairs defining polygon i -verts = [] - -# Set up the x sequence -xs = np.linspace(0., 10., 26) - -# The ith polygon will appear on the plane y = zs[i] -zs = range(4) - -for i in zs: - ys = np.random.rand(len(xs)) - verts.append(polygon_under_graph(xs, ys)) - -poly = PolyCollection(verts, facecolors=[cc('r'), cc('g'), cc('b'), cc('y')]) -ax.add_collection3d(poly, zs=zs, zdir='y') - -ax.set_xlabel('X') -ax.set_ylabel('Y') -ax.set_zlabel('Z') -ax.set_xlim(0, 10) -ax.set_ylim(-1, 4) -ax.set_zlim(0, 1) - -plt.show() diff --git a/examples/mplot3d/quiver3d_demo.py b/examples/mplot3d/quiver3d_demo.py deleted file mode 100644 index 16ba7eab0190..000000000000 --- a/examples/mplot3d/quiver3d_demo.py +++ /dev/null @@ -1,29 +0,0 @@ -''' -============== -3D quiver plot -============== - -Demonstrates plotting directional arrows at points on a 3d meshgrid. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -import numpy as np - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Make the grid -x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2), - np.arange(-0.8, 1, 0.2), - np.arange(-0.8, 1, 0.8)) - -# Make the direction data for the arrows -u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z) -v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z) -w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) * - np.sin(np.pi * z)) - -ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True) - -plt.show() diff --git a/examples/mplot3d/rotate_axes3d_demo.py b/examples/mplot3d/rotate_axes3d_demo.py deleted file mode 100644 index aa12f3ce9a9a..000000000000 --- a/examples/mplot3d/rotate_axes3d_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -''' -================== -Rotating a 3D plot -================== - -A very simple animation of a rotating 3D plot. - -See wire3d_animation_demo for another simple example of animating a 3D plot. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# load some test data for demonstration and plot a wireframe -X, Y, Z = axes3d.get_test_data(0.1) -ax.plot_wireframe(X, Y, Z, rstride=5, cstride=5) - -# rotate the axes and update -for angle in range(0, 360): - ax.view_init(30, angle) - plt.draw() - plt.pause(.001) diff --git a/examples/mplot3d/scatter3d_demo.py b/examples/mplot3d/scatter3d_demo.py deleted file mode 100644 index 21f4932b80a2..000000000000 --- a/examples/mplot3d/scatter3d_demo.py +++ /dev/null @@ -1,38 +0,0 @@ -''' -============== -3D scatterplot -============== - -Demonstration of a basic scatterplot in 3D. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - - -def randrange(n, vmin, vmax): - ''' - Helper function to make an array of random numbers having shape (n, ) - with each number distributed Uniform(vmin, vmax). - ''' - return (vmax - vmin)*np.random.rand(n) + vmin - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -n = 100 - -# For each set of style and range settings, plot n random points in the box -# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh]. -for c, m, zlow, zhigh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]: - xs = randrange(n, 23, 32) - ys = randrange(n, 0, 100) - zs = randrange(n, zlow, zhigh) - ax.scatter(xs, ys, zs, c=c, marker=m) - -ax.set_xlabel('X Label') -ax.set_ylabel('Y Label') -ax.set_zlabel('Z Label') - -plt.show() diff --git a/examples/mplot3d/subplot3d_demo.py b/examples/mplot3d/subplot3d_demo.py deleted file mode 100644 index 9ece9f7ddc02..000000000000 --- a/examples/mplot3d/subplot3d_demo.py +++ /dev/null @@ -1,45 +0,0 @@ -''' -==================== -3D plots as subplots -==================== - -Demonstrate including 3D plots as subplots. -''' - -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d.axes3d import Axes3D, get_test_data -from matplotlib import cm -import numpy as np - - -# set up a figure twice as wide as it is tall -fig = plt.figure(figsize=plt.figaspect(0.5)) - -#=============== -# First subplot -#=============== -# set up the axes for the first plot -ax = fig.add_subplot(1, 2, 1, projection='3d') - -# plot a 3D surface like in the example mplot3d/surface3d_demo -X = np.arange(-5, 5, 0.25) -Y = np.arange(-5, 5, 0.25) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) -surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, - linewidth=0, antialiased=False) -ax.set_zlim(-1.01, 1.01) -fig.colorbar(surf, shrink=0.5, aspect=10) - -#=============== -# Second subplot -#=============== -# set up the axes for the second plot -ax = fig.add_subplot(1, 2, 2, projection='3d') - -# plot a 3D wireframe like in the example mplot3d/wire3d_demo -X, Y, Z = get_test_data(0.05) -ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) - -plt.show() diff --git a/examples/mplot3d/surface3d_demo.py b/examples/mplot3d/surface3d_demo.py deleted file mode 100644 index 9ed6b0f75aea..000000000000 --- a/examples/mplot3d/surface3d_demo.py +++ /dev/null @@ -1,42 +0,0 @@ -''' -====================== -3D surface (color map) -====================== - -Demonstrates plotting a 3D surface colored with the coolwarm color map. -The surface is made opaque by using antialiased=False. - -Also demonstrates using the LinearLocator and custom formatting for the -z axis tick labels. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -from matplotlib import cm -from matplotlib.ticker import LinearLocator, FormatStrFormatter -import numpy as np - - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Make data. -X = np.arange(-5, 5, 0.25) -Y = np.arange(-5, 5, 0.25) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) - -# Plot the surface. -surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, - linewidth=0, antialiased=False) - -# Customize the z axis. -ax.set_zlim(-1.01, 1.01) -ax.zaxis.set_major_locator(LinearLocator(10)) -ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) - -# Add a color bar which maps values to colors. -fig.colorbar(surf, shrink=0.5, aspect=5) - -plt.show() diff --git a/examples/mplot3d/surface3d_demo2.py b/examples/mplot3d/surface3d_demo2.py deleted file mode 100644 index 8135f059eef1..000000000000 --- a/examples/mplot3d/surface3d_demo2.py +++ /dev/null @@ -1,27 +0,0 @@ -''' -======================== -3D surface (solid color) -======================== - -Demonstrates a very basic plot of a 3D surface using a solid color. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# Make data -u = np.linspace(0, 2 * np.pi, 100) -v = np.linspace(0, np.pi, 100) -x = 10 * np.outer(np.cos(u), np.sin(v)) -y = 10 * np.outer(np.sin(u), np.sin(v)) -z = 10 * np.outer(np.ones(np.size(u)), np.cos(v)) - -# Plot the surface -ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b') - -plt.show() diff --git a/examples/mplot3d/surface3d_demo3.py b/examples/mplot3d/surface3d_demo3.py deleted file mode 100644 index 370a5a4a2958..000000000000 --- a/examples/mplot3d/surface3d_demo3.py +++ /dev/null @@ -1,44 +0,0 @@ -''' -========================= -3D surface (checkerboard) -========================= - -Demonstrates plotting a 3D surface colored in a checkerboard pattern. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -from matplotlib import cm -from matplotlib.ticker import LinearLocator -import numpy as np - - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Make data. -X = np.arange(-5, 5, 0.25) -xlen = len(X) -Y = np.arange(-5, 5, 0.25) -ylen = len(Y) -X, Y = np.meshgrid(X, Y) -R = np.sqrt(X**2 + Y**2) -Z = np.sin(R) - -# Create an empty array of strings with the same shape as the meshgrid, and -# populate it with two colors in a checkerboard pattern. -colortuple = ('y', 'b') -colors = np.empty(X.shape, dtype=str) -for y in range(ylen): - for x in range(xlen): - colors[x, y] = colortuple[(x + y) % len(colortuple)] - -# Plot the surface with face colors taken from the array we made. -surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors, - linewidth=0) - -# Customize the z axis. -ax.set_zlim(-1, 1) -ax.w_zaxis.set_major_locator(LinearLocator(6)) - -plt.show() diff --git a/examples/mplot3d/surface3d_radial_demo.py b/examples/mplot3d/surface3d_radial_demo.py deleted file mode 100644 index c18a4d4dec2b..000000000000 --- a/examples/mplot3d/surface3d_radial_demo.py +++ /dev/null @@ -1,39 +0,0 @@ -''' -================================= -3D surface with polar coordinates -================================= - -Demonstrates plotting a surface defined in polar coordinates. -Uses the reversed version of the YlGnBu color map. -Also demonstrates writing axis labels with latex math mode. - -Example contributed by Armin Moser. -''' - -from mpl_toolkits.mplot3d import Axes3D -from matplotlib import pyplot as plt -import numpy as np - - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# Create the mesh in polar coordinates and compute corresponding Z. -r = np.linspace(0, 1.25, 50) -p = np.linspace(0, 2*np.pi, 50) -R, P = np.meshgrid(r, p) -Z = ((R**2 - 1)**2) - -# Express the mesh in the cartesian system. -X, Y = R*np.cos(P), R*np.sin(P) - -# Plot the surface. -ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=plt.cm.YlGnBu_r) - -# Tweak the limits and add latex math labels. -ax.set_zlim(0, 1) -ax.set_xlabel(r'$\phi_\mathrm{real}$') -ax.set_ylabel(r'$\phi_\mathrm{im}$') -ax.set_zlabel(r'$V(\phi)$') - -plt.show() diff --git a/examples/mplot3d/text3d_demo.py b/examples/mplot3d/text3d_demo.py deleted file mode 100644 index 6b1963e2a43b..000000000000 --- a/examples/mplot3d/text3d_demo.py +++ /dev/null @@ -1,47 +0,0 @@ -''' -====================== -Text annotations in 3D -====================== - -Demonstrates the placement of text annotations on a 3D plot. - -Functionality shown: -- Using the text function with three types of 'zdir' values: None, - an axis name (ex. 'x'), or a direction tuple (ex. (1, 1, 0)). -- Using the text function with the color keyword. -- Using the text2D function to place text on a fixed position on the ax object. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt - - -fig = plt.figure() -ax = fig.gca(projection='3d') - -# Demo 1: zdir -zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1)) -xs = (1, 4, 4, 9, 4, 1) -ys = (2, 5, 8, 10, 1, 2) -zs = (10, 3, 8, 9, 1, 8) - -for zdir, x, y, z in zip(zdirs, xs, ys, zs): - label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir) - ax.text(x, y, z, label, zdir) - -# Demo 2: color -ax.text(9, 0, 0, "red", color='red') - -# Demo 3: text2D -# Placement 0, 0 would be the bottom left, 1, 1 would be the top right. -ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes) - -# Tweaking display region and labels -ax.set_xlim(0, 10) -ax.set_ylim(0, 10) -ax.set_zlim(0, 10) -ax.set_xlabel('X axis') -ax.set_ylabel('Y axis') -ax.set_zlabel('Z axis') - -plt.show() diff --git a/examples/mplot3d/tricontour3d_demo.py b/examples/mplot3d/tricontour3d_demo.py deleted file mode 100644 index f78ff63c8532..000000000000 --- a/examples/mplot3d/tricontour3d_demo.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -========================== -Triangular 3D contour plot -========================== - -Contour plots of unstructured triangular grids. - -The data used is the same as in the second plot of trisurf3d_demo2. -tricontourf3d_demo shows the filled version of this example. -""" - -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.tri as tri -import numpy as np - -n_angles = 48 -n_radii = 8 -min_radius = 0.25 - -# Create the mesh in polar coordinates and compute x, y, z. -radii = np.linspace(min_radius, 0.95, n_radii) -angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += np.pi/n_angles - -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -z = (np.cos(radii)*np.cos(angles*3.0)).flatten() - -# Create a custom triangulation. -triang = tri.Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triang.set_mask(mask) - -fig = plt.figure() -ax = fig.gca(projection='3d') -ax.tricontour(triang, z, cmap=plt.cm.CMRmap) - -# Customize the view angle so it's easier to understand the plot. -ax.view_init(elev=45.) - -plt.show() diff --git a/examples/mplot3d/tricontourf3d_demo.py b/examples/mplot3d/tricontourf3d_demo.py deleted file mode 100644 index 27513f2bd5cd..000000000000 --- a/examples/mplot3d/tricontourf3d_demo.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -================================= -Triangular 3D filled contour plot -================================= - -Filled contour plots of unstructured triangular grids. - -The data used is the same as in the second plot of trisurf3d_demo2. -tricontour3d_demo shows the unfilled version of this example. -""" - -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.tri as tri -import numpy as np - -# First create the x, y, z coordinates of the points. -n_angles = 48 -n_radii = 8 -min_radius = 0.25 - -# Create the mesh in polar coordinates and compute x, y, z. -radii = np.linspace(min_radius, 0.95, n_radii) -angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += np.pi/n_angles - -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -z = (np.cos(radii)*np.cos(angles*3.0)).flatten() - -# Create a custom triangulation. -triang = tri.Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triang.set_mask(mask) - -fig = plt.figure() -ax = fig.gca(projection='3d') -ax.tricontourf(triang, z, cmap=plt.cm.CMRmap) - -# Customize the view angle so it's easier to understand the plot. -ax.view_init(elev=45.) - -plt.show() diff --git a/examples/mplot3d/trisurf3d_demo.py b/examples/mplot3d/trisurf3d_demo.py deleted file mode 100644 index 192d4eb8aa06..000000000000 --- a/examples/mplot3d/trisurf3d_demo.py +++ /dev/null @@ -1,38 +0,0 @@ -''' -====================== -Triangular 3D surfaces -====================== - -Plot a 3D surface with a triangular mesh. -''' - -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.pyplot as plt -import numpy as np - - -n_radii = 8 -n_angles = 36 - -# Make radii and angles spaces (radius r=0 omitted to eliminate duplication). -radii = np.linspace(0.125, 1.0, n_radii) -angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False) - -# Repeat all angles for each radius. -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) - -# Convert polar (radii, angles) coords to cartesian (x, y) coords. -# (0, 0) is manually added at this stage, so there will be no duplicate -# points in the (x, y) plane. -x = np.append(0, (radii*np.cos(angles)).flatten()) -y = np.append(0, (radii*np.sin(angles)).flatten()) - -# Compute z to make the pringle surface. -z = np.sin(-x*y) - -fig = plt.figure() -ax = fig.gca(projection='3d') - -ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True) - -plt.show() diff --git a/examples/mplot3d/trisurf3d_demo2.py b/examples/mplot3d/trisurf3d_demo2.py deleted file mode 100644 index 3a6677c76c01..000000000000 --- a/examples/mplot3d/trisurf3d_demo2.py +++ /dev/null @@ -1,80 +0,0 @@ -''' -=========================== -More triangular 3D surfaces -=========================== - -Two additional examples of plotting surfaces with triangular mesh. - -The first demonstrates use of plot_trisurf's triangles argument, and the -second sets a Triangulation object's mask and passes the object directly -to plot_trisurf. -''' - -import numpy as np -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D -import matplotlib.tri as mtri - - -fig = plt.figure(figsize=plt.figaspect(0.5)) - -#============ -# First plot -#============ - -# Make a mesh in the space of parameterisation variables u and v -u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=50) -v = np.linspace(-0.5, 0.5, endpoint=True, num=10) -u, v = np.meshgrid(u, v) -u, v = u.flatten(), v.flatten() - -# This is the Mobius mapping, taking a u, v pair and returning an x, y, z -# triple -x = (1 + 0.5 * v * np.cos(u / 2.0)) * np.cos(u) -y = (1 + 0.5 * v * np.cos(u / 2.0)) * np.sin(u) -z = 0.5 * v * np.sin(u / 2.0) - -# Triangulate parameter space to determine the triangles -tri = mtri.Triangulation(u, v) - -# Plot the surface. The triangles in parameter space determine which x, y, z -# points are connected by an edge. -ax = fig.add_subplot(1, 2, 1, projection='3d') -ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral) -ax.set_zlim(-1, 1) - - -#============ -# Second plot -#============ - -# Make parameter spaces radii and angles. -n_angles = 36 -n_radii = 8 -min_radius = 0.25 -radii = np.linspace(min_radius, 0.95, n_radii) - -angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += np.pi/n_angles - -# Map radius, angle pairs to x, y, z points. -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -z = (np.cos(radii)*np.cos(angles*3.0)).flatten() - -# Create the Triangulation; no triangles so Delaunay triangulation created. -triang = mtri.Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid**2 + ymid**2 < min_radius**2, 1, 0) -triang.set_mask(mask) - -# Plot the surface. -ax = fig.add_subplot(1, 2, 2, projection='3d') -ax.plot_trisurf(triang, z, cmap=plt.cm.CMRmap) - - -plt.show() diff --git a/examples/mplot3d/wire3d_animation_demo.py b/examples/mplot3d/wire3d_animation_demo.py deleted file mode 100644 index 1083f006436f..000000000000 --- a/examples/mplot3d/wire3d_animation_demo.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -========================== -Rotating 3D wireframe plot -========================== - -A very simple 'animation' of a 3D plot. See also rotate_axes3d_demo. -""" - -from __future__ import print_function - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt -import numpy as np -import time - - -def generate(X, Y, phi): - ''' - Generates Z data for the points in the X, Y meshgrid and parameter phi. - ''' - R = 1 - np.sqrt(X**2 + Y**2) - return np.cos(2 * np.pi * X + phi) * R - - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# Make the X, Y meshgrid. -xs = np.linspace(-1, 1, 50) -ys = np.linspace(-1, 1, 50) -X, Y = np.meshgrid(xs, ys) - -# Set the z axis limits so they aren't recalculated each frame. -ax.set_zlim(-1, 1) - -# Begin plotting. -wframe = None -tstart = time.time() -for phi in np.linspace(0, 180. / np.pi, 100): - # If a line collection is already remove it before drawing. - if wframe: - ax.collections.remove(wframe) - - # Plot the new wireframe and pause briefly before continuing. - Z = generate(X, Y, phi) - wframe = ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2) - plt.pause(.001) - -print('Average FPS: %f' % (100 / (time.time() - tstart))) diff --git a/examples/mplot3d/wire3d_demo.py b/examples/mplot3d/wire3d_demo.py deleted file mode 100644 index cd91cc57ac2f..000000000000 --- a/examples/mplot3d/wire3d_demo.py +++ /dev/null @@ -1,22 +0,0 @@ -''' -================= -3D wireframe plot -================= - -A very basic demonstration of a wireframe plot. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt - - -fig = plt.figure() -ax = fig.add_subplot(111, projection='3d') - -# Grab some test data. -X, Y, Z = axes3d.get_test_data(0.05) - -# Plot a basic wireframe. -ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) - -plt.show() diff --git a/examples/mplot3d/wire3d_zero_stride.py b/examples/mplot3d/wire3d_zero_stride.py deleted file mode 100644 index 0eac7b70385f..000000000000 --- a/examples/mplot3d/wire3d_zero_stride.py +++ /dev/null @@ -1,28 +0,0 @@ -''' -=================================== -3D wireframe plots in one direction -=================================== - -Demonstrates that setting rstride or cstride to 0 causes wires to not be -generated in the corresponding direction. -''' - -from mpl_toolkits.mplot3d import axes3d -import matplotlib.pyplot as plt - - -fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(8, 12), subplot_kw={'projection': '3d'}) - -# Get the test data -X, Y, Z = axes3d.get_test_data(0.05) - -# Give the first plot only wireframes of the type y = c -ax1.plot_wireframe(X, Y, Z, rstride=10, cstride=0) -ax1.set_title("Column (x) stride set to 0") - -# Give the second plot only wireframes of the type x = c -ax2.plot_wireframe(X, Y, Z, rstride=0, cstride=10) -ax2.set_title("Row (y) stride set to 0") - -plt.tight_layout() -plt.show() diff --git a/examples/pie_and_polar_charts/pie_demo_features.py b/examples/pie_and_polar_charts/pie_demo_features.py deleted file mode 100644 index 32aaa94f9569..000000000000 --- a/examples/pie_and_polar_charts/pie_demo_features.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Demo of a basic pie chart plus a few additional features. - -In addition to the basic pie chart, this demo shows a few optional features: - - * slice labels - * auto-labeling the percentage - * offsetting a slice with "explode" - * drop-shadow - * custom start angle - -Note about the custom start angle: - -The default ``startangle`` is 0, which would start the "Frogs" slice on the -positive x-axis. This example sets ``startangle = 90`` such that everything is -rotated counter-clockwise by 90 degrees, and the frog slice starts on the -positive y-axis. -""" -import matplotlib.pyplot as plt - -# The slices will be ordered and plotted counter-clockwise. -labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' -sizes = [15, 30, 45, 10] -explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') - -plt.pie(sizes, explode=explode, labels=labels, - autopct='%1.1f%%', shadow=True, startangle=90) -# Set aspect ratio to be equal so that pie is drawn as a circle. -plt.axis('equal') - -fig = plt.figure() -ax = fig.gca() -import numpy as np - -ax.pie(np.random.random(4), explode=explode, labels=labels, - autopct='%1.1f%%', shadow=True, startangle=90, - radius=0.25, center=(0, 0), frame=True) -ax.pie(np.random.random(4), explode=explode, labels=labels, - autopct='%1.1f%%', shadow=True, startangle=90, - radius=0.25, center=(1, 1), frame=True) -ax.pie(np.random.random(4), explode=explode, labels=labels, - autopct='%1.1f%%', shadow=True, startangle=90, - radius=0.25, center=(0, 1), frame=True) -ax.pie(np.random.random(4), explode=explode, labels=labels, - autopct='%1.1f%%', shadow=True, startangle=90, - radius=0.25, center=(1, 0), frame=True) - -ax.set_xticks([0, 1]) -ax.set_yticks([0, 1]) -ax.set_xticklabels(["Sunny", "Cloudy"]) -ax.set_yticklabels(["Dry", "Rainy"]) -ax.set_xlim((-0.5, 1.5)) -ax.set_ylim((-0.5, 1.5)) - -# Set aspect ratio to be equal so that pie is drawn as a circle. -ax.set_aspect('equal') - -plt.show() diff --git a/examples/pie_and_polar_charts/polar_bar_demo.py b/examples/pie_and_polar_charts/polar_bar_demo.py deleted file mode 100644 index 32e3831b2bad..000000000000 --- a/examples/pie_and_polar_charts/polar_bar_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Demo of bar plot on a polar axis. -""" -import numpy as np -import matplotlib.pyplot as plt - - -N = 20 -theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False) -radii = 10 * np.random.rand(N) -width = np.pi / 4 * np.random.rand(N) - -ax = plt.subplot(111, projection='polar') -bars = ax.bar(theta, radii, width=width, bottom=0.0) - -# Use custom colors and opacity -for r, bar in zip(radii, bars): - bar.set_facecolor(plt.cm.viridis(r / 10.)) - bar.set_alpha(0.5) - -plt.show() diff --git a/examples/pie_and_polar_charts/polar_scatter_demo.py b/examples/pie_and_polar_charts/polar_scatter_demo.py deleted file mode 100644 index 71992f3c458c..000000000000 --- a/examples/pie_and_polar_charts/polar_scatter_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Demo of scatter plot on a polar axis. - -Size increases radially in this example and color increases with angle -(just to verify the symbols are being scattered correctly). -""" -import numpy as np -import matplotlib.pyplot as plt - - -N = 150 -r = 2 * np.random.rand(N) -theta = 2 * np.pi * np.random.rand(N) -area = 200 * r**2 -colors = theta - -ax = plt.subplot(111, projection='polar') -c = ax.scatter(theta, r, c=colors, s=area, cmap=plt.cm.hsv) -c.set_alpha(0.75) - -plt.show() diff --git a/examples/pylab_examples/README b/examples/pylab_examples/README deleted file mode 100644 index ca0d48e04541..000000000000 --- a/examples/pylab_examples/README +++ /dev/null @@ -1,24 +0,0 @@ -Here are some demos of how to use the matplotlib. - - --- data_helper.py - a convenience module to load some data from the - data dir - --- embedding_in_gtk - The Figure class derives from gtk.DrawingArea, - so it is easy to embed in larger applications. - --- histograms_gauss.py - 2D histograms; requires the jdh.mlab module - --- simple_plot.py - the basic 2D line plot - --- subplot_demo.py - how to do multiple axes on a single plot - --- vline_hline_demo.py - working with straight lines - --- stock_demo.py - working with large datasets. Click on the plot and - launch the navigation tool; wheel mouse over the navigation - buttons to scroll and zoom. There are 58 days of minute by - minute stock quotes for two tickers. The plot lib uses - Numeric's super speedy searchsorted routine to extract the - clipping indices so only the data in the viewport are handled. - diff --git a/examples/pylab_examples/accented_text.py b/examples/pylab_examples/accented_text.py deleted file mode 100644 index 1902ad71b480..000000000000 --- a/examples/pylab_examples/accented_text.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -matplotlib supports accented characters via TeX mathtext - -The following accents are provided: \hat, \breve, \grave, \bar, -\acute, \tilde, \vec, \dot, \ddot. All of them have the same syntax, -e.g., to make an overbar you do \bar{o} or to make an o umlaut you do -\ddot{o}. The shortcuts are also provided, e.g.,: \"o \'e \`e \~n \.x -\^y - -""" -import matplotlib.pyplot as plt - -plt.axes([0.1, 0.15, 0.8, 0.75]) -plt.plot(range(10)) - -plt.title(r'$\ddot{o}\acute{e}\grave{e}\hat{O}\breve{i}\bar{A}\tilde{n}\vec{q}$', fontsize=20) -# shorthand is also supported and curly's are optional -plt.xlabel(r"""$\"o\ddot o \'e\`e\~n\.x\^y$""", fontsize=20) - - -plt.show() diff --git a/examples/pylab_examples/agg_buffer.py b/examples/pylab_examples/agg_buffer.py deleted file mode 100755 index cf7042793ba0..000000000000 --- a/examples/pylab_examples/agg_buffer.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Use backend agg to access the figure canvas as an RGB string and then -convert it to an array and pass it to Pillow for rendering. -""" - -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib.backends.backend_agg import FigureCanvasAgg - - -try: - from PIL import Image -except ImportError: - raise SystemExit("Pillow must be installed to run this example") - -plt.plot([1, 2, 3]) - -canvas = plt.get_current_fig_manager().canvas - -agg = canvas.switch_backends(FigureCanvasAgg) -agg.draw() -s = agg.tostring_rgb() - -# get the width and the height to resize the matrix -l, b, w, h = agg.figure.bbox.bounds -w, h = int(w), int(h) - -X = np.fromstring(s, np.uint8) -X.shape = h, w, 3 - -try: - im = Image.fromstring("RGB", (w, h), s) -except Exception: - im = Image.frombytes("RGB", (w, h), s) - -# Uncomment this line to display the image using ImageMagick's -# `display` tool. -# im.show() diff --git a/examples/pylab_examples/agg_buffer_to_array.py b/examples/pylab_examples/agg_buffer_to_array.py deleted file mode 100644 index 6c94da1e4ada..000000000000 --- a/examples/pylab_examples/agg_buffer_to_array.py +++ /dev/null @@ -1,17 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -# make an agg figure -fig, ax = plt.subplots() -ax.plot([1, 2, 3]) -ax.set_title('a simple figure') -fig.canvas.draw() - -# grab the pixel buffer and dump it into a numpy array -X = np.array(fig.canvas.renderer._renderer) - -# now display the array X as an Axes in a new figure -fig2 = plt.figure() -ax2 = fig2.add_subplot(111, frameon=False) -ax2.imshow(X) -plt.show() diff --git a/examples/pylab_examples/alignment_test.py b/examples/pylab_examples/alignment_test.py deleted file mode 100644 index 5de0d6a33a48..000000000000 --- a/examples/pylab_examples/alignment_test.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -You can precisely layout text in data or axes (0,1) coordinates. This -example shows you some of the alignment and rotation specifications to -layout text -""" - -import matplotlib.pyplot as plt -from matplotlib.lines import Line2D -from matplotlib.patches import Rectangle - -# build a rectangle in axes coords -left, width = .25, .5 -bottom, height = .25, .5 -right = left + width -top = bottom + height -ax = plt.gca() -p = plt.Rectangle((left, bottom), width, height, - fill=False, - ) -p.set_transform(ax.transAxes) -p.set_clip_on(False) -ax.add_patch(p) - - -ax.text(left, bottom, 'left top', - horizontalalignment='left', - verticalalignment='top', - transform=ax.transAxes) - -ax.text(left, bottom, 'left bottom', - horizontalalignment='left', - verticalalignment='bottom', - transform=ax.transAxes) - -ax.text(right, top, 'right bottom', - horizontalalignment='right', - verticalalignment='bottom', - transform=ax.transAxes) - -ax.text(right, top, 'right top', - horizontalalignment='right', - verticalalignment='top', - transform=ax.transAxes) - -ax.text(right, bottom, 'center top', - horizontalalignment='center', - verticalalignment='top', - transform=ax.transAxes) - -ax.text(left, 0.5*(bottom + top), 'right center', - horizontalalignment='right', - verticalalignment='center', - rotation='vertical', - transform=ax.transAxes) - -ax.text(left, 0.5*(bottom + top), 'left center', - horizontalalignment='left', - verticalalignment='center', - rotation='vertical', - transform=ax.transAxes) - -ax.text(0.5*(left + right), 0.5*(bottom + top), 'middle', - horizontalalignment='center', - verticalalignment='center', - transform=ax.transAxes) - -ax.text(right, 0.5*(bottom + top), 'centered', - horizontalalignment='center', - verticalalignment='center', - rotation='vertical', - transform=ax.transAxes) - -ax.text(left, top, 'rotated\nwith newlines', - horizontalalignment='center', - verticalalignment='center', - rotation=45, - transform=ax.transAxes) - -plt.axis('off') - -plt.show() diff --git a/examples/pylab_examples/anchored_artists.py b/examples/pylab_examples/anchored_artists.py deleted file mode 100644 index 08104ffc383a..000000000000 --- a/examples/pylab_examples/anchored_artists.py +++ /dev/null @@ -1,110 +0,0 @@ -from matplotlib.patches import Rectangle, Ellipse - -from matplotlib.offsetbox import AnchoredOffsetbox, AuxTransformBox, VPacker,\ - TextArea, DrawingArea - - -class AnchoredText(AnchoredOffsetbox): - def __init__(self, s, loc, pad=0.4, borderpad=0.5, prop=None, frameon=True): - - self.txt = TextArea(s, - minimumdescent=False) - - super(AnchoredText, self).__init__(loc, pad=pad, borderpad=borderpad, - child=self.txt, - prop=prop, - frameon=frameon) - - -class AnchoredSizeBar(AnchoredOffsetbox): - def __init__(self, transform, size, label, loc, - pad=0.1, borderpad=0.1, sep=2, prop=None, frameon=True): - """ - Draw a horizontal bar with the size in data coordinate of the give axes. - A label will be drawn underneath (center-aligned). - - pad, borderpad in fraction of the legend font size (or prop) - sep in points. - """ - self.size_bar = AuxTransformBox(transform) - self.size_bar.add_artist(Rectangle((0, 0), size, 0, fc="none", lw=1.0)) - - self.txt_label = TextArea(label, minimumdescent=False) - - self._box = VPacker(children=[self.size_bar, self.txt_label], - align="center", - pad=0, sep=sep) - - AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad, - child=self._box, - prop=prop, - frameon=frameon) - - -class AnchoredEllipse(AnchoredOffsetbox): - def __init__(self, transform, width, height, angle, loc, - pad=0.1, borderpad=0.1, prop=None, frameon=True): - """ - Draw an ellipse the size in data coordinate of the give axes. - - pad, borderpad in fraction of the legend font size (or prop) - """ - self._box = AuxTransformBox(transform) - self.ellipse = Ellipse((0, 0), width, height, angle) - self._box.add_artist(self.ellipse) - - AnchoredOffsetbox.__init__(self, loc, pad=pad, borderpad=borderpad, - child=self._box, - prop=prop, - frameon=frameon) - - -class AnchoredDrawingArea(AnchoredOffsetbox): - def __init__(self, width, height, xdescent, ydescent, - loc, pad=0.4, borderpad=0.5, prop=None, frameon=True): - - self.da = DrawingArea(width, height, xdescent, ydescent) - - super(AnchoredDrawingArea, self).__init__(loc, pad=pad, borderpad=borderpad, - child=self.da, - prop=None, - frameon=frameon) - - -if __name__ == "__main__": - - import matplotlib.pyplot as plt - - ax = plt.gca() - ax.set_aspect(1.) - - at = AnchoredText("Figure 1a", - loc=2, frameon=True) - at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") - ax.add_artist(at) - - from matplotlib.patches import Circle - ada = AnchoredDrawingArea(20, 20, 0, 0, - loc=1, pad=0., frameon=False) - p = Circle((10, 10), 10) - ada.da.add_artist(p) - ax.add_artist(ada) - - # draw an ellipse of width=0.1, height=0.15 in the data coordinate - ae = AnchoredEllipse(ax.transData, width=0.1, height=0.15, angle=0., - loc=3, pad=0.5, borderpad=0.4, frameon=True) - - ax.add_artist(ae) - - # draw a horizontal bar with length of 0.1 in Data coordinate - # (ax.transData) with a label underneath. - asb = AnchoredSizeBar(ax.transData, - 0.1, - r"1$^{\prime}$", - loc=8, - pad=0.1, borderpad=0.5, sep=5, - frameon=False) - ax.add_artist(asb) - - plt.draw() - plt.show() diff --git a/examples/pylab_examples/animation_demo.py b/examples/pylab_examples/animation_demo.py deleted file mode 100644 index a8048d357b1c..000000000000 --- a/examples/pylab_examples/animation_demo.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Pyplot animation example. - -The method shown here is only for very simple, low-performance -use. For more demanding applications, look at the animation -module and the examples that use it. -""" - -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(6) -y = np.arange(5) -z = x * y[:, np.newaxis] - -for i in range(5): - if i == 0: - p = plt.imshow(z) - fig = plt.gcf() - plt.clim() # clamp the color limits - plt.title("Boring slide show") - else: - z = z + 2 - p.set_data(z) - - print("step", i) - plt.pause(0.5) diff --git a/examples/pylab_examples/annotation_demo.py b/examples/pylab_examples/annotation_demo.py deleted file mode 100644 index 2b67f169cc33..000000000000 --- a/examples/pylab_examples/annotation_demo.py +++ /dev/null @@ -1,142 +0,0 @@ -""" -Some examples of how to annotate points in figures. You specify an -annotation point xy=(x,y) and a text point xytext=(x,y) for the -annotated points and text location, respectively. Optionally, you can -specify the coordinate system of xy and xytext with one of the -following strings for xycoords and textcoords (default is 'data') - - - 'figure points' : points from the lower left corner of the figure - 'figure pixels' : pixels from the lower left corner of the figure - 'figure fraction' : 0,0 is lower left of figure and 1,1 is upper, right - 'axes points' : points from lower left corner of axes - 'axes pixels' : pixels from lower left corner of axes - 'axes fraction' : 0,0 is lower left of axes and 1,1 is upper right - 'offset points' : Specify an offset (in points) from the xy value - 'offset pixels' : Specify an offset (in pixels) from the xy value - 'data' : use the axes data coordinate system - -Optionally, you can specify arrow properties which draws and arrow -from the text to the annotated point by giving a dictionary of arrow -properties - -Valid keys are - - width : the width of the arrow in points - frac : the fraction of the arrow length occupied by the head - headwidth : the width of the base of the arrow head in points - shrink : move the tip and base some percent away from the - annotated point and text - any key for matplotlib.patches.polygon (e.g., facecolor) - -For physical coordinate systems (points or pixels) the origin is the -(bottom, left) of the figure or axes. -""" - - -import matplotlib.pyplot as plt -from matplotlib.patches import Ellipse -import numpy as np - - -if 1: - # if only one location is given, the text and xypoint being - # annotated are assumed to be the same - fig = plt.figure() - ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1, 5), ylim=(-3, 5)) - - t = np.arange(0.0, 5.0, 0.01) - s = np.cos(2*np.pi*t) - line, = ax.plot(t, s) - - ax.annotate('figure pixels', - xy=(10, 10), xycoords='figure pixels') - - ax.annotate('figure points', xy=(80, 80), - xycoords='figure points') - - ax.annotate('point offset from data', xy=(2, 1), - xycoords='data', - xytext=(-15, 25), textcoords='offset points', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='right', verticalalignment='bottom', - ) - - ax.annotate('axes fraction', xy=(3, 1), xycoords='data', - xytext=(0.8, 0.95), textcoords='axes fraction', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='right', verticalalignment='top', - ) - - ax.annotate('figure fraction', xy=(.025, .975), - xycoords='figure fraction', - horizontalalignment='left', verticalalignment='top', - fontsize=20) - - # use negative points or pixels to specify from right, top -10, 10 - # is 10 points to the left of the right side of the axes and 10 - # points above the bottom - ax.annotate('pixel offset from axes fraction', xy=(1, 0), - xycoords='axes fraction', - xytext=(-20, 20), - textcoords='offset pixels', - horizontalalignment='right', - verticalalignment='bottom') - - -if 1: - # you can specify the xypoint and the xytext in different - # positions and coordinate systems, and optionally turn on a - # connecting line and mark the point with a marker. Annotations - # work on polar axes too. In the example below, the xy point is - # in native coordinates (xycoords defaults to 'data'). For a - # polar axes, this is in (theta, radius) space. The text in this - # example is placed in the fractional figure coordinate system. - # Text keyword args like horizontal and vertical alignment are - # respected - fig = plt.figure() - ax = fig.add_subplot(111, projection='polar') - r = np.arange(0, 1, 0.001) - theta = 2*2*np.pi*r - line, = ax.plot(theta, r) - - ind = 800 - thisr, thistheta = r[ind], theta[ind] - ax.plot([thistheta], [thisr], 'o') - ax.annotate('a polar annotation', - xy=(thistheta, thisr), # theta, radius - xytext=(0.05, 0.05), # fraction, fraction - textcoords='figure fraction', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='left', - verticalalignment='bottom', - ) - - -if 1: - # You can also use polar notation on a cartesian axes. Here the - # native coordinate system ('data') is cartesian, so you need to - # specify the xycoords and textcoords as 'polar' if you want to - # use (theta, radius) - - el = Ellipse((0, 0), 10, 20, facecolor='r', alpha=0.5) - - fig = plt.figure() - ax = fig.add_subplot(111, aspect='equal') - ax.add_artist(el) - el.set_clip_box(ax.bbox) - ax.annotate('the top', - xy=(np.pi/2., 10.), # theta, radius - xytext=(np.pi/3, 20.), # theta, radius - xycoords='polar', - textcoords='polar', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='left', - verticalalignment='bottom', - clip_on=True, # clip to the axes bounding box - ) - - ax.set_xlim(-20, 20) - ax.set_ylim(-20, 20) - -plt.show() diff --git a/examples/pylab_examples/annotation_demo2.py b/examples/pylab_examples/annotation_demo2.py deleted file mode 100644 index ee6e2c95a24f..000000000000 --- a/examples/pylab_examples/annotation_demo2.py +++ /dev/null @@ -1,153 +0,0 @@ - -import matplotlib.pyplot as plt -from matplotlib.patches import Ellipse -import numpy as np - -if 1: - fig = plt.figure(1, figsize=(8, 5)) - ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1, 5), ylim=(-4, 3)) - - t = np.arange(0.0, 5.0, 0.01) - s = np.cos(2*np.pi*t) - line, = ax.plot(t, s, lw=3) - - ax.annotate('arrowstyle', xy=(0, 1), xycoords='data', - xytext=(-50, 30), textcoords='offset points', - arrowprops=dict(arrowstyle="->") - ) - - ax.annotate('arc3', xy=(0.5, -1), xycoords='data', - xytext=(-30, -30), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc3,rad=.2") - ) - - ax.annotate('arc', xy=(1., 1), xycoords='data', - xytext=(-40, 30), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc,angleA=0,armA=30,rad=10"), - ) - - ax.annotate('arc', xy=(1.5, -1), xycoords='data', - xytext=(-40, -30), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="arc,angleA=0,armA=20,angleB=-90,armB=15,rad=7"), - ) - - ax.annotate('angle', xy=(2., 1), xycoords='data', - xytext=(-50, 30), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="angle,angleA=0,angleB=90,rad=10"), - ) - - ax.annotate('angle3', xy=(2.5, -1), xycoords='data', - xytext=(-50, -30), textcoords='offset points', - arrowprops=dict(arrowstyle="->", - connectionstyle="angle3,angleA=0,angleB=-90"), - ) - - ax.annotate('angle', xy=(3., 1), xycoords='data', - xytext=(-50, 30), textcoords='offset points', - bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="->", - connectionstyle="angle,angleA=0,angleB=90,rad=10"), - ) - - ax.annotate('angle', xy=(3.5, -1), xycoords='data', - xytext=(-70, -60), textcoords='offset points', - size=20, - bbox=dict(boxstyle="round4,pad=.5", fc="0.8"), - arrowprops=dict(arrowstyle="->", - connectionstyle="angle,angleA=0,angleB=-90,rad=10"), - ) - - ax.annotate('angle', xy=(4., 1), xycoords='data', - xytext=(-50, 30), textcoords='offset points', - bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="->", - shrinkA=0, shrinkB=10, - connectionstyle="angle,angleA=0,angleB=90,rad=10"), - ) - - ann = ax.annotate('', xy=(4., 1.), xycoords='data', - xytext=(4.5, -1), textcoords='data', - arrowprops=dict(arrowstyle="<->", - connectionstyle="bar", - ec="k", - shrinkA=5, shrinkB=5, - ) - ) - - -if 1: - fig = plt.figure(2) - fig.clf() - ax = fig.add_subplot(111, autoscale_on=False, xlim=(-1, 5), ylim=(-5, 3)) - - el = Ellipse((2, -1), 0.5, 0.5) - ax.add_patch(el) - - ax.annotate('$->$', xy=(2., -1), xycoords='data', - xytext=(-150, -140), textcoords='offset points', - bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="->", - patchB=el, - connectionstyle="angle,angleA=90,angleB=0,rad=10"), - ) - - ax.annotate('fancy', xy=(2., -1), xycoords='data', - xytext=(-100, 60), textcoords='offset points', - size=20, - # bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="fancy", - fc="0.6", ec="none", - patchB=el, - connectionstyle="angle3,angleA=0,angleB=-90"), - ) - - ax.annotate('simple', xy=(2., -1), xycoords='data', - xytext=(100, 60), textcoords='offset points', - size=20, - # bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="simple", - fc="0.6", ec="none", - patchB=el, - connectionstyle="arc3,rad=0.3"), - ) - - ax.annotate('wedge', xy=(2., -1), xycoords='data', - xytext=(-100, -100), textcoords='offset points', - size=20, - # bbox=dict(boxstyle="round", fc="0.8"), - arrowprops=dict(arrowstyle="wedge,tail_width=0.7", - fc="0.6", ec="none", - patchB=el, - connectionstyle="arc3,rad=-0.3"), - ) - - ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', - xytext=(0, -45), textcoords='offset points', - size=20, - bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec=(1., .5, .5)), - arrowprops=dict(arrowstyle="wedge,tail_width=1.", - fc=(1.0, 0.7, 0.7), ec=(1., .5, .5), - patchA=None, - patchB=el, - relpos=(0.2, 0.8), - connectionstyle="arc3,rad=-0.1"), - ) - - ann = ax.annotate('wedge', xy=(2., -1), xycoords='data', - xytext=(35, 0), textcoords='offset points', - size=20, va="center", - bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec="none"), - arrowprops=dict(arrowstyle="wedge,tail_width=1.", - fc=(1.0, 0.7, 0.7), ec="none", - patchA=None, - patchB=el, - relpos=(0.2, 0.5), - ) - ) - - -plt.show() diff --git a/examples/pylab_examples/annotation_demo3.py b/examples/pylab_examples/annotation_demo3.py deleted file mode 100644 index 0c298bdbc121..000000000000 --- a/examples/pylab_examples/annotation_demo3.py +++ /dev/null @@ -1,96 +0,0 @@ -import matplotlib.pyplot as plt - -fig, (ax1, ax2) = plt.subplots(1, 2) - -bbox_args = dict(boxstyle="round", fc="0.8") -arrow_args = dict(arrowstyle="->") - -ax1.annotate('figure fraction : 0, 0', xy=(0, 0), xycoords='figure fraction', - xytext=(20, 20), textcoords='offset points', - ha="left", va="bottom", - bbox=bbox_args, - arrowprops=arrow_args - ) - -ax1.annotate('figure fraction : 1, 1', xy=(1, 1), xycoords='figure fraction', - xytext=(-20, -20), textcoords='offset points', - ha="right", va="top", - bbox=bbox_args, - arrowprops=arrow_args - ) - -ax1.annotate('axes fraction : 0, 0', xy=(0, 0), xycoords='axes fraction', - xytext=(20, 20), textcoords='offset points', - ha="left", va="bottom", - bbox=bbox_args, - arrowprops=arrow_args - ) - -ax1.annotate('axes fraction : 1, 1', xy=(1, 1), xycoords='axes fraction', - xytext=(-20, -20), textcoords='offset points', - ha="right", va="top", - bbox=bbox_args, - arrowprops=arrow_args - ) - - -an1 = ax1.annotate('Drag me 1', xy=(.5, .7), xycoords='data', - #xytext=(.5, .7), textcoords='data', - ha="center", va="center", - bbox=bbox_args, - #arrowprops=arrow_args - ) - -an2 = ax1.annotate('Drag me 2', xy=(.5, .5), xycoords=an1, - xytext=(.5, .3), textcoords='axes fraction', - ha="center", va="center", - bbox=bbox_args, - arrowprops=dict(patchB=an1.get_bbox_patch(), - connectionstyle="arc3,rad=0.2", - **arrow_args) - ) - -an3 = ax1.annotate('', xy=(.5, .5), xycoords=an2, - xytext=(.5, .5), textcoords=an1, - ha="center", va="center", - bbox=bbox_args, - arrowprops=dict(patchA=an1.get_bbox_patch(), - patchB=an2.get_bbox_patch(), - connectionstyle="arc3,rad=0.2", - **arrow_args) - ) - - -t = ax2.annotate('xy=(0, 1)\nxycoords=("data", "axes fraction")', - xy=(0, 1), xycoords=("data", 'axes fraction'), - xytext=(0, -20), textcoords='offset points', - ha="center", va="top", - bbox=bbox_args, - arrowprops=arrow_args - ) - -from matplotlib.text import OffsetFrom - -ax2.annotate('xy=(0.5, 0)\nxycoords=artist', - xy=(0.5, 0.), xycoords=t, - xytext=(0, -20), textcoords='offset points', - ha="center", va="top", - bbox=bbox_args, - arrowprops=arrow_args - ) - -ax2.annotate('xy=(0.8, 0.5)\nxycoords=ax1.transData', - xy=(0.8, 0.5), xycoords=ax1.transData, - xytext=(10, 10), textcoords=OffsetFrom(ax2.bbox, (0, 0), "points"), - ha="left", va="bottom", - bbox=bbox_args, - arrowprops=arrow_args - ) - -ax2.set_xlim(-2, 2) -ax2.set_ylim(-2, 2) - -an1.draggable() -an2.draggable() - -plt.show() diff --git a/examples/pylab_examples/anscombe.py b/examples/pylab_examples/anscombe.py deleted file mode 100755 index 0c043af34ce4..000000000000 --- a/examples/pylab_examples/anscombe.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import print_function -""" -Edward Tufte uses this example from Anscombe to show 4 datasets of x -and y that have the same mean, standard deviation, and regression -line, but which are qualitatively different. - -matplotlib fun for a rainy day -""" - -import matplotlib.pyplot as plt -import numpy as np - -x = np.array([10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]) -y1 = np.array([8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]) -y2 = np.array([9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]) -y3 = np.array([7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]) -x4 = np.array([8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8]) -y4 = np.array([6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]) - - -def fit(x): - return 3 + 0.5*x - - -xfit = np.array([np.amin(x), np.amax(x)]) - -plt.subplot(221) -plt.plot(x, y1, 'ks', xfit, fit(xfit), 'r-', lw=2) -plt.axis([2, 20, 2, 14]) -plt.setp(plt.gca(), xticklabels=[], yticks=(4, 8, 12), xticks=(0, 10, 20)) -plt.text(3, 12, 'I', fontsize=20) - -plt.subplot(222) -plt.plot(x, y2, 'ks', xfit, fit(xfit), 'r-', lw=2) -plt.axis([2, 20, 2, 14]) -plt.setp(plt.gca(), xticklabels=[], yticks=(4, 8, 12), yticklabels=[], xticks=(0, 10, 20)) -plt.text(3, 12, 'II', fontsize=20) - -plt.subplot(223) -plt.plot(x, y3, 'ks', xfit, fit(xfit), 'r-', lw=2) -plt.axis([2, 20, 2, 14]) -plt.text(3, 12, 'III', fontsize=20) -plt.setp(plt.gca(), yticks=(4, 8, 12), xticks=(0, 10, 20)) - -plt.subplot(224) - -xfit = np.array([np.amin(x4), np.amax(x4)]) -plt.plot(x4, y4, 'ks', xfit, fit(xfit), 'r-', lw=2) -plt.axis([2, 20, 2, 14]) -plt.setp(plt.gca(), yticklabels=[], yticks=(4, 8, 12), xticks=(0, 10, 20)) -plt.text(3, 12, 'IV', fontsize=20) - -# verify the stats -pairs = (x, y1), (x, y2), (x, y3), (x4, y4) -for x, y in pairs: - print('mean=%1.2f, std=%1.2f, r=%1.2f' % (np.mean(y), np.std(y), np.corrcoef(x, y)[0][1])) - -plt.show() diff --git a/examples/pylab_examples/arctest.py b/examples/pylab_examples/arctest.py deleted file mode 100644 index 35c12bee407d..000000000000 --- a/examples/pylab_examples/arctest.py +++ /dev/null @@ -1,18 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - - -def f(t): - 'a damped exponential' - s1 = np.cos(2 * np.pi * t) - e1 = np.exp(-t) - return s1 * e1 - -t1 = np.arange(0.0, 5.0, .2) - - -l = plt.plot(t1, f(t1), 'ro') -plt.setp(l, 'markersize', 30) -plt.setp(l, 'markerfacecolor', 'b') - -plt.show() diff --git a/examples/pylab_examples/arrow_demo.py b/examples/pylab_examples/arrow_demo.py deleted file mode 100644 index 73781f5f1e65..000000000000 --- a/examples/pylab_examples/arrow_demo.py +++ /dev/null @@ -1,313 +0,0 @@ -"""Arrow drawing example for the new fancy_arrow facilities. - -Code contributed by: Rob Knight - -usage: - - python arrow_demo.py realistic|full|sample|extreme - - -""" -import matplotlib.pyplot as plt -import numpy as np - -rates_to_bases = {'r1': 'AT', 'r2': 'TA', 'r3': 'GA', 'r4': 'AG', 'r5': 'CA', - 'r6': 'AC', 'r7': 'GT', 'r8': 'TG', 'r9': 'CT', 'r10': 'TC', - 'r11': 'GC', 'r12': 'CG'} -numbered_bases_to_rates = dict([(v, k) for k, v in rates_to_bases.items()]) -lettered_bases_to_rates = dict([(v, 'r' + v) for k, v in rates_to_bases.items()]) - - -def add_dicts(d1, d2): - """Adds two dicts and returns the result.""" - result = d1.copy() - result.update(d2) - return result - - -def make_arrow_plot(data, size=4, display='length', shape='right', - max_arrow_width=0.03, arrow_sep=0.02, alpha=0.5, - normalize_data=False, ec=None, labelcolor=None, - head_starts_at_zero=True, rate_labels=lettered_bases_to_rates, - **kwargs): - """Makes an arrow plot. - - Parameters: - - data: dict with probabilities for the bases and pair transitions. - size: size of the graph in inches. - display: 'length', 'width', or 'alpha' for arrow property to change. - shape: 'full', 'left', or 'right' for full or half arrows. - max_arrow_width: maximum width of an arrow, data coordinates. - arrow_sep: separation between arrows in a pair, data coordinates. - alpha: maximum opacity of arrows, default 0.8. - - **kwargs can be anything allowed by a Arrow object, e.g. - linewidth and edgecolor. - """ - - plt.xlim(-0.5, 1.5) - plt.ylim(-0.5, 1.5) - plt.gcf().set_size_inches(size, size) - plt.xticks([]) - plt.yticks([]) - max_text_size = size*12 - min_text_size = size - label_text_size = size*2.5 - text_params = {'ha': 'center', 'va': 'center', 'family': 'sans-serif', - 'fontweight': 'bold'} - r2 = np.sqrt(2) - - deltas = { - 'AT': (1, 0), - 'TA': (-1, 0), - 'GA': (0, 1), - 'AG': (0, -1), - 'CA': (-1/r2, 1/r2), - 'AC': (1/r2, -1/r2), - 'GT': (1/r2, 1/r2), - 'TG': (-1/r2, -1/r2), - 'CT': (0, 1), - 'TC': (0, -1), - 'GC': (1, 0), - 'CG': (-1, 0) - } - - colors = { - 'AT': 'r', - 'TA': 'k', - 'GA': 'g', - 'AG': 'r', - 'CA': 'b', - 'AC': 'r', - 'GT': 'g', - 'TG': 'k', - 'CT': 'b', - 'TC': 'k', - 'GC': 'g', - 'CG': 'b' - } - - label_positions = { - 'AT': 'center', - 'TA': 'center', - 'GA': 'center', - 'AG': 'center', - 'CA': 'left', - 'AC': 'left', - 'GT': 'left', - 'TG': 'left', - 'CT': 'center', - 'TC': 'center', - 'GC': 'center', - 'CG': 'center' - } - - def do_fontsize(k): - return float(np.clip(max_text_size*np.sqrt(data[k]), - min_text_size, max_text_size)) - - A = plt.text(0, 1, '$A_3$', color='r', size=do_fontsize('A'), **text_params) - T = plt.text(1, 1, '$T_3$', color='k', size=do_fontsize('T'), **text_params) - G = plt.text(0, 0, '$G_3$', color='g', size=do_fontsize('G'), **text_params) - C = plt.text(1, 0, '$C_3$', color='b', size=do_fontsize('C'), **text_params) - - arrow_h_offset = 0.25 # data coordinates, empirically determined - max_arrow_length = 1 - 2*arrow_h_offset - - max_arrow_width = max_arrow_width - max_head_width = 2.5*max_arrow_width - max_head_length = 2*max_arrow_width - arrow_params = {'length_includes_head': True, 'shape': shape, - 'head_starts_at_zero': head_starts_at_zero} - ax = plt.gca() - sf = 0.6 # max arrow size represents this in data coords - - d = (r2/2 + arrow_h_offset - 0.5)/r2 # distance for diags - r2v = arrow_sep/r2 # offset for diags - - # tuple of x, y for start position - positions = { - 'AT': (arrow_h_offset, 1 + arrow_sep), - 'TA': (1 - arrow_h_offset, 1 - arrow_sep), - 'GA': (-arrow_sep, arrow_h_offset), - 'AG': (arrow_sep, 1 - arrow_h_offset), - 'CA': (1 - d - r2v, d - r2v), - 'AC': (d + r2v, 1 - d + r2v), - 'GT': (d - r2v, d + r2v), - 'TG': (1 - d + r2v, 1 - d - r2v), - 'CT': (1 - arrow_sep, arrow_h_offset), - 'TC': (1 + arrow_sep, 1 - arrow_h_offset), - 'GC': (arrow_h_offset, arrow_sep), - 'CG': (1 - arrow_h_offset, -arrow_sep), - } - - if normalize_data: - # find maximum value for rates, i.e. where keys are 2 chars long - max_val = 0 - for k, v in data.items(): - if len(k) == 2: - max_val = max(max_val, v) - # divide rates by max val, multiply by arrow scale factor - for k, v in data.items(): - data[k] = v/max_val*sf - - def draw_arrow(pair, alpha=alpha, ec=ec, labelcolor=labelcolor): - # set the length of the arrow - if display == 'length': - length = max_head_length + data[pair]/sf*(max_arrow_length - - max_head_length) - else: - length = max_arrow_length - # set the transparency of the arrow - if display == 'alph': - alpha = min(data[pair]/sf, alpha) - else: - alpha = alpha - # set the width of the arrow - if display == 'width': - scale = data[pair]/sf - width = max_arrow_width*scale - head_width = max_head_width*scale - head_length = max_head_length*scale - else: - width = max_arrow_width - head_width = max_head_width - head_length = max_head_length - - fc = colors[pair] - ec = ec or fc - - x_scale, y_scale = deltas[pair] - x_pos, y_pos = positions[pair] - plt.arrow(x_pos, y_pos, x_scale*length, y_scale*length, - fc=fc, ec=ec, alpha=alpha, width=width, head_width=head_width, - head_length=head_length, **arrow_params) - - # figure out coordinates for text - # if drawing relative to base: x and y are same as for arrow - # dx and dy are one arrow width left and up - # need to rotate based on direction of arrow, use x_scale and y_scale - # as sin x and cos x? - sx, cx = y_scale, x_scale - - where = label_positions[pair] - if where == 'left': - orig_position = 3*np.array([[max_arrow_width, max_arrow_width]]) - elif where == 'absolute': - orig_position = np.array([[max_arrow_length/2.0, 3*max_arrow_width]]) - elif where == 'right': - orig_position = np.array([[length - 3*max_arrow_width, - 3*max_arrow_width]]) - elif where == 'center': - orig_position = np.array([[length/2.0, 3*max_arrow_width]]) - else: - raise ValueError("Got unknown position parameter %s" % where) - - M = np.array([[cx, sx], [-sx, cx]]) - coords = np.dot(orig_position, M) + [[x_pos, y_pos]] - x, y = np.ravel(coords) - orig_label = rate_labels[pair] - label = '$%s_{_{\mathrm{%s}}}$' % (orig_label[0], orig_label[1:]) - - plt.text(x, y, label, size=label_text_size, ha='center', va='center', - color=labelcolor or fc) - - for p in sorted(positions): - draw_arrow(p) - -# test data -all_on_max = dict([(i, 1) for i in 'TCAG'] + - [(i + j, 0.6) for i in 'TCAG' for j in 'TCAG']) - -realistic_data = { - 'A': 0.4, - 'T': 0.3, - 'G': 0.5, - 'C': 0.2, - 'AT': 0.4, - 'AC': 0.3, - 'AG': 0.2, - 'TA': 0.2, - 'TC': 0.3, - 'TG': 0.4, - 'CT': 0.2, - 'CG': 0.3, - 'CA': 0.2, - 'GA': 0.1, - 'GT': 0.4, - 'GC': 0.1, - } - -extreme_data = { - 'A': 0.75, - 'T': 0.10, - 'G': 0.10, - 'C': 0.05, - 'AT': 0.6, - 'AC': 0.3, - 'AG': 0.1, - 'TA': 0.02, - 'TC': 0.3, - 'TG': 0.01, - 'CT': 0.2, - 'CG': 0.5, - 'CA': 0.2, - 'GA': 0.1, - 'GT': 0.4, - 'GC': 0.2, - } - -sample_data = { - 'A': 0.2137, - 'T': 0.3541, - 'G': 0.1946, - 'C': 0.2376, - 'AT': 0.0228, - 'AC': 0.0684, - 'AG': 0.2056, - 'TA': 0.0315, - 'TC': 0.0629, - 'TG': 0.0315, - 'CT': 0.1355, - 'CG': 0.0401, - 'CA': 0.0703, - 'GA': 0.1824, - 'GT': 0.0387, - 'GC': 0.1106, - } - - -if __name__ == '__main__': - from sys import argv - d = None - if len(argv) > 1: - if argv[1] == 'full': - d = all_on_max - scaled = False - elif argv[1] == 'extreme': - d = extreme_data - scaled = False - elif argv[1] == 'realistic': - d = realistic_data - scaled = False - elif argv[1] == 'sample': - d = sample_data - scaled = True - if d is None: - d = all_on_max - scaled = False - if len(argv) > 2: - display = argv[2] - else: - display = 'length' - - size = 4 - plt.figure(figsize=(size, size)) - - make_arrow_plot(d, display=display, linewidth=0.001, edgecolor=None, - normalize_data=scaled, head_starts_at_zero=True, size=size) - - plt.draw() - - plt.show() diff --git a/examples/pylab_examples/arrow_simple_demo.py b/examples/pylab_examples/arrow_simple_demo.py deleted file mode 100644 index 65fb5a9eabc3..000000000000 --- a/examples/pylab_examples/arrow_simple_demo.py +++ /dev/null @@ -1,5 +0,0 @@ -import matplotlib.pyplot as plt - -ax = plt.axes() -ax.arrow(0, 0, 0.5, 0.5, head_width=0.05, head_length=0.1, fc='k', ec='k') -plt.show() diff --git a/examples/pylab_examples/axes_demo.py b/examples/pylab_examples/axes_demo.py deleted file mode 100644 index 7e425c932ad2..000000000000 --- a/examples/pylab_examples/axes_demo.py +++ /dev/null @@ -1,33 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -# create some data to use for the plot -dt = 0.001 -t = np.arange(0.0, 10.0, dt) -r = np.exp(-t[:1000]/0.05) # impulse response -x = np.random.randn(len(t)) -s = np.convolve(x, r)[:len(x)]*dt # colored noise - -# the main axes is subplot(111) by default -plt.plot(t, s) -plt.axis([0, 1, 1.1*np.amin(s), 2*np.amax(s)]) -plt.xlabel('time (s)') -plt.ylabel('current (nA)') -plt.title('Gaussian colored noise') - -# this is an inset axes over the main axes -a = plt.axes([.65, .6, .2, .2], facecolor='y') -n, bins, patches = plt.hist(s, 400, normed=1) -plt.title('Probability') -plt.xticks([]) -plt.yticks([]) - -# this is another inset axes over the main axes -a = plt.axes([0.2, 0.6, .2, .2], facecolor='y') -plt.plot(t[:len(r)], r) -plt.title('Impulse response') -plt.xlim(0, 0.2) -plt.xticks([]) -plt.yticks([]) - -plt.show() diff --git a/examples/pylab_examples/axes_props.py b/examples/pylab_examples/axes_props.py deleted file mode 100644 index bc6de460a500..000000000000 --- a/examples/pylab_examples/axes_props.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -You can control the axis tick and grid properties -""" - -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 2.0, 0.01) -s = np.sin(2 * np.pi * t) -fig, ax = plt.subplots() -ax.plot(t, s) -ax.grid(True) - -ticklines = ax.get_xticklines() + ax.get_yticklines() -gridlines = ax.get_xgridlines() + ax.get_ygridlines() -ticklabels = ax.get_xticklabels() + ax.get_yticklabels() - -for line in ticklines: - line.set_linewidth(3) - -for line in gridlines: - line.set_linestyle('-.') - -for label in ticklabels: - label.set_color('r') - label.set_fontsize('medium') - -plt.show() diff --git a/examples/pylab_examples/axes_zoom_effect.py b/examples/pylab_examples/axes_zoom_effect.py deleted file mode 100644 index e34a3cebc2a3..000000000000 --- a/examples/pylab_examples/axes_zoom_effect.py +++ /dev/null @@ -1,118 +0,0 @@ -from matplotlib.transforms import Bbox, TransformedBbox, \ - blended_transform_factory - -from mpl_toolkits.axes_grid1.inset_locator import BboxPatch, BboxConnector,\ - BboxConnectorPatch - - -def connect_bbox(bbox1, bbox2, - loc1a, loc2a, loc1b, loc2b, - prop_lines, prop_patches=None): - if prop_patches is None: - prop_patches = prop_lines.copy() - prop_patches["alpha"] = prop_patches.get("alpha", 1)*0.2 - - c1 = BboxConnector(bbox1, bbox2, loc1=loc1a, loc2=loc2a, **prop_lines) - c1.set_clip_on(False) - c2 = BboxConnector(bbox1, bbox2, loc1=loc1b, loc2=loc2b, **prop_lines) - c2.set_clip_on(False) - - bbox_patch1 = BboxPatch(bbox1, **prop_patches) - bbox_patch2 = BboxPatch(bbox2, **prop_patches) - - p = BboxConnectorPatch(bbox1, bbox2, - # loc1a=3, loc2a=2, loc1b=4, loc2b=1, - loc1a=loc1a, loc2a=loc2a, loc1b=loc1b, loc2b=loc2b, - **prop_patches) - p.set_clip_on(False) - - return c1, c2, bbox_patch1, bbox_patch2, p - - -def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs): - """ - ax1 : the main axes - ax1 : the zoomed axes - (xmin,xmax) : the limits of the colored area in both plot axes. - - connect ax1 & ax2. The x-range of (xmin, xmax) in both axes will - be marked. The keywords parameters will be used ti create - patches. - - """ - - trans1 = blended_transform_factory(ax1.transData, ax1.transAxes) - trans2 = blended_transform_factory(ax2.transData, ax2.transAxes) - - bbox = Bbox.from_extents(xmin, 0, xmax, 1) - - mybbox1 = TransformedBbox(bbox, trans1) - mybbox2 = TransformedBbox(bbox, trans2) - - prop_patches = kwargs.copy() - prop_patches["ec"] = "none" - prop_patches["alpha"] = 0.2 - - c1, c2, bbox_patch1, bbox_patch2, p = \ - connect_bbox(mybbox1, mybbox2, - loc1a=3, loc2a=2, loc1b=4, loc2b=1, - prop_lines=kwargs, prop_patches=prop_patches) - - ax1.add_patch(bbox_patch1) - ax2.add_patch(bbox_patch2) - ax2.add_patch(c1) - ax2.add_patch(c2) - ax2.add_patch(p) - - return c1, c2, bbox_patch1, bbox_patch2, p - - -def zoom_effect02(ax1, ax2, **kwargs): - """ - ax1 : the main axes - ax1 : the zoomed axes - - Similar to zoom_effect01. The xmin & xmax will be taken from the - ax1.viewLim. - """ - - tt = ax1.transScale + (ax1.transLimits + ax2.transAxes) - trans = blended_transform_factory(ax2.transData, tt) - - mybbox1 = ax1.bbox - mybbox2 = TransformedBbox(ax1.viewLim, trans) - - prop_patches = kwargs.copy() - prop_patches["ec"] = "none" - prop_patches["alpha"] = 0.2 - - c1, c2, bbox_patch1, bbox_patch2, p = \ - connect_bbox(mybbox1, mybbox2, - loc1a=3, loc2a=2, loc1b=4, loc2b=1, - prop_lines=kwargs, prop_patches=prop_patches) - - ax1.add_patch(bbox_patch1) - ax2.add_patch(bbox_patch2) - ax2.add_patch(c1) - ax2.add_patch(c2) - ax2.add_patch(p) - - return c1, c2, bbox_patch1, bbox_patch2, p - - -import matplotlib.pyplot as plt - -plt.figure(1, figsize=(5, 5)) -ax1 = plt.subplot(221) -ax2 = plt.subplot(212) -ax2.set_xlim(0, 1) -ax2.set_xlim(0, 5) -zoom_effect01(ax1, ax2, 0.2, 0.8) - - -ax1 = plt.subplot(222) -ax1.set_xlim(2, 3) -ax2.set_xlim(0, 5) -zoom_effect02(ax1, ax2) - -plt.show() diff --git a/examples/pylab_examples/axhspan_demo.py b/examples/pylab_examples/axhspan_demo.py deleted file mode 100644 index 13a91188166f..000000000000 --- a/examples/pylab_examples/axhspan_demo.py +++ /dev/null @@ -1,32 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -t = np.arange(-1, 2, .01) -s = np.sin(2*np.pi*t) - -plt.plot(t, s) -# draw a thick red hline at y=0 that spans the xrange -l = plt.axhline(linewidth=8, color='#d62728') - -# draw a default hline at y=1 that spans the xrange -l = plt.axhline(y=1) - -# draw a default vline at x=1 that spans the yrange -l = plt.axvline(x=1) - -# draw a thick blue vline at x=0 that spans the upper quadrant of -# the yrange -l = plt.axvline(x=0, ymin=0.75, linewidth=8, color='#1f77b4') - -# draw a default hline at y=.5 that spans the middle half of -# the axes -l = plt.axhline(y=.5, xmin=0.25, xmax=0.75) - -p = plt.axhspan(0.25, 0.75, facecolor='0.5', alpha=0.5) - -p = plt.axvspan(1.25, 1.55, facecolor='#2ca02c', alpha=0.5) - -plt.axis([-1, 2, -1, 2]) - - -plt.show() diff --git a/examples/pylab_examples/axis_equal_demo.py b/examples/pylab_examples/axis_equal_demo.py deleted file mode 100644 index 998720dac1de..000000000000 --- a/examples/pylab_examples/axis_equal_demo.py +++ /dev/null @@ -1,32 +0,0 @@ -'''This example is only interesting when ran in interactive mode''' - -import matplotlib.pyplot as plt -import numpy as np - -# Plot circle or radius 3 - -an = np.linspace(0, 2*np.pi, 100) - -plt.subplot(221) -plt.plot(3*np.cos(an), 3*np.sin(an)) -plt.title('not equal, looks like ellipse', fontsize=10) - -plt.subplot(222) -plt.plot(3*np.cos(an), 3*np.sin(an)) -plt.axis('equal') -plt.title('equal, looks like circle', fontsize=10) - -plt.subplot(223) -plt.plot(3*np.cos(an), 3*np.sin(an)) -plt.axis('equal') -plt.axis([-3, 3, -3, 3]) -plt.title('looks like circle, even after changing limits', fontsize=10) - -plt.subplot(224) -plt.plot(3*np.cos(an), 3*np.sin(an)) -plt.axis('equal') -plt.axis([-3, 3, -3, 3]) -plt.plot([0, 4], [0, 4]) -plt.title('still equal after adding line', fontsize=10) - -plt.show() diff --git a/examples/pylab_examples/bar_stacked.py b/examples/pylab_examples/bar_stacked.py deleted file mode 100755 index d2fb3b94841b..000000000000 --- a/examples/pylab_examples/bar_stacked.py +++ /dev/null @@ -1,24 +0,0 @@ -# a stacked bar plot with errorbars -import numpy as np -import matplotlib.pyplot as plt - - -N = 5 -menMeans = (20, 35, 30, 35, 27) -womenMeans = (25, 32, 34, 20, 25) -menStd = (2, 3, 4, 1, 2) -womenStd = (3, 5, 2, 3, 3) -ind = np.arange(N) # the x locations for the groups -width = 0.35 # the width of the bars: can also be len(x) sequence - -p1 = plt.bar(ind, menMeans, width, color='#d62728', yerr=menStd) -p2 = plt.bar(ind, womenMeans, width, - bottom=menMeans, yerr=womenStd) - -plt.ylabel('Scores') -plt.title('Scores by group and gender') -plt.xticks(ind + width/2., ('G1', 'G2', 'G3', 'G4', 'G5')) -plt.yticks(np.arange(0, 81, 10)) -plt.legend((p1[0], p2[0]), ('Men', 'Women')) - -plt.show() diff --git a/examples/pylab_examples/barb_demo.py b/examples/pylab_examples/barb_demo.py deleted file mode 100644 index 5681dacf4c8e..000000000000 --- a/examples/pylab_examples/barb_demo.py +++ /dev/null @@ -1,53 +0,0 @@ -''' -Demonstration of wind barb plots -''' -import matplotlib.pyplot as plt -import numpy as np - -x = np.linspace(-5, 5, 5) -X, Y = np.meshgrid(x, x) -U, V = 12*X, 12*Y - -data = [(-1.5, .5, -6, -6), - (1, -1, -46, 46), - (-3, -1, 11, -11), - (1, 1.5, 80, 80), - (0.5, 0.25, 25, 15), - (-1.5, -0.5, -5, 40)] - -data = np.array(data, dtype=[('x', np.float32), ('y', np.float32), - ('u', np.float32), ('v', np.float32)]) - -# Default parameters, uniform grid -ax = plt.subplot(2, 2, 1) -ax.barbs(X, Y, U, V) - -# Arbitrary set of vectors, make them longer and change the pivot point -#(point around which they're rotated) to be the middle -ax = plt.subplot(2, 2, 2) -ax.barbs(data['x'], data['y'], data['u'], data['v'], length=8, pivot='middle') - -# Showing colormapping with uniform grid. Fill the circle for an empty barb, -# don't round the values, and change some of the size parameters -ax = plt.subplot(2, 2, 3) -ax.barbs(X, Y, U, V, np.sqrt(U*U + V*V), fill_empty=True, rounding=False, - sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3)) - -# Change colors as well as the increments for parts of the barbs -ax = plt.subplot(2, 2, 4) -ax.barbs(data['x'], data['y'], data['u'], data['v'], flagcolor='r', - barbcolor=['b', 'g'], barb_increments=dict(half=10, full=20, flag=100), - flip_barb=True) - -# Masked arrays are also supported -masked_u = np.ma.masked_array(data['u']) -masked_u[4] = 1000 # Bad value that should not be plotted when masked -masked_u[4] = np.ma.masked - -# Identical plot to panel 2 in the first figure, but with the point at -#(0.5, 0.25) missing (masked) -fig2 = plt.figure() -ax = fig2.add_subplot(1, 1, 1) -ax.barbs(data['x'], data['y'], masked_u, data['v'], length=8, pivot='middle') - -plt.show() diff --git a/examples/pylab_examples/barchart_demo.py b/examples/pylab_examples/barchart_demo.py deleted file mode 100644 index 5d591f45cc90..000000000000 --- a/examples/pylab_examples/barchart_demo.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Bar chart demo with pairs of bars grouped for easy comparison. -""" -import numpy as np -import matplotlib.pyplot as plt - - -n_groups = 5 - -means_men = (20, 35, 30, 35, 27) -std_men = (2, 3, 4, 1, 2) - -means_women = (25, 32, 34, 20, 25) -std_women = (3, 5, 2, 3, 3) - -fig, ax = plt.subplots() - -index = np.arange(n_groups) -bar_width = 0.35 - -opacity = 0.4 -error_config = {'ecolor': '0.3'} - -rects1 = plt.bar(index, means_men, bar_width, - alpha=opacity, - color='b', - yerr=std_men, - error_kw=error_config, - label='Men') - -rects2 = plt.bar(index + bar_width, means_women, bar_width, - alpha=opacity, - color='r', - yerr=std_women, - error_kw=error_config, - label='Women') - -plt.xlabel('Group') -plt.ylabel('Scores') -plt.title('Scores by group and gender') -plt.xticks(index + bar_width, ('A', 'B', 'C', 'D', 'E')) -plt.legend() - -plt.tight_layout() -plt.show() diff --git a/examples/pylab_examples/barchart_demo2.py b/examples/pylab_examples/barchart_demo2.py deleted file mode 100644 index faa2bb76511c..000000000000 --- a/examples/pylab_examples/barchart_demo2.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Thanks Josh Hemann for the example - -This examples comes from an application in which grade school gym -teachers wanted to be able to show parents how their child did across -a handful of fitness tests, and importantly, relative to how other -children did. To extract the plotting code for demo purposes, we'll -just make up some data for little Johnny Doe... - -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.ticker import MaxNLocator -from collections import namedtuple - -Student = namedtuple('Student', ['name', 'grade', 'gender']) -Score = namedtuple('Score', ['score', 'percentile']) - -# GLOBAL CONSTANTS -testNames = ['Pacer Test', 'Flexed Arm\n Hang', 'Mile Run', 'Agility', - 'Push Ups'] -testMeta = dict(zip(testNames, ['laps', 'sec', 'min:sec', 'sec', ''])) - - -def attach_ordinal(num): - """helper function to add ordinal string to integers - - 1 -> 1st - 56 -> 56th - """ - suffixes = dict((str(i), v) for i, v in - enumerate(['th', 'st', 'nd', 'rd', 'th', - 'th', 'th', 'th', 'th', 'th'])) - - v = str(num) - # special case early teens - if v in {'11', '12', '13'}: - return v + 'th' - return v + suffixes[v[-1]] - - -def format_score(scr, test): - """ - Build up the score labels for the right Y-axis by first - appending a carriage return to each string and then tacking on - the appropriate meta information (i.e., 'laps' vs 'seconds'). We - want the labels centered on the ticks, so if there is no meta - info (like for pushups) then don't add the carriage return to - the string - """ - md = testMeta[test] - if md: - return '{0}\n{1}'.format(scr, md) - else: - return scr - - -def format_ycursor(y): - y = int(y) - if y < 0 or y >= len(testNames): - return '' - else: - return testNames[y] - - -def plot_student_results(student, scores, cohort_size): - # create the figure - fig, ax1 = plt.subplots(figsize=(9, 7)) - fig.subplots_adjust(left=0.115, right=0.88) - fig.canvas.set_window_title('Eldorado K-8 Fitness Chart') - - pos = np.arange(len(testNames)) + 0.5 # Center bars on the Y-axis ticks - - rects = ax1.barh(pos, [scores[k].percentile for k in testNames], - align='center', - height=0.5, color='m', - tick_label=testNames) - - ax1.set_title(student.name) - - ax1.set_xlim([0, 100]) - ax1.xaxis.set_major_locator(MaxNLocator(11)) - ax1.xaxis.grid(True, linestyle='--', which='major', - color='grey', alpha=.25) - - # Plot a solid vertical gridline to highlight the median position - ax1.axvline(50, color='grey', alpha=0.25) - # set X-axis tick marks at the deciles - cohort_label = ax1.text(.5, -.07, 'Cohort Size: {0}'.format(cohort_size), - horizontalalignment='center', size='small', - transform=ax1.transAxes) - - # Set the right-hand Y-axis ticks and labels - ax2 = ax1.twinx() - - scoreLabels = [format_score(scores[k].score, k) for k in testNames] - - # set the tick locations - ax2.set_yticks(pos) - # make sure that the limits are set equally on both yaxis so the - # ticks line up - ax2.set_ylim(ax1.get_ylim()) - - # set the tick labels - ax2.set_yticklabels(scoreLabels) - - ax2.set_ylabel('Test Scores') - - ax2.set_xlabel(('Percentile Ranking Across ' - '{grade} Grade {gender}s').format( - grade=attach_ordinal(student.grade), - gender=student.gender.title())) - - rect_labels = [] - # Lastly, write in the ranking inside each bar to aid in interpretation - for rect in rects: - # Rectangle widths are already integer-valued but are floating - # type, so it helps to remove the trailing decimal point and 0 by - # converting width to int type - width = int(rect.get_width()) - - rankStr = attach_ordinal(width) - # The bars aren't wide enough to print the ranking inside - if (width < 5): - # Shift the text to the right side of the right edge - xloc = width + 1 - # Black against white background - clr = 'black' - align = 'left' - else: - # Shift the text to the left side of the right edge - xloc = 0.98*width - # White on magenta - clr = 'white' - align = 'right' - - # Center the text vertically in the bar - yloc = rect.get_y() + rect.get_height()/2.0 - label = ax1.text(xloc, yloc, rankStr, horizontalalignment=align, - verticalalignment='center', color=clr, weight='bold', - clip_on=True) - rect_labels.append(label) - - # make the interactive mouse over give the bar title - ax2.fmt_ydata = format_ycursor - # return all of the artists created - return {'fig': fig, - 'ax': ax1, - 'ax_right': ax2, - 'bars': rects, - 'perc_labels': rect_labels, - 'cohort_label': cohort_label} - -student = Student('Johnny Doe', 2, 'boy') -scores = dict(zip(testNames, - (Score(v, p) for v, p in - zip(['7', '48', '12:52', '17', '14'], - np.round(np.random.uniform(0, 1, - len(testNames))*100, 0))))) -cohort_size = 62 # The number of other 2nd grade boys - -arts = plot_student_results(student, scores, cohort_size) diff --git a/examples/pylab_examples/barcode_demo.py b/examples/pylab_examples/barcode_demo.py deleted file mode 100644 index 2e5b480145b3..000000000000 --- a/examples/pylab_examples/barcode_demo.py +++ /dev/null @@ -1,24 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -# the bar -x = np.where(np.random.rand(500) > 0.7, 1.0, 0.0) - -axprops = dict(xticks=[], yticks=[]) -barprops = dict(aspect='auto', cmap=plt.cm.binary, interpolation='nearest') - -fig = plt.figure() - -# a vertical barcode -- this is broken at present -x.shape = len(x), 1 -ax = fig.add_axes([0.1, 0.3, 0.1, 0.6], **axprops) -ax.imshow(x, **barprops) - -x = x.copy() -# a horizontal barcode -x.shape = 1, len(x) -ax = fig.add_axes([0.3, 0.1, 0.6, 0.1], **axprops) -ax.imshow(x, **barprops) - - -plt.show() diff --git a/examples/pylab_examples/boxplot_demo.py b/examples/pylab_examples/boxplot_demo.py deleted file mode 100644 index 288332180e71..000000000000 --- a/examples/pylab_examples/boxplot_demo.py +++ /dev/null @@ -1,52 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -# fake up some data -spread = np.random.rand(50) * 100 -center = np.ones(25) * 50 -flier_high = np.random.rand(10) * 100 + 100 -flier_low = np.random.rand(10) * -100 -data = np.concatenate((spread, center, flier_high, flier_low), 0) - -# basic plot -plt.boxplot(data) - -# notched plot -plt.figure() -plt.boxplot(data, 1) - -# change outlier point symbols -plt.figure() -plt.boxplot(data, 0, 'gD') - -# don't show outlier points -plt.figure() -plt.boxplot(data, 0, '') - -# horizontal boxes -plt.figure() -plt.boxplot(data, 0, 'rs', 0) - -# change whisker length -plt.figure() -plt.boxplot(data, 0, 'rs', 0, 0.75) - -# fake up some more data -spread = np.random.rand(50) * 100 -center = np.ones(25) * 40 -flier_high = np.random.rand(10) * 100 + 100 -flier_low = np.random.rand(10) * -100 -d2 = np.concatenate((spread, center, flier_high, flier_low), 0) -data.shape = (-1, 1) -d2.shape = (-1, 1) -# data = concatenate( (data, d2), 1 ) -# Making a 2-D array only works if all the columns are the -# same length. If they are not, then use a list instead. -# This is actually more efficient because boxplot converts -# a 2-D array into a list of vectors internally anyway. -data = [data, d2, d2[::2, 0]] -# multiple box plots on one figure -plt.figure() -plt.boxplot(data) - -plt.show() diff --git a/examples/pylab_examples/boxplot_demo2.py b/examples/pylab_examples/boxplot_demo2.py deleted file mode 100644 index 06d192b355a9..000000000000 --- a/examples/pylab_examples/boxplot_demo2.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Thanks Josh Hemann for the example -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.patches import Polygon - - -# Generate some data from five different probability distributions, -# each with different characteristics. We want to play with how an IID -# bootstrap resample of the data preserves the distributional -# properties of the original sample, and a boxplot is one visual tool -# to make this assessment -numDists = 5 -randomDists = ['Normal(1,1)', ' Lognormal(1,1)', 'Exp(1)', 'Gumbel(6,4)', - 'Triangular(2,9,11)'] -N = 500 -np.random.seed(0) -norm = np.random.normal(1, 1, N) -logn = np.random.lognormal(1, 1, N) -expo = np.random.exponential(1, N) -gumb = np.random.gumbel(6, 4, N) -tria = np.random.triangular(2, 9, 11, N) - -# Generate some random indices that we'll use to resample the original data -# arrays. For code brevity, just use the same random indices for each array -bootstrapIndices = np.random.random_integers(0, N - 1, N) -normBoot = norm[bootstrapIndices] -expoBoot = expo[bootstrapIndices] -gumbBoot = gumb[bootstrapIndices] -lognBoot = logn[bootstrapIndices] -triaBoot = tria[bootstrapIndices] - -data = [norm, normBoot, logn, lognBoot, expo, expoBoot, gumb, gumbBoot, - tria, triaBoot] - -fig, ax1 = plt.subplots(figsize=(10, 6)) -fig.canvas.set_window_title('A Boxplot Example') -plt.subplots_adjust(left=0.075, right=0.95, top=0.9, bottom=0.25) - -bp = plt.boxplot(data, notch=0, sym='+', vert=1, whis=1.5) -plt.setp(bp['boxes'], color='black') -plt.setp(bp['whiskers'], color='black') -plt.setp(bp['fliers'], color='red', marker='+') - -# Add a horizontal grid to the plot, but make it very light in color -# so we can use it for reading data values but not be distracting -ax1.yaxis.grid(True, linestyle='-', which='major', color='lightgrey', - alpha=0.5) - -# Hide these grid behind plot objects -ax1.set_axisbelow(True) -ax1.set_title('Comparison of IID Bootstrap Resampling Across Five Distributions') -ax1.set_xlabel('Distribution') -ax1.set_ylabel('Value') - -# Now fill the boxes with desired colors -boxColors = ['darkkhaki', 'royalblue'] -numBoxes = numDists*2 -medians = list(range(numBoxes)) -for i in range(numBoxes): - box = bp['boxes'][i] - boxX = [] - boxY = [] - for j in range(5): - boxX.append(box.get_xdata()[j]) - boxY.append(box.get_ydata()[j]) - boxCoords = list(zip(boxX, boxY)) - # Alternate between Dark Khaki and Royal Blue - k = i % 2 - boxPolygon = Polygon(boxCoords, facecolor=boxColors[k]) - ax1.add_patch(boxPolygon) - # Now draw the median lines back over what we just filled in - med = bp['medians'][i] - medianX = [] - medianY = [] - for j in range(2): - medianX.append(med.get_xdata()[j]) - medianY.append(med.get_ydata()[j]) - plt.plot(medianX, medianY, 'k') - medians[i] = medianY[0] - # Finally, overplot the sample averages, with horizontal alignment - # in the center of each box - plt.plot([np.average(med.get_xdata())], [np.average(data[i])], - color='w', marker='*', markeredgecolor='k') - -# Set the axes ranges and axes labels -ax1.set_xlim(0.5, numBoxes + 0.5) -top = 40 -bottom = -5 -ax1.set_ylim(bottom, top) -xtickNames = plt.setp(ax1, xticklabels=np.repeat(randomDists, 2)) -plt.setp(xtickNames, rotation=45, fontsize=8) - -# Due to the Y-axis scale being different across samples, it can be -# hard to compare differences in medians across the samples. Add upper -# X-axis tick labels with the sample medians to aid in comparison -# (just use two decimal places of precision) -pos = np.arange(numBoxes) + 1 -upperLabels = [str(np.round(s, 2)) for s in medians] -weights = ['bold', 'semibold'] -for tick, label in zip(range(numBoxes), ax1.get_xticklabels()): - k = tick % 2 - ax1.text(pos[tick], top - (top*0.05), upperLabels[tick], - horizontalalignment='center', size='x-small', weight=weights[k], - color=boxColors[k]) - -# Finally, add a basic legend -plt.figtext(0.80, 0.08, str(N) + ' Random Numbers', - backgroundcolor=boxColors[0], color='black', weight='roman', - size='x-small') -plt.figtext(0.80, 0.045, 'IID Bootstrap Resample', - backgroundcolor=boxColors[1], - color='white', weight='roman', size='x-small') -plt.figtext(0.80, 0.015, '*', color='white', backgroundcolor='silver', - weight='roman', size='medium') -plt.figtext(0.815, 0.013, ' Average Value', color='black', weight='roman', - size='x-small') - -plt.show() diff --git a/examples/pylab_examples/boxplot_demo3.py b/examples/pylab_examples/boxplot_demo3.py deleted file mode 100644 index ef03bde7256c..000000000000 --- a/examples/pylab_examples/boxplot_demo3.py +++ /dev/null @@ -1,47 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - - -def fakeBootStrapper(n): - ''' - This is just a placeholder for the user's method of - bootstrapping the median and its confidence intervals. - - Returns an arbitrary median and confidence intervals - packed into a tuple - ''' - if n == 1: - med = 0.1 - CI = (-0.25, 0.25) - else: - med = 0.2 - CI = (-0.35, 0.50) - - return med, CI - - -np.random.seed(2) -inc = 0.1 -e1 = np.random.normal(0, 1, size=(500,)) -e2 = np.random.normal(0, 1, size=(500,)) -e3 = np.random.normal(0, 1 + inc, size=(500,)) -e4 = np.random.normal(0, 1 + 2*inc, size=(500,)) - -treatments = [e1, e2, e3, e4] -med1, CI1 = fakeBootStrapper(1) -med2, CI2 = fakeBootStrapper(2) -medians = [None, None, med1, med2] -conf_intervals = [None, None, CI1, CI2] - -fig, ax = plt.subplots() -pos = np.array(range(len(treatments))) + 1 -bp = ax.boxplot(treatments, sym='k+', positions=pos, - notch=1, bootstrap=5000, - usermedians=medians, - conf_intervals=conf_intervals) - -ax.set_xlabel('treatment') -ax.set_ylabel('response') -plt.setp(bp['whiskers'], color='k', linestyle='-') -plt.setp(bp['fliers'], markersize=3.0) -plt.show() diff --git a/examples/pylab_examples/broken_axis.py b/examples/pylab_examples/broken_axis.py deleted file mode 100644 index c62a7fa4f690..000000000000 --- a/examples/pylab_examples/broken_axis.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Broken axis example, where the y-axis will have a portion cut out. -""" -import matplotlib.pyplot as plt -import numpy as np - - -# 30 points between [0, 0.2) originally made using np.random.rand(30)*.2 -pts = np.array([ - 0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, 0.039, 0.161, 0.018, - 0.143, 0.056, 0.125, 0.096, 0.094, 0.051, 0.043, 0.021, 0.138, 0.075, - 0.109, 0.195, 0.050, 0.074, 0.079, 0.155, 0.020, 0.010, 0.061, 0.008]) - -# Now let's make two outlier points which are far away from everything. -pts[[3, 14]] += .8 - -# If we were to simply plot pts, we'd lose most of the interesting -# details due to the outliers. So let's 'break' or 'cut-out' the y-axis -# into two portions - use the top (ax) for the outliers, and the bottom -# (ax2) for the details of the majority of our data -f, (ax, ax2) = plt.subplots(2, 1, sharex=True) - -# plot the same data on both axes -ax.plot(pts) -ax2.plot(pts) - -# zoom-in / limit the view to different portions of the data -ax.set_ylim(.78, 1.) # outliers only -ax2.set_ylim(0, .22) # most of the data - -# hide the spines between ax and ax2 -ax.spines['bottom'].set_visible(False) -ax2.spines['top'].set_visible(False) -ax.xaxis.tick_top() -ax.tick_params(labeltop='off') # don't put tick labels at the top -ax2.xaxis.tick_bottom() - -# This looks pretty good, and was fairly painless, but you can get that -# cut-out diagonal lines look with just a bit more work. The important -# thing to know here is that in axes coordinates, which are always -# between 0-1, spine endpoints are at these locations (0,0), (0,1), -# (1,0), and (1,1). Thus, we just need to put the diagonals in the -# appropriate corners of each of our axes, and so long as we use the -# right transform and disable clipping. - -d = .015 # how big to make the diagonal lines in axes coordinates -# arguments to pass to plot, just so we don't keep repeating them -kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) -ax.plot((-d, +d), (-d, +d), **kwargs) # top-left diagonal -ax.plot((1 - d, 1 + d), (-d, +d), **kwargs) # top-right diagonal - -kwargs.update(transform=ax2.transAxes) # switch to the bottom axes -ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs) # bottom-left diagonal -ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs) # bottom-right diagonal - -# What's cool about this is that now if we vary the distance between -# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(), -# the diagonal lines will move accordingly, and stay right at the tips -# of the spines they are 'breaking' - -plt.show() diff --git a/examples/pylab_examples/broken_barh.py b/examples/pylab_examples/broken_barh.py deleted file mode 100644 index 0c21f234b4ff..000000000000 --- a/examples/pylab_examples/broken_barh.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Make a "broken" horizontal bar plot, i.e., one with gaps -""" -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.broken_barh([(110, 30), (150, 10)], (10, 9), facecolors='blue') -ax.broken_barh([(10, 50), (100, 20), (130, 10)], (20, 9), - facecolors=('red', 'yellow', 'green')) -ax.set_ylim(5, 35) -ax.set_xlim(0, 200) -ax.set_xlabel('seconds since start') -ax.set_yticks([15, 25]) -ax.set_yticklabels(['Bill', 'Jim']) -ax.grid(True) -ax.annotate('race interrupted', (61, 25), - xytext=(0.8, 0.9), textcoords='axes fraction', - arrowprops=dict(facecolor='black', shrink=0.05), - fontsize=16, - horizontalalignment='right', verticalalignment='top') - -plt.show() diff --git a/examples/pylab_examples/centered_ticklabels.py b/examples/pylab_examples/centered_ticklabels.py deleted file mode 100644 index e745acf965bc..000000000000 --- a/examples/pylab_examples/centered_ticklabels.py +++ /dev/null @@ -1,50 +0,0 @@ -# sometimes it is nice to have ticklabels centered. mpl currently -# associates a label with a tick, and the label can be aligned -# 'center', 'left', or 'right' using the horizontal alignment property: -# -# -# for label in ax.xaxis.get_xticklabels(): -# label.set_horizontalalignment('right') -# -# -# but this doesn't help center the label between ticks. One solution -# is to "face it". Use the minor ticks to place a tick centered -# between the major ticks. Here is an example that labels the months, -# centered between the ticks - -import numpy as np -import matplotlib.cbook as cbook -import matplotlib.dates as dates -import matplotlib.ticker as ticker -import matplotlib.pyplot as plt - -# load some financial data; apple's stock price -fh = cbook.get_sample_data('aapl.npy.gz') -try: - # Python3 cannot load python2 .npy files with datetime(object) arrays - # unless the encoding is set to bytes. Hovever this option was - # not added until numpy 1.10 so this example will only work with - # python 2 or with numpy 1.10 and later. - r = np.load(fh, encoding='bytes') -except TypeError: - r = np.load(fh) -fh.close() -r = r[-250:] # get the last 250 days - -fig, ax = plt.subplots() -ax.plot(r.date, r.adj_close) - -ax.xaxis.set_major_locator(dates.MonthLocator()) -ax.xaxis.set_minor_locator(dates.MonthLocator(bymonthday=15)) - -ax.xaxis.set_major_formatter(ticker.NullFormatter()) -ax.xaxis.set_minor_formatter(dates.DateFormatter('%b')) - -for tick in ax.xaxis.get_minor_ticks(): - tick.tick1line.set_markersize(0) - tick.tick2line.set_markersize(0) - tick.label1.set_horizontalalignment('center') - -imid = len(r)//2 -ax.set_xlabel(str(r.date[imid].year)) -plt.show() diff --git a/examples/pylab_examples/cohere_demo.py b/examples/pylab_examples/cohere_demo.py deleted file mode 100644 index 854aeb46877b..000000000000 --- a/examples/pylab_examples/cohere_demo.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Compute the coherence of two signals -""" -import numpy as np -import matplotlib.pyplot as plt - -# make a little extra space between the subplots -plt.subplots_adjust(wspace=0.5) - -dt = 0.01 -t = np.arange(0, 30, dt) -nse1 = np.random.randn(len(t)) # white noise 1 -nse2 = np.random.randn(len(t)) # white noise 2 -r = np.exp(-t/0.05) - -cnse1 = np.convolve(nse1, r, mode='same')*dt # colored noise 1 -cnse2 = np.convolve(nse2, r, mode='same')*dt # colored noise 2 - -# two signals with a coherent part and a random part -s1 = 0.01*np.sin(2*np.pi*10*t) + cnse1 -s2 = 0.01*np.sin(2*np.pi*10*t) + cnse2 - -plt.subplot(211) -plt.plot(t, s1, t, s2) -plt.xlim(0, 5) -plt.xlabel('time') -plt.ylabel('s1 and s2') -plt.grid(True) - -plt.subplot(212) -cxy, f = plt.cohere(s1, s2, 256, 1./dt) -plt.ylabel('coherence') -plt.show() diff --git a/examples/pylab_examples/color_by_yvalue.py b/examples/pylab_examples/color_by_yvalue.py deleted file mode 100644 index 59825a1a9a3c..000000000000 --- a/examples/pylab_examples/color_by_yvalue.py +++ /dev/null @@ -1,17 +0,0 @@ -# use masked arrays to plot a line with different colors by y-value -import numpy as np -import matplotlib.pyplot as plt - -t = np.arange(0.0, 2.0, 0.01) -s = np.sin(2*np.pi*t) - -upper = 0.77 -lower = -0.77 - - -supper = np.ma.masked_where(s < upper, s) -slower = np.ma.masked_where(s > lower, s) -smiddle = np.ma.masked_where(np.logical_or(s < lower, s > upper), s) - -plt.plot(t, smiddle, t, slower, t, supper) -plt.show() diff --git a/examples/pylab_examples/color_demo.py b/examples/pylab_examples/color_demo.py deleted file mode 100755 index 0da064f6cdd1..000000000000 --- a/examples/pylab_examples/color_demo.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -matplotlib gives you 5 ways to specify colors, - - 1) as a single letter string, ala MATLAB - - 2) as an html style hex string or html color name - - 3) as an R,G,B tuple, where R,G,B, range from 0-1 - - 4) as a string representing a floating point number - from 0 to 1, corresponding to shades of gray. - - 5) as a special color "Cn", where n is a number 0-9 specifying the - nth color in the currently active color cycle. - -See help(colors) for more info. -""" -import matplotlib.pyplot as plt -import numpy as np - -plt.subplot(111, facecolor='darkslategray') -#subplot(111, facecolor='#ababab') -t = np.arange(0.0, 2.0, 0.01) -s = np.sin(2*np.pi*t) -plt.plot(t, s, 'C1') -plt.xlabel('time (s)', color='C1') -plt.ylabel('voltage (mV)', color='0.5') # grayscale color -plt.title('About as silly as it gets, folks', color='#afeeee') -plt.show() diff --git a/examples/pylab_examples/colorbar_tick_labelling_demo.py b/examples/pylab_examples/colorbar_tick_labelling_demo.py deleted file mode 100644 index ff4d9c906655..000000000000 --- a/examples/pylab_examples/colorbar_tick_labelling_demo.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Produce custom labelling for a colorbar. - -Contributed by Scott Sinclair -""" - -import matplotlib.pyplot as plt -import numpy as np -from matplotlib import cm -from numpy.random import randn - -# Make plot with vertical (default) colorbar -fig, ax = plt.subplots() - -data = np.clip(randn(250, 250), -1, 1) - -cax = ax.imshow(data, interpolation='nearest', cmap=cm.coolwarm) -ax.set_title('Gaussian noise with vertical colorbar') - -# Add colorbar, make sure to specify tick locations to match desired ticklabels -cbar = fig.colorbar(cax, ticks=[-1, 0, 1]) -cbar.ax.set_yticklabels(['< -1', '0', '> 1']) # vertically oriented colorbar - -# Make plot with horizontal colorbar -fig, ax = plt.subplots() - -data = np.clip(randn(250, 250), -1, 1) - -cax = ax.imshow(data, interpolation='nearest', cmap=cm.afmhot) -ax.set_title('Gaussian noise with horizontal colorbar') - -cbar = fig.colorbar(cax, ticks=[-1, 0, 1], orientation='horizontal') -cbar.ax.set_xticklabels(['Low', 'Medium', 'High']) # horizontal colorbar - -plt.show() diff --git a/examples/pylab_examples/colours.py b/examples/pylab_examples/colours.py deleted file mode 100644 index adcbd4331317..000000000000 --- a/examples/pylab_examples/colours.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- noplot -*- -""" -Some simple functions to generate colours. -""" -import numpy as np -from matplotlib import colors as mcolors - - -def pastel(colour, weight=2.4): - """ Convert colour into a nice pastel shade""" - rgb = np.asarray(mcolors.to_rgba(colour)[:3]) - # scale colour - maxc = max(rgb) - if maxc < 1.0 and maxc > 0: - # scale colour - scale = 1.0 / maxc - rgb = rgb * scale - # now decrease saturation - total = rgb.sum() - slack = 0 - for x in rgb: - slack += 1.0 - x - - # want to increase weight from total to weight - # pick x s.t. slack * x == weight - total - # x = (weight - total) / slack - x = (weight - total) / slack - - rgb = [c + (x * (1.0 - c)) for c in rgb] - - return rgb - - -def get_colours(n): - """ Return n pastel colours. """ - base = np.asarray([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) - - if n <= 3: - return base[0:n] - - # how many new colours to we need to insert between - # red and green and between green and blue? - needed = (((n - 3) + 1) / 2, (n - 3) / 2) - - colours = [] - for start in (0, 1): - for x in np.linspace(0, 1, needed[start] + 2): - colours.append((base[start] * (1.0 - x)) + - (base[start + 1] * x)) - - return [pastel(c) for c in colours[0:n]] diff --git a/examples/pylab_examples/contour_corner_mask.py b/examples/pylab_examples/contour_corner_mask.py deleted file mode 100644 index 7be99f7182ee..000000000000 --- a/examples/pylab_examples/contour_corner_mask.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Illustrate the difference between corner_mask=False and corner_mask=True -for masked contour plots. -""" -import matplotlib.pyplot as plt -import numpy as np - -# Data to plot. -x, y = np.meshgrid(np.arange(7), np.arange(10)) -z = np.sin(0.5*x)*np.cos(0.52*y) - -# Mask various z values. -mask = np.zeros_like(z, dtype=np.bool) -mask[2, 3:5] = True -mask[3:5, 4] = True -mask[7, 2] = True -mask[5, 0] = True -mask[0, 6] = True -z = np.ma.array(z, mask=mask) - -corner_masks = [False, True] -for i, corner_mask in enumerate(corner_masks): - plt.subplot(1, 2, i+1) - cs = plt.contourf(x, y, z, corner_mask=corner_mask) - plt.contour(cs, colors='k') - plt.title('corner_mask = {0}'.format(corner_mask)) - - # Plot grid. - plt.grid(c='k', ls='-', alpha=0.3) - - # Indicate masked points with red circles. - plt.plot(np.ma.array(x, mask=~mask), y, 'ro') - -plt.show() diff --git a/examples/pylab_examples/contour_demo.py b/examples/pylab_examples/contour_demo.py deleted file mode 100755 index b3555b1de1eb..000000000000 --- a/examples/pylab_examples/contour_demo.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Illustrate simple contour plotting, contours on an image with -a colorbar for the contours, and labelled contours. - -See also contour_image.py. -""" -import matplotlib -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt - -matplotlib.rcParams['xtick.direction'] = 'out' -matplotlib.rcParams['ytick.direction'] = 'out' - -delta = 0.025 -x = np.arange(-3.0, 3.0, delta) -y = np.arange(-2.0, 2.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -# difference of Gaussians -Z = 10.0 * (Z2 - Z1) - - -# Create a simple contour plot with labels using default colors. The -# inline argument to clabel will control whether the labels are draw -# over the line segments of the contour, removing the lines beneath -# the label -plt.figure() -CS = plt.contour(X, Y, Z) -plt.clabel(CS, inline=1, fontsize=10) -plt.title('Simplest default with labels') - - -# contour labels can be placed manually by providing list of positions -# (in data coordinate). See ginput_manual_clabel.py for interactive -# placement. -plt.figure() -CS = plt.contour(X, Y, Z) -manual_locations = [(-1, -1.4), (-0.62, -0.7), (-2, 0.5), (1.7, 1.2), (2.0, 1.4), (2.4, 1.7)] -plt.clabel(CS, inline=1, fontsize=10, manual=manual_locations) -plt.title('labels at selected locations') - - -# You can force all the contours to be the same color. -plt.figure() -CS = plt.contour(X, Y, Z, 6, - colors='k', # negative contours will be dashed by default - ) -plt.clabel(CS, fontsize=9, inline=1) -plt.title('Single color - negative contours dashed') - -# You can set negative contours to be solid instead of dashed: -matplotlib.rcParams['contour.negative_linestyle'] = 'solid' -plt.figure() -CS = plt.contour(X, Y, Z, 6, - colors='k', # negative contours will be dashed by default - ) -plt.clabel(CS, fontsize=9, inline=1) -plt.title('Single color - negative contours solid') - - -# And you can manually specify the colors of the contour -plt.figure() -CS = plt.contour(X, Y, Z, 6, - linewidths=np.arange(.5, 4, .5), - colors=('r', 'green', 'blue', (1, 1, 0), '#afeeee', '0.5') - ) -plt.clabel(CS, fontsize=9, inline=1) -plt.title('Crazy lines') - - -# Or you can use a colormap to specify the colors; the default -# colormap will be used for the contour lines -plt.figure() -im = plt.imshow(Z, interpolation='bilinear', origin='lower', - cmap=cm.gray, extent=(-3, 3, -2, 2)) -levels = np.arange(-1.2, 1.6, 0.2) -CS = plt.contour(Z, levels, - origin='lower', - linewidths=2, - extent=(-3, 3, -2, 2)) - -# Thicken the zero contour. -zc = CS.collections[6] -plt.setp(zc, linewidth=4) - -plt.clabel(CS, levels[1::2], # label every second level - inline=1, - fmt='%1.1f', - fontsize=14) - -# make a colorbar for the contour lines -CB = plt.colorbar(CS, shrink=0.8, extend='both') - -plt.title('Lines with colorbar') -#plt.hot() # Now change the colormap for the contour lines and colorbar -plt.flag() - -# We can still add a colorbar for the image, too. -CBI = plt.colorbar(im, orientation='horizontal', shrink=0.8) - -# This makes the original colorbar look a bit out of place, -# so let's improve its position. - -l, b, w, h = plt.gca().get_position().bounds -ll, bb, ww, hh = CB.ax.get_position().bounds -CB.ax.set_position([ll, b + 0.1*h, ww, h*0.8]) - - -plt.show() diff --git a/examples/pylab_examples/contour_image.py b/examples/pylab_examples/contour_image.py deleted file mode 100755 index 71b853c154cd..000000000000 --- a/examples/pylab_examples/contour_image.py +++ /dev/null @@ -1,106 +0,0 @@ -''' -Test combinations of contouring, filled contouring, and image plotting. -For contour labelling, see contour_demo.py. - -The emphasis in this demo is on showing how to make contours register -correctly on images, and on how to get both of them oriented as -desired. In particular, note the usage of the "origin" and "extent" -keyword arguments to imshow and contour. -''' -import matplotlib.pyplot as plt -import numpy as np -from matplotlib import mlab, cm - -# Default delta is large because that makes it fast, and it illustrates -# the correct registration between image and contours. -delta = 0.5 - -extent = (-3, 4, -4, 3) - -x = np.arange(-3.0, 4.001, delta) -y = np.arange(-4.0, 3.001, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = (Z1 - Z2) * 10 - -levels = np.arange(-2.0, 1.601, 0.4) # Boost the upper limit to avoid truncation errors. - -norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max()) -cmap = cm.PRGn - -plt.figure() - - -plt.subplot(2, 2, 1) - -cset1 = plt.contourf(X, Y, Z, levels, - cmap=cm.get_cmap(cmap, len(levels) - 1), - norm=norm, - ) -# It is not necessary, but for the colormap, we need only the -# number of levels minus 1. To avoid discretization error, use -# either this number or a large number such as the default (256). - -# If we want lines as well as filled regions, we need to call -# contour separately; don't try to change the edgecolor or edgewidth -# of the polygons in the collections returned by contourf. -# Use levels output from previous call to guarantee they are the same. -cset2 = plt.contour(X, Y, Z, cset1.levels, - colors='k', - hold='on') -# We don't really need dashed contour lines to indicate negative -# regions, so let's turn them off. -for c in cset2.collections: - c.set_linestyle('solid') - -# It is easier here to make a separate call to contour than -# to set up an array of colors and linewidths. -# We are making a thick green line as a zero contour. -# Specify the zero level as a tuple with only 0 in it. -cset3 = plt.contour(X, Y, Z, (0,), - colors='g', - linewidths=2, - hold='on') -plt.title('Filled contours') -plt.colorbar(cset1) -#hot() - - -plt.subplot(2, 2, 2) - -plt.imshow(Z, extent=extent, cmap=cmap, norm=norm) -v = plt.axis() -plt.contour(Z, levels, hold='on', colors='k', - origin='upper', extent=extent) -plt.axis(v) -plt.title("Image, origin 'upper'") - -plt.subplot(2, 2, 3) - -plt.imshow(Z, origin='lower', extent=extent, cmap=cmap, norm=norm) -v = plt.axis() -plt.contour(Z, levels, hold='on', colors='k', - origin='lower', extent=extent) -plt.axis(v) -plt.title("Image, origin 'lower'") - -plt.subplot(2, 2, 4) - -# We will use the interpolation "nearest" here to show the actual -# image pixels. -# Note that the contour lines don't extend to the edge of the box. -# This is intentional. The Z values are defined at the center of each -# image pixel (each color block on the following subplot), so the -# domain that is contoured does not extend beyond these pixel centers. -im = plt.imshow(Z, interpolation='nearest', extent=extent, cmap=cmap, norm=norm) -v = plt.axis() -plt.contour(Z, levels, hold='on', colors='k', - origin='image', extent=extent) -plt.axis(v) -ylim = plt.get(plt.gca(), 'ylim') -plt.setp(plt.gca(), ylim=ylim[::-1]) -plt.title("Image, origin from rc, reversed y-axis") -plt.colorbar(im) - -plt.show() diff --git a/examples/pylab_examples/contour_label_demo.py b/examples/pylab_examples/contour_label_demo.py deleted file mode 100644 index fe0cf87cb563..000000000000 --- a/examples/pylab_examples/contour_label_demo.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Illustrate some of the more advanced things that one can do with -contour labels. - -See also contour_demo.py. -""" -import matplotlib -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.ticker as ticker -import matplotlib.pyplot as plt - -matplotlib.rcParams['xtick.direction'] = 'out' -matplotlib.rcParams['ytick.direction'] = 'out' - -################################################## -# Define our surface -################################################## -delta = 0.025 -x = np.arange(-3.0, 3.0, delta) -y = np.arange(-2.0, 2.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -# difference of Gaussians -Z = 10.0 * (Z2 - Z1) - -################################################## -# Make contour labels using creative float classes -# Follows suggestion of Manuel Metz -################################################## -plt.figure() - -# Basic contour plot -CS = plt.contour(X, Y, Z) - - -# Define a class that forces representation of float to look a certain way -# This remove trailing zero so '1.0' becomes '1' -class nf(float): - def __repr__(self): - str = '%.1f' % (self.__float__(),) - if str[-1] == '0': - return '%.0f' % self.__float__() - else: - return '%.1f' % self.__float__() - -# Recast levels to new class -CS.levels = [nf(val) for val in CS.levels] - -# Label levels with specially formatted floats -if plt.rcParams["text.usetex"]: - fmt = r'%r \%%' -else: - fmt = '%r %%' -plt.clabel(CS, CS.levels, inline=True, fmt=fmt, fontsize=10) - -################################################## -# Label contours with arbitrary strings using a -# dictionary -################################################## -plt.figure() - -# Basic contour plot -CS = plt.contour(X, Y, Z) - -fmt = {} -strs = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh'] -for l, s in zip(CS.levels, strs): - fmt[l] = s - -# Label every other level using strings -plt.clabel(CS, CS.levels[::2], inline=True, fmt=fmt, fontsize=10) - -# Use a Formatter - -plt.figure() - -CS = plt.contour(X, Y, 100**Z, locator=plt.LogLocator()) -fmt = ticker.LogFormatterMathtext() -fmt.create_dummy_axis() -plt.clabel(CS, CS.levels, fmt=fmt) -plt.title("$100^Z$") - -plt.show() diff --git a/examples/pylab_examples/contourf_demo.py b/examples/pylab_examples/contourf_demo.py deleted file mode 100755 index 21448f99d7d6..000000000000 --- a/examples/pylab_examples/contourf_demo.py +++ /dev/null @@ -1,106 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -origin = 'lower' -#origin = 'upper' - -delta = 0.025 - -x = y = np.arange(-3.0, 3.01, delta) -X, Y = np.meshgrid(x, y) -Z1 = plt.mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = plt.mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = 10 * (Z1 - Z2) - -nr, nc = Z.shape - -# put NaNs in one corner: -Z[-nr//6:, -nc//6:] = np.nan -# contourf will convert these to masked - - -Z = np.ma.array(Z) -# mask another corner: -Z[:nr//6, :nc//6] = np.ma.masked - -# mask a circle in the middle: -interior = np.sqrt((X**2) + (Y**2)) < 0.5 -Z[interior] = np.ma.masked - - -# We are using automatic selection of contour levels; -# this is usually not such a good idea, because they don't -# occur on nice boundaries, but we do it here for purposes -# of illustration. -CS = plt.contourf(X, Y, Z, 10, - #[-1, -0.1, 0, 0.1], - #alpha=0.5, - cmap=plt.cm.bone, - origin=origin) - -# Note that in the following, we explicitly pass in a subset of -# the contour levels used for the filled contours. Alternatively, -# We could pass in additional levels to provide extra resolution, -# or leave out the levels kwarg to use all of the original levels. - -CS2 = plt.contour(CS, levels=CS.levels[::2], - colors='r', - origin=origin, - hold='on') - -plt.title('Nonsense (3 masked regions)') -plt.xlabel('word length anomaly') -plt.ylabel('sentence length anomaly') - -# Make a colorbar for the ContourSet returned by the contourf call. -cbar = plt.colorbar(CS) -cbar.ax.set_ylabel('verbosity coefficient') -# Add the contour line levels to the colorbar -cbar.add_lines(CS2) - -plt.figure() - -# Now make a contour plot with the levels specified, -# and with the colormap generated automatically from a list -# of colors. -levels = [-1.5, -1, -0.5, 0, 0.5, 1] -CS3 = plt.contourf(X, Y, Z, levels, - colors=('r', 'g', 'b'), - origin=origin, - extend='both') -# Our data range extends outside the range of levels; make -# data below the lowest contour level yellow, and above the -# highest level cyan: -CS3.cmap.set_under('yellow') -CS3.cmap.set_over('cyan') - -CS4 = plt.contour(X, Y, Z, levels, - colors=('k',), - linewidths=(3,), - origin=origin) -plt.title('Listed colors (3 masked regions)') -plt.clabel(CS4, fmt='%2.1f', colors='w', fontsize=14) - -# Notice that the colorbar command gets all the information it -# needs from the ContourSet object, CS3. -plt.colorbar(CS3) - -# Illustrate all 4 possible "extend" settings: -extends = ["neither", "both", "min", "max"] -cmap = plt.cm.get_cmap("winter") -cmap.set_under("magenta") -cmap.set_over("yellow") -# Note: contouring simply excludes masked or nan regions, so -# instead of using the "bad" colormap value for them, it draws -# nothing at all in them. Therefore the following would have -# no effect: -# cmap.set_bad("red") - -fig, axs = plt.subplots(2, 2) -for ax, extend in zip(axs.ravel(), extends): - cs = ax.contourf(X, Y, Z, levels, cmap=cmap, extend=extend, origin=origin) - fig.colorbar(cs, ax=ax, shrink=0.9) - ax.set_title("extend = %s" % extend) - ax.locator_params(nbins=4) - -plt.show() diff --git a/examples/pylab_examples/contourf_hatching.py b/examples/pylab_examples/contourf_hatching.py deleted file mode 100755 index b1395ec5f3b1..000000000000 --- a/examples/pylab_examples/contourf_hatching.py +++ /dev/null @@ -1,44 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - - -# invent some numbers, turning the x and y arrays into simple -# 2d arrays, which make combining them together easier. -x = np.linspace(-3, 5, 150).reshape(1, -1) -y = np.linspace(-3, 5, 120).reshape(-1, 1) -z = np.cos(x) + np.sin(y) - -# we no longer need x and y to be 2 dimensional, so flatten them. -x, y = x.flatten(), y.flatten() - - -# --------------------------------------------- -# | Plot #1 | -# --------------------------------------------- -# the simplest hatched plot with a colorbar -fig = plt.figure() -cs = plt.contourf(x, y, z, hatches=['-', '/', '\\', '//'], - cmap=plt.get_cmap('gray'), - extend='both', alpha=0.5 - ) -plt.colorbar() - - -# --------------------------------------------- -# | Plot #2 | -# --------------------------------------------- -# a plot of hatches without color with a legend -plt.figure() -n_levels = 6 -plt.contour(x, y, z, n_levels, colors='black', linestyles='-') -cs = plt.contourf(x, y, z, n_levels, colors='none', - hatches=['.', '/', '\\', None, '\\\\', '*'], - extend='lower' - ) - -# create a legend for the contour set -artists, labels = cs.legend_elements() -plt.legend(artists, labels, handleheight=2) - - -plt.show() diff --git a/examples/pylab_examples/coords_demo.py b/examples/pylab_examples/coords_demo.py deleted file mode 100755 index 7945e87b21cd..000000000000 --- a/examples/pylab_examples/coords_demo.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -An example of how to interact with the plotting canvas by connecting -to move and click events -""" -from __future__ import print_function -import sys -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 1.0, 0.01) -s = np.sin(2*np.pi*t) -fig, ax = plt.subplots() -ax.plot(t, s) - - -def on_move(event): - # get the x and y pixel coords - x, y = event.x, event.y - - if event.inaxes: - ax = event.inaxes # the axes instance - print('data coords %f %f' % (event.xdata, event.ydata)) - - -def on_click(event): - # get the x and y coords, flip y from top to bottom - x, y = event.x, event.y - if event.button == 1: - if event.inaxes is not None: - print('data coords %f %f' % (event.xdata, event.ydata)) - -binding_id = plt.connect('motion_notify_event', on_move) -plt.connect('button_press_event', on_click) - -if "test_disconnect" in sys.argv: - print("disconnecting console coordinate printout...") - plt.disconnect(binding_id) - -plt.show() diff --git a/examples/pylab_examples/coords_report.py b/examples/pylab_examples/coords_report.py deleted file mode 100644 index cdc0d445a43e..000000000000 --- a/examples/pylab_examples/coords_report.py +++ /dev/null @@ -1,17 +0,0 @@ -# override the default reporting of coords - -import matplotlib.pyplot as plt -import numpy as np - - -def millions(x): - return '$%1.1fM' % (x*1e-6) - -x = np.random.rand(20) -y = 1e7*np.random.rand(20) - -fig, ax = plt.subplots() -ax.fmt_ydata = millions -plt.plot(x, y, 'o') - -plt.show() diff --git a/examples/pylab_examples/csd_demo.py b/examples/pylab_examples/csd_demo.py deleted file mode 100644 index 3e2fe072eec3..000000000000 --- a/examples/pylab_examples/csd_demo.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Compute the cross spectral density of two signals -""" -import numpy as np -import matplotlib.pyplot as plt - - -fig, (ax1, ax2) = plt.subplots(2, 1) -# make a little extra space between the subplots -fig.subplots_adjust(hspace=0.5) - -dt = 0.01 -t = np.arange(0, 30, dt) -nse1 = np.random.randn(len(t)) # white noise 1 -nse2 = np.random.randn(len(t)) # white noise 2 -r = np.exp(-t/0.05) - -cnse1 = np.convolve(nse1, r, mode='same')*dt # colored noise 1 -cnse2 = np.convolve(nse2, r, mode='same')*dt # colored noise 2 - -# two signals with a coherent part and a random part -s1 = 0.01*np.sin(2*np.pi*10*t) + cnse1 -s2 = 0.01*np.sin(2*np.pi*10*t) + cnse2 - -ax1.plot(t, s1, t, s2) -ax1.set_xlim(0, 5) -ax1.set_xlabel('time') -ax1.set_ylabel('s1 and s2') -ax1.grid(True) - -cxy, f = ax2.csd(s1, s2, 256, 1./dt) -ax2.set_ylabel('CSD (db)') -plt.show() diff --git a/examples/pylab_examples/cursor_demo.py b/examples/pylab_examples/cursor_demo.py deleted file mode 100755 index 57d28f8e1236..000000000000 --- a/examples/pylab_examples/cursor_demo.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- noplot -*- - -""" -This example shows how to use matplotlib to provide a data cursor. It -uses matplotlib to draw the cursor and may be a slow since this -requires redrawing the figure with every mouse move. - -Faster cursoring is possible using native GUI drawing, as in -wxcursor_demo.py. - -The mpldatacursor and mplcursors third-party packages can be used to achieve a -similar effect. See - https://github.com/joferkington/mpldatacursor - https://github.com/anntzer/mplcursors -""" -from __future__ import print_function -import matplotlib.pyplot as plt -import numpy as np - - -class Cursor(object): - def __init__(self, ax): - self.ax = ax - self.lx = ax.axhline(color='k') # the horiz line - self.ly = ax.axvline(color='k') # the vert line - - # text location in axes coords - self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes) - - def mouse_move(self, event): - if not event.inaxes: - return - - x, y = event.xdata, event.ydata - # update the line positions - self.lx.set_ydata(y) - self.ly.set_xdata(x) - - self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y)) - plt.draw() - - -class SnaptoCursor(object): - """ - Like Cursor but the crosshair snaps to the nearest x,y point - For simplicity, I'm assuming x is sorted - """ - - def __init__(self, ax, x, y): - self.ax = ax - self.lx = ax.axhline(color='k') # the horiz line - self.ly = ax.axvline(color='k') # the vert line - self.x = x - self.y = y - # text location in axes coords - self.txt = ax.text(0.7, 0.9, '', transform=ax.transAxes) - - def mouse_move(self, event): - - if not event.inaxes: - return - - x, y = event.xdata, event.ydata - - indx = min(np.searchsorted(self.x, [x])[0], len(self.x) - 1) - x = self.x[indx] - y = self.y[indx] - # update the line positions - self.lx.set_ydata(y) - self.ly.set_xdata(x) - - self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y)) - print('x=%1.2f, y=%1.2f' % (x, y)) - plt.draw() - -t = np.arange(0.0, 1.0, 0.01) -s = np.sin(2*2*np.pi*t) -fig, ax = plt.subplots() - -#cursor = Cursor(ax) -cursor = SnaptoCursor(ax, t, s) -plt.connect('motion_notify_event', cursor.mouse_move) - -ax.plot(t, s, 'o') -plt.axis([0, 1, -1, 1]) -plt.show() diff --git a/examples/pylab_examples/custom_cmap.py b/examples/pylab_examples/custom_cmap.py deleted file mode 100644 index b3e44843be79..000000000000 --- a/examples/pylab_examples/custom_cmap.py +++ /dev/null @@ -1,215 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.colors import LinearSegmentedColormap - -""" -Creating a colormap from a list of colors ------------------------------------------ -Creating a colormap from a list of colors can be done with the `from_list` -method of `LinearSegmentedColormap`. You must pass a list of RGB tuples that -define the mixture of colors from 0 to 1. - - -Creating custom colormaps -------------------------- -It is also possible to create a custom mapping for a colormap. This is -accomplished by creating dictionary that specifies how the RGB channels -change from one end of the cmap to the other. - -Example: suppose you want red to increase from 0 to 1 over the bottom -half, green to do the same over the middle half, and blue over the top -half. Then you would use: - -cdict = {'red': ((0.0, 0.0, 0.0), - (0.5, 1.0, 1.0), - (1.0, 1.0, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.75, 1.0, 1.0), - (1.0, 1.0, 1.0)), - - 'blue': ((0.0, 0.0, 0.0), - (0.5, 0.0, 0.0), - (1.0, 1.0, 1.0))} - -If, as in this example, there are no discontinuities in the r, g, and b -components, then it is quite simple: the second and third element of -each tuple, above, is the same--call it "y". The first element ("x") -defines interpolation intervals over the full range of 0 to 1, and it -must span that whole range. In other words, the values of x divide the -0-to-1 range into a set of segments, and y gives the end-point color -values for each segment. - -Now consider the green. cdict['green'] is saying that for -0 <= x <= 0.25, y is zero; no green. -0.25 < x <= 0.75, y varies linearly from 0 to 1. -x > 0.75, y remains at 1, full green. - -If there are discontinuities, then it is a little more complicated. -Label the 3 elements in each row in the cdict entry for a given color as -(x, y0, y1). Then for values of x between x[i] and x[i+1] the color -value is interpolated between y1[i] and y0[i+1]. - -Going back to the cookbook example, look at cdict['red']; because y0 != -y1, it is saying that for x from 0 to 0.5, red increases from 0 to 1, -but then it jumps down, so that for x from 0.5 to 1, red increases from -0.7 to 1. Green ramps from 0 to 1 as x goes from 0 to 0.5, then jumps -back to 0, and ramps back to 1 as x goes from 0.5 to 1. - -row i: x y0 y1 - / - / -row i+1: x y0 y1 - -Above is an attempt to show that for x in the range x[i] to x[i+1], the -interpolation is between y1[i] and y0[i+1]. So, y0[0] and y1[-1] are -never used. - -""" -# Make some illustrative fake data: - -x = np.arange(0, np.pi, 0.1) -y = np.arange(0, 2*np.pi, 0.1) -X, Y = np.meshgrid(x, y) -Z = np.cos(X) * np.sin(Y) * 10 - - -# --- Colormaps from a list --- - -colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] # R -> G -> B -n_bins = [3, 6, 10, 100] # Discretizes the interpolation into bins -cmap_name = 'my_list' -fig, axs = plt.subplots(2, 2, figsize=(6, 9)) -fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05) -for n_bin, ax in zip(n_bins, axs.ravel()): - # Create the colormap - cm = LinearSegmentedColormap.from_list( - cmap_name, colors, N=n_bin) - # Fewer bins will result in "coarser" colomap interpolation - im = ax.imshow(Z, interpolation='nearest', origin='lower', cmap=cm) - ax.set_title("N bins: %s" % n_bin) - fig.colorbar(im, ax=ax) - - -# --- Custom colormaps --- - -cdict1 = {'red': ((0.0, 0.0, 0.0), - (0.5, 0.0, 0.1), - (1.0, 1.0, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 1.0), - (0.5, 0.1, 0.0), - (1.0, 0.0, 0.0)) - } - -cdict2 = {'red': ((0.0, 0.0, 0.0), - (0.5, 0.0, 1.0), - (1.0, 0.1, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 0.1), - (0.5, 1.0, 0.0), - (1.0, 0.0, 0.0)) - } - -cdict3 = {'red': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.5, 0.8, 1.0), - (0.75, 1.0, 1.0), - (1.0, 0.4, 1.0)), - - 'green': ((0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.5, 0.9, 0.9), - (0.75, 0.0, 0.0), - (1.0, 0.0, 0.0)), - - 'blue': ((0.0, 0.0, 0.4), - (0.25, 1.0, 1.0), - (0.5, 1.0, 0.8), - (0.75, 0.0, 0.0), - (1.0, 0.0, 0.0)) - } - -# Make a modified version of cdict3 with some transparency -# in the middle of the range. -cdict4 = cdict3.copy() -cdict4['alpha'] = ((0.0, 1.0, 1.0), - # (0.25,1.0, 1.0), - (0.5, 0.3, 0.3), - # (0.75,1.0, 1.0), - (1.0, 1.0, 1.0)) - - -# Now we will use this example to illustrate 3 ways of -# handling custom colormaps. -# First, the most direct and explicit: - -blue_red1 = LinearSegmentedColormap('BlueRed1', cdict1) - -# Second, create the map explicitly and register it. -# Like the first method, this method works with any kind -# of Colormap, not just -# a LinearSegmentedColormap: - -blue_red2 = LinearSegmentedColormap('BlueRed2', cdict2) -plt.register_cmap(cmap=blue_red2) - -# Third, for LinearSegmentedColormap only, -# leave everything to register_cmap: - -plt.register_cmap(name='BlueRed3', data=cdict3) # optional lut kwarg -plt.register_cmap(name='BlueRedAlpha', data=cdict4) - -# Make the figure: - -fig, axs = plt.subplots(2, 2, figsize=(6, 9)) -fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05) - -# Make 4 subplots: - -im1 = axs[0, 0].imshow(Z, interpolation='nearest', cmap=blue_red1) -fig.colorbar(im1, ax=axs[0, 0]) - -cmap = plt.get_cmap('BlueRed2') -im2 = axs[1, 0].imshow(Z, interpolation='nearest', cmap=cmap) -fig.colorbar(im2, ax=axs[1, 0]) - -# Now we will set the third cmap as the default. One would -# not normally do this in the middle of a script like this; -# it is done here just to illustrate the method. - -plt.rcParams['image.cmap'] = 'BlueRed3' - -im3 = axs[0, 1].imshow(Z, interpolation='nearest') -fig.colorbar(im3, ax=axs[0, 1]) -axs[0, 1].set_title("Alpha = 1") - -# Or as yet another variation, we can replace the rcParams -# specification *before* the imshow with the following *after* -# imshow. -# This sets the new default *and* sets the colormap of the last -# image-like item plotted via pyplot, if any. -# - -# Draw a line with low zorder so it will be behind the image. -axs[1, 1].plot([0, 10*np.pi], [0, 20*np.pi], color='c', lw=20, zorder=-1) - -im4 = axs[1, 1].imshow(Z, interpolation='nearest') -fig.colorbar(im4, ax=axs[1, 1]) - -# Here it is: changing the colormap for the current image and its -# colorbar after they have been plotted. -im4.set_cmap('BlueRedAlpha') -axs[1, 1].set_title("Varying alpha") -# - -fig.suptitle('Custom Blue-Red colormaps', fontsize=16) - -plt.show() diff --git a/examples/pylab_examples/custom_figure_class.py b/examples/pylab_examples/custom_figure_class.py deleted file mode 100644 index 56c4de49923d..000000000000 --- a/examples/pylab_examples/custom_figure_class.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -You can pass a custom Figure constructor to figure if you want to derive from the default Figure. This simple example creates a figure with a figure title -""" -from matplotlib.pyplot import figure, show -from matplotlib.figure import Figure - - -class MyFigure(Figure): - def __init__(self, *args, **kwargs): - """ - custom kwarg figtitle is a figure title - """ - figtitle = kwargs.pop('figtitle', 'hi mom') - Figure.__init__(self, *args, **kwargs) - self.text(0.5, 0.95, figtitle, ha='center') - -fig = figure(FigureClass=MyFigure, figtitle='my title') -ax = fig.add_subplot(111) -ax.plot([1, 2, 3]) - -show() diff --git a/examples/pylab_examples/custom_ticker1.py b/examples/pylab_examples/custom_ticker1.py deleted file mode 100755 index dfd0fc847ff7..000000000000 --- a/examples/pylab_examples/custom_ticker1.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -The new ticker code was designed to explicitly support user customized -ticking. The documentation -http://matplotlib.org/matplotlib.ticker.html details this -process. That code defines a lot of preset tickers but was primarily -designed to be user extensible. - -In this example a user defined function is used to format the ticks in -millions of dollars on the y axis -""" -from matplotlib.ticker import FuncFormatter -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(4) -money = [1.5e5, 2.5e6, 5.5e6, 2.0e7] - - -def millions(x, pos): - 'The two args are the value and tick position' - return '$%1.1fM' % (x*1e-6) - -formatter = FuncFormatter(millions) - -fig, ax = plt.subplots() -ax.yaxis.set_major_formatter(formatter) -plt.bar(x, money) -plt.xticks(x + 0.5, ('Bill', 'Fred', 'Mary', 'Sue')) -plt.show() diff --git a/examples/pylab_examples/customize_rc.py b/examples/pylab_examples/customize_rc.py deleted file mode 100644 index 8a4531e6dfa1..000000000000 --- a/examples/pylab_examples/customize_rc.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -I'm not trying to make a good looking figure here, but just to show -some examples of customizing rc params on the fly - -If you like to work interactively, and need to create different sets -of defaults for figures (e.g., one set of defaults for publication, one -set for interactive exploration), you may want to define some -functions in a custom module that set the defaults, e.g., - -def set_pub(): - rc('font', weight='bold') # bold fonts are easier to see - rc('tick', labelsize=15) # tick labels bigger - rc('lines', lw=1, color='k') # thicker black lines (no budget for color!) - rc('grid', c='0.5', ls='-', lw=0.5) # solid gray grid lines - rc('savefig', dpi=300) # higher res outputs - - - -Then as you are working interactively, you just need to do - ->>> set_pub() ->>> subplot(111) ->>> plot([1,2,3]) ->>> savefig('myfig') ->>> rcdefaults() # restore the defaults - -""" -import matplotlib.pyplot as plt - -plt.subplot(311) -plt.plot([1, 2, 3]) - -# the axes attributes need to be set before the call to subplot -plt.rc('font', weight='bold') -plt.rc('xtick.major', size=5, pad=7) -plt.rc('xtick', labelsize=15) - -# using aliases for color, linestyle and linewidth; gray, solid, thick -plt.rc('grid', c='0.5', ls='-', lw=5) -plt.rc('lines', lw=2, color='g') -plt.subplot(312) - -plt.plot([1, 2, 3]) -plt.grid(True) - -plt.rcdefaults() -plt.subplot(313) -plt.plot([1, 2, 3]) -plt.grid(True) -plt.show() diff --git a/examples/pylab_examples/dashpointlabel.py b/examples/pylab_examples/dashpointlabel.py deleted file mode 100644 index 9e8e03bedbbe..000000000000 --- a/examples/pylab_examples/dashpointlabel.py +++ /dev/null @@ -1,37 +0,0 @@ -import matplotlib.pyplot as plt - -DATA = ((1, 3), - (2, 4), - (3, 1), - (4, 2)) -# dash_style = -# direction, length, (text)rotation, dashrotation, push -# (The parameters are varied to show their effects, -# not for visual appeal). -dash_style = ( - (0, 20, -15, 30, 10), - (1, 30, 0, 15, 10), - (0, 40, 15, 15, 10), - (1, 20, 30, 60, 10), - ) - -fig, ax = plt.subplots() - -(x, y) = zip(*DATA) -ax.plot(x, y, marker='o') -for i in range(len(DATA)): - (x, y) = DATA[i] - (dd, dl, r, dr, dp) = dash_style[i] - #print('dashlen call %d' % dl) - t = ax.text(x, y, str((x, y)), withdash=True, - dashdirection=dd, - dashlength=dl, - rotation=r, - dashrotation=dr, - dashpush=dp, - ) - -ax.set_xlim((0.0, 5.0)) -ax.set_ylim((0.0, 5.0)) - -plt.show() diff --git a/examples/pylab_examples/data_helper.py b/examples/pylab_examples/data_helper.py deleted file mode 100755 index f10b804b9745..000000000000 --- a/examples/pylab_examples/data_helper.py +++ /dev/null @@ -1,60 +0,0 @@ -# Some functions to load a return data for the plot demos - -from numpy import fromstring, argsort, take, array, resize -import matplotlib.cbook as cbook - - -def get_two_stock_data(): - """ - load stock time and price data for two stocks The return values - (d1,p1,d2,p2) are the trade time (in days) and prices for stocks 1 - and 2 (intc and aapl) - """ - ticker1, ticker2 = 'INTC', 'AAPL' - - file1 = cbook.get_sample_data('INTC.dat.gz') - file2 = cbook.get_sample_data('AAPL.dat.gz') - M1 = fromstring(file1.read(), '= len(self.dates) or ind < 0: - return '' - - return self.dates[ind].strftime(self.fmt) - -formatter = MyFormatter(r.date) - -fig, ax = plt.subplots() -ax.xaxis.set_major_formatter(formatter) -ax.plot(numpy.arange(len(r)), r.close, 'o-') -fig.autofmt_xdate() -plt.show() diff --git a/examples/pylab_examples/demo_agg_filter.py b/examples/pylab_examples/demo_agg_filter.py deleted file mode 100644 index 3337497675d2..000000000000 --- a/examples/pylab_examples/demo_agg_filter.py +++ /dev/null @@ -1,331 +0,0 @@ -import matplotlib.pyplot as plt - -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab - - -def smooth1d(x, window_len): - # copied from http://www.scipy.org/Cookbook/SignalSmooth - - s = np.r_[2*x[0] - x[window_len:1:-1], x, 2*x[-1] - x[-1:-window_len:-1]] - w = np.hanning(window_len) - y = np.convolve(w/w.sum(), s, mode='same') - return y[window_len-1:-window_len+1] - - -def smooth2d(A, sigma=3): - - window_len = max(int(sigma), 3)*2 + 1 - A1 = np.array([smooth1d(x, window_len) for x in np.asarray(A)]) - A2 = np.transpose(A1) - A3 = np.array([smooth1d(x, window_len) for x in A2]) - A4 = np.transpose(A3) - - return A4 - - -class BaseFilter(object): - def prepare_image(self, src_image, dpi, pad): - ny, nx, depth = src_image.shape - #tgt_image = np.zeros([pad*2+ny, pad*2+nx, depth], dtype="d") - padded_src = np.zeros([pad*2 + ny, pad*2 + nx, depth], dtype="d") - padded_src[pad:-pad, pad:-pad, :] = src_image[:, :, :] - - return padded_src # , tgt_image - - def get_pad(self, dpi): - return 0 - - def __call__(self, im, dpi): - pad = self.get_pad(dpi) - padded_src = self.prepare_image(im, dpi, pad) - tgt_image = self.process_image(padded_src, dpi) - return tgt_image, -pad, -pad - - -class OffsetFilter(BaseFilter): - def __init__(self, offsets=None): - if offsets is None: - self.offsets = (0, 0) - else: - self.offsets = offsets - - def get_pad(self, dpi): - return int(max(*self.offsets)/72.*dpi) - - def process_image(self, padded_src, dpi): - ox, oy = self.offsets - a1 = np.roll(padded_src, int(ox/72.*dpi), axis=1) - a2 = np.roll(a1, -int(oy/72.*dpi), axis=0) - return a2 - - -class GaussianFilter(BaseFilter): - "simple gauss filter" - - def __init__(self, sigma, alpha=0.5, color=None): - self.sigma = sigma - self.alpha = alpha - if color is None: - self.color = (0, 0, 0) - else: - self.color = color - - def get_pad(self, dpi): - return int(self.sigma*3/72.*dpi) - - def process_image(self, padded_src, dpi): - #offsetx, offsety = int(self.offsets[0]), int(self.offsets[1]) - tgt_image = np.zeros_like(padded_src) - aa = smooth2d(padded_src[:, :, -1]*self.alpha, - self.sigma/72.*dpi) - tgt_image[:, :, -1] = aa - tgt_image[:, :, :-1] = self.color - return tgt_image - - -class DropShadowFilter(BaseFilter): - def __init__(self, sigma, alpha=0.3, color=None, offsets=None): - self.gauss_filter = GaussianFilter(sigma, alpha, color) - self.offset_filter = OffsetFilter(offsets) - - def get_pad(self, dpi): - return max(self.gauss_filter.get_pad(dpi), - self.offset_filter.get_pad(dpi)) - - def process_image(self, padded_src, dpi): - t1 = self.gauss_filter.process_image(padded_src, dpi) - t2 = self.offset_filter.process_image(t1, dpi) - return t2 - - -from matplotlib.colors import LightSource - - -class LightFilter(BaseFilter): - "simple gauss filter" - - def __init__(self, sigma, fraction=0.5): - self.gauss_filter = GaussianFilter(sigma, alpha=1) - self.light_source = LightSource() - self.fraction = fraction - - def get_pad(self, dpi): - return self.gauss_filter.get_pad(dpi) - - def process_image(self, padded_src, dpi): - t1 = self.gauss_filter.process_image(padded_src, dpi) - elevation = t1[:, :, 3] - rgb = padded_src[:, :, :3] - - rgb2 = self.light_source.shade_rgb(rgb, elevation, - fraction=self.fraction) - - tgt = np.empty_like(padded_src) - tgt[:, :, :3] = rgb2 - tgt[:, :, 3] = padded_src[:, :, 3] - - return tgt - - -class GrowFilter(BaseFilter): - "enlarge the area" - - def __init__(self, pixels, color=None): - self.pixels = pixels - if color is None: - self.color = (1, 1, 1) - else: - self.color = color - - def __call__(self, im, dpi): - pad = self.pixels - ny, nx, depth = im.shape - new_im = np.empty([pad*2 + ny, pad*2 + nx, depth], dtype="d") - alpha = new_im[:, :, 3] - alpha.fill(0) - alpha[pad:-pad, pad:-pad] = im[:, :, -1] - alpha2 = np.clip(smooth2d(alpha, self.pixels/72.*dpi) * 5, 0, 1) - new_im[:, :, -1] = alpha2 - new_im[:, :, :-1] = self.color - offsetx, offsety = -pad, -pad - - return new_im, offsetx, offsety - - -from matplotlib.artist import Artist - - -class FilteredArtistList(Artist): - """ - A simple container to draw filtered artist. - """ - - def __init__(self, artist_list, filter): - self._artist_list = artist_list - self._filter = filter - Artist.__init__(self) - - def draw(self, renderer): - renderer.start_rasterizing() - renderer.start_filter() - for a in self._artist_list: - a.draw(renderer) - renderer.stop_filter(self._filter) - renderer.stop_rasterizing() - - -import matplotlib.transforms as mtransforms - - -def filtered_text(ax): - # mostly copied from contour_demo.py - - # prepare image - delta = 0.025 - x = np.arange(-3.0, 3.0, delta) - y = np.arange(-2.0, 2.0, delta) - X, Y = np.meshgrid(x, y) - Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) - # difference of Gaussians - Z = 10.0 * (Z2 - Z1) - - # draw - im = ax.imshow(Z, interpolation='bilinear', origin='lower', - cmap=cm.gray, extent=(-3, 3, -2, 2)) - levels = np.arange(-1.2, 1.6, 0.2) - CS = ax.contour(Z, levels, - origin='lower', - linewidths=2, - extent=(-3, 3, -2, 2)) - - ax.set_aspect("auto") - - # contour label - cl = ax.clabel(CS, levels[1::2], # label every second level - inline=1, - fmt='%1.1f', - fontsize=11) - - # change clable color to black - from matplotlib.patheffects import Normal - for t in cl: - t.set_color("k") - # to force TextPath (i.e., same font in all backends) - t.set_path_effects([Normal()]) - - # Add white glows to improve visibility of labels. - white_glows = FilteredArtistList(cl, GrowFilter(3)) - ax.add_artist(white_glows) - white_glows.set_zorder(cl[0].get_zorder() - 0.1) - - ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) - - -def drop_shadow_line(ax): - # copied from examples/misc/svg_filter_line.py - - # draw lines - l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-", - mec="b", mfc="w", lw=5, mew=3, ms=10, label="Line 1") - l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "ro-", - mec="r", mfc="w", lw=5, mew=3, ms=10, label="Line 1") - - gauss = DropShadowFilter(4) - - for l in [l1, l2]: - - # draw shadows with same lines with slight offset. - - xx = l.get_xdata() - yy = l.get_ydata() - shadow, = ax.plot(xx, yy) - shadow.update_from(l) - - # offset transform - ot = mtransforms.offset_copy(l.get_transform(), ax.figure, - x=4.0, y=-6.0, units='points') - - shadow.set_transform(ot) - - # adjust zorder of the shadow lines so that it is drawn below the - # original lines - shadow.set_zorder(l.get_zorder() - 0.5) - shadow.set_agg_filter(gauss) - shadow.set_rasterized(True) # to support mixed-mode renderers - - ax.set_xlim(0., 1.) - ax.set_ylim(0., 1.) - - ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) - - -def drop_shadow_patches(ax): - # copyed from barchart_demo.py - N = 5 - menMeans = (20, 35, 30, 35, 27) - - ind = np.arange(N) # the x locations for the groups - width = 0.35 # the width of the bars - - rects1 = ax.bar(ind, menMeans, width, color='r', ec="w", lw=2) - - womenMeans = (25, 32, 34, 20, 25) - rects2 = ax.bar(ind + width + 0.1, womenMeans, width, color='y', ec="w", lw=2) - - #gauss = GaussianFilter(1.5, offsets=(1,1), ) - gauss = DropShadowFilter(5, offsets=(1, 1), ) - shadow = FilteredArtistList(rects1 + rects2, gauss) - ax.add_artist(shadow) - shadow.set_zorder(rects1[0].get_zorder() - 0.1) - - ax.set_xlim(ind[0] - 0.5, ind[-1] + 1.5) - ax.set_ylim(0, 40) - - ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) - - -def light_filter_pie(ax): - fracs = [15, 30, 45, 10] - explode = (0, 0.05, 0, 0) - pies = ax.pie(fracs, explode=explode) - ax.patch.set_visible(True) - - light_filter = LightFilter(9) - for p in pies[0]: - p.set_agg_filter(light_filter) - p.set_rasterized(True) # to support mixed-mode renderers - p.set(ec="none", - lw=2) - - gauss = DropShadowFilter(9, offsets=(3, 4), alpha=0.7) - shadow = FilteredArtistList(pies[0], gauss) - ax.add_artist(shadow) - shadow.set_zorder(pies[0][0].get_zorder() - 0.1) - - -if 1: - - plt.figure(1, figsize=(6, 6)) - plt.subplots_adjust(left=0.05, right=0.95) - - ax = plt.subplot(221) - filtered_text(ax) - - ax = plt.subplot(222) - drop_shadow_line(ax) - - ax = plt.subplot(223) - drop_shadow_patches(ax) - - ax = plt.subplot(224) - ax.set_aspect(1) - light_filter_pie(ax) - ax.set_frame_on(True) - - plt.show() diff --git a/examples/pylab_examples/demo_annotation_box.py b/examples/pylab_examples/demo_annotation_box.py deleted file mode 100644 index 860195f4b932..000000000000 --- a/examples/pylab_examples/demo_annotation_box.py +++ /dev/null @@ -1,92 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -from matplotlib.patches import Circle -from matplotlib.offsetbox import (TextArea, DrawingArea, OffsetImage, - AnnotationBbox) -from matplotlib.cbook import get_sample_data - - -if 1: - fig, ax = plt.subplots() - - # Define a 1st position to annotate (display it with a marker) - xy = (0.5, 0.7) - ax.plot(xy[0], xy[1], ".r") - - # Annotate the 1st position with a text box ('Test 1') - offsetbox = TextArea("Test 1", minimumdescent=False) - - ab = AnnotationBbox(offsetbox, xy, - xybox=(-20, 40), - xycoords='data', - boxcoords="offset points", - arrowprops=dict(arrowstyle="->")) - ax.add_artist(ab) - - # Annotate the 1st position with another text box ('Test') - offsetbox = TextArea("Test", minimumdescent=False) - - ab = AnnotationBbox(offsetbox, xy, - xybox=(1.02, xy[1]), - xycoords='data', - boxcoords=("axes fraction", "data"), - box_alignment=(0., 0.5), - arrowprops=dict(arrowstyle="->")) - ax.add_artist(ab) - - # Define a 2nd position to annotate (don't display with a marker this time) - xy = [0.3, 0.55] - - # Annotate the 2nd position with a circle patch - da = DrawingArea(20, 20, 0, 0) - p = Circle((10, 10), 10) - da.add_artist(p) - - ab = AnnotationBbox(da, xy, - xybox=(1.02, xy[1]), - xycoords='data', - boxcoords=("axes fraction", "data"), - box_alignment=(0., 0.5), - arrowprops=dict(arrowstyle="->")) - - ax.add_artist(ab) - - # Annotate the 2nd position with an image (a generated array of pixels) - arr = np.arange(100).reshape((10, 10)) - im = OffsetImage(arr, zoom=2) - im.image.axes = ax - - ab = AnnotationBbox(im, xy, - xybox=(-50., 50.), - xycoords='data', - boxcoords="offset points", - pad=0.3, - arrowprops=dict(arrowstyle="->")) - - ax.add_artist(ab) - - # Annotate the 2nd position with another image (a Grace Hopper portrait) - fn = get_sample_data("grace_hopper.png", asfileobj=False) - arr_img = plt.imread(fn, format='png') - - imagebox = OffsetImage(arr_img, zoom=0.2) - imagebox.image.axes = ax - - ab = AnnotationBbox(imagebox, xy, - xybox=(120., -80.), - xycoords='data', - boxcoords="offset points", - pad=0.5, - arrowprops=dict( - arrowstyle="->", - connectionstyle="angle,angleA=0,angleB=90,rad=3") - ) - - ax.add_artist(ab) - - # Fix the display limits to see everything - ax.set_xlim(0, 1) - ax.set_ylim(0, 1) - - plt.show() diff --git a/examples/pylab_examples/demo_bboximage.py b/examples/pylab_examples/demo_bboximage.py deleted file mode 100644 index e8c2a412e705..000000000000 --- a/examples/pylab_examples/demo_bboximage.py +++ /dev/null @@ -1,61 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.image import BboxImage -from matplotlib.transforms import Bbox, TransformedBbox - -if __name__ == "__main__": - - fig = plt.figure(1) - ax = plt.subplot(121) - - txt = ax.text(0.5, 0.5, "test", size=30, ha="center", color="w") - kwargs = dict() - - bbox_image = BboxImage(txt.get_window_extent, - norm=None, - origin=None, - clip_on=False, - **kwargs - ) - a = np.arange(256).reshape(1, 256)/256. - bbox_image.set_data(a) - ax.add_artist(bbox_image) - - ax = plt.subplot(122) - a = np.linspace(0, 1, 256).reshape(1, -1) - a = np.vstack((a, a)) - - maps = sorted(m for m in plt.cm.cmap_d if not m.endswith("_r")) - #nmaps = len(maps) + 1 - - #fig.subplots_adjust(top=0.99, bottom=0.01, left=0.2, right=0.99) - - ncol = 2 - nrow = len(maps)//ncol + 1 - - xpad_fraction = 0.3 - dx = 1./(ncol + xpad_fraction*(ncol - 1)) - - ypad_fraction = 0.3 - dy = 1./(nrow + ypad_fraction*(nrow - 1)) - - for i, m in enumerate(maps): - ix, iy = divmod(i, nrow) - #plt.figimage(a, 10, i*10, cmap=plt.get_cmap(m), origin='lower') - bbox0 = Bbox.from_bounds(ix*dx*(1 + xpad_fraction), - 1. - iy*dy*(1 + ypad_fraction) - dy, - dx, dy) - bbox = TransformedBbox(bbox0, ax.transAxes) - - bbox_image = BboxImage(bbox, - cmap=plt.get_cmap(m), - norm=None, - origin=None, - **kwargs - ) - - bbox_image.set_data(a) - ax.add_artist(bbox_image) - - plt.draw() - plt.show() diff --git a/examples/pylab_examples/demo_ribbon_box.py b/examples/pylab_examples/demo_ribbon_box.py deleted file mode 100644 index 39bbe39789af..000000000000 --- a/examples/pylab_examples/demo_ribbon_box.py +++ /dev/null @@ -1,134 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.image import BboxImage - -from matplotlib._png import read_png -import matplotlib.colors -from matplotlib.cbook import get_sample_data - - -class RibbonBox(object): - - original_image = read_png(get_sample_data("Minduka_Present_Blue_Pack.png", - asfileobj=False)) - cut_location = 70 - b_and_h = original_image[:, :, 2] - color = original_image[:, :, 2] - original_image[:, :, 0] - alpha = original_image[:, :, 3] - nx = original_image.shape[1] - - def __init__(self, color): - rgb = matplotlib.colors.to_rgba(color)[:3] - - im = np.empty(self.original_image.shape, - self.original_image.dtype) - - im[:, :, :3] = self.b_and_h[:, :, np.newaxis] - im[:, :, :3] -= self.color[:, :, np.newaxis]*(1. - np.array(rgb)) - im[:, :, 3] = self.alpha - - self.im = im - - def get_stretched_image(self, stretch_factor): - stretch_factor = max(stretch_factor, 1) - ny, nx, nch = self.im.shape - ny2 = int(ny*stretch_factor) - - stretched_image = np.empty((ny2, nx, nch), - self.im.dtype) - cut = self.im[self.cut_location, :, :] - stretched_image[:, :, :] = cut - stretched_image[:self.cut_location, :, :] = \ - self.im[:self.cut_location, :, :] - stretched_image[-(ny - self.cut_location):, :, :] = \ - self.im[-(ny - self.cut_location):, :, :] - - self._cached_im = stretched_image - return stretched_image - - -class RibbonBoxImage(BboxImage): - zorder = 1 - - def __init__(self, bbox, color, - cmap=None, - norm=None, - interpolation=None, - origin=None, - filternorm=1, - filterrad=4.0, - resample=False, - **kwargs - ): - - BboxImage.__init__(self, bbox, - cmap=cmap, - norm=norm, - interpolation=interpolation, - origin=origin, - filternorm=filternorm, - filterrad=filterrad, - resample=resample, - **kwargs - ) - - self._ribbonbox = RibbonBox(color) - self._cached_ny = None - - def draw(self, renderer, *args, **kwargs): - - bbox = self.get_window_extent(renderer) - stretch_factor = bbox.height / bbox.width - - ny = int(stretch_factor*self._ribbonbox.nx) - if self._cached_ny != ny: - arr = self._ribbonbox.get_stretched_image(stretch_factor) - self.set_array(arr) - self._cached_ny = ny - - BboxImage.draw(self, renderer, *args, **kwargs) - - -if 1: - from matplotlib.transforms import Bbox, TransformedBbox - from matplotlib.ticker import ScalarFormatter - - fig, ax = plt.subplots() - - years = np.arange(2004, 2009) - box_colors = [(0.8, 0.2, 0.2), - (0.2, 0.8, 0.2), - (0.2, 0.2, 0.8), - (0.7, 0.5, 0.8), - (0.3, 0.8, 0.7), - ] - heights = np.random.random(years.shape) * 7000 + 3000 - - fmt = ScalarFormatter(useOffset=False) - ax.xaxis.set_major_formatter(fmt) - - for year, h, bc in zip(years, heights, box_colors): - bbox0 = Bbox.from_extents(year - 0.4, 0., year + 0.4, h) - bbox = TransformedBbox(bbox0, ax.transData) - rb_patch = RibbonBoxImage(bbox, bc, interpolation="bicubic") - - ax.add_artist(rb_patch) - - ax.annotate(r"%d" % (int(h/100.)*100), - (year, h), va="bottom", ha="center") - - patch_gradient = BboxImage(ax.bbox, - interpolation="bicubic", - zorder=0.1, - ) - gradient = np.zeros((2, 2, 4), dtype=float) - gradient[:, :, :3] = [1, 1, 0.] - gradient[:, :, 3] = [[0.1, 0.3], [0.3, 0.5]] # alpha channel - patch_gradient.set_array(gradient) - ax.add_artist(patch_gradient) - - ax.set_xlim(years[0] - 0.5, years[-1] + 0.5) - ax.set_ylim(0, 10000) - - fig.savefig('ribbon_box.png') - plt.show() diff --git a/examples/pylab_examples/demo_text_path.py b/examples/pylab_examples/demo_text_path.py deleted file mode 100644 index f155fd5dc6f2..000000000000 --- a/examples/pylab_examples/demo_text_path.py +++ /dev/null @@ -1,151 +0,0 @@ - -# -*- coding: utf-8 -*- - -import matplotlib.pyplot as plt -from matplotlib.image import BboxImage -import numpy as np -from matplotlib.transforms import IdentityTransform - -import matplotlib.patches as mpatches - -from matplotlib.offsetbox import AnnotationBbox,\ - AnchoredOffsetbox, AuxTransformBox - -from matplotlib.cbook import get_sample_data - -from matplotlib.text import TextPath - - -class PathClippedImagePatch(mpatches.PathPatch): - """ - The given image is used to draw the face of the patch. Internally, - it uses BboxImage whose clippath set to the path of the patch. - - FIXME : The result is currently dpi dependent. - """ - - def __init__(self, path, bbox_image, **kwargs): - mpatches.PathPatch.__init__(self, path, **kwargs) - self._init_bbox_image(bbox_image) - - def set_facecolor(self, color): - """simply ignore facecolor""" - mpatches.PathPatch.set_facecolor(self, "none") - - def _init_bbox_image(self, im): - - bbox_image = BboxImage(self.get_window_extent, - norm=None, - origin=None, - ) - bbox_image.set_transform(IdentityTransform()) - - bbox_image.set_data(im) - self.bbox_image = bbox_image - - def draw(self, renderer=None): - - # the clip path must be updated every draw. any solution? -JJ - self.bbox_image.set_clip_path(self._path, self.get_transform()) - self.bbox_image.draw(renderer) - - mpatches.PathPatch.draw(self, renderer) - - -if 1: - - usetex = plt.rcParams["text.usetex"] - - fig = plt.figure(1) - - # EXAMPLE 1 - - ax = plt.subplot(211) - - from matplotlib._png import read_png - fn = get_sample_data("grace_hopper.png", asfileobj=False) - arr = read_png(fn) - - text_path = TextPath((0, 0), "!?", size=150) - p = PathClippedImagePatch(text_path, arr, ec="k", - transform=IdentityTransform()) - - #p.set_clip_on(False) - - # make offset box - offsetbox = AuxTransformBox(IdentityTransform()) - offsetbox.add_artist(p) - - # make anchored offset box - ao = AnchoredOffsetbox(loc=2, child=offsetbox, frameon=True, borderpad=0.2) - ax.add_artist(ao) - - # another text - from matplotlib.patches import PathPatch - if usetex: - r = r"\mbox{textpath supports mathtext \& \TeX}" - else: - r = r"textpath supports mathtext & TeX" - - text_path = TextPath((0, 0), r, - size=20, usetex=usetex) - - p1 = PathPatch(text_path, ec="w", lw=3, fc="w", alpha=0.9, - transform=IdentityTransform()) - p2 = PathPatch(text_path, ec="none", fc="k", - transform=IdentityTransform()) - - offsetbox2 = AuxTransformBox(IdentityTransform()) - offsetbox2.add_artist(p1) - offsetbox2.add_artist(p2) - - ab = AnnotationBbox(offsetbox2, (0.95, 0.05), - xycoords='axes fraction', - boxcoords="offset points", - box_alignment=(1., 0.), - frameon=False - ) - ax.add_artist(ab) - - ax.imshow([[0, 1, 2], [1, 2, 3]], cmap=plt.cm.gist_gray_r, - interpolation="bilinear", - aspect="auto") - - # EXAMPLE 2 - - ax = plt.subplot(212) - - arr = np.arange(256).reshape(1, 256)/256. - - if usetex: - s = r"$\displaystyle\left[\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}\right]$!" - else: - s = r"$\left[\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}\right]$!" - text_path = TextPath((0, 0), s, size=40, usetex=usetex) - text_patch = PathClippedImagePatch(text_path, arr, ec="none", - transform=IdentityTransform()) - - shadow1 = mpatches.Shadow(text_patch, 1, -1, props=dict(fc="none", ec="0.6", lw=3)) - shadow2 = mpatches.Shadow(text_patch, 1, -1, props=dict(fc="0.3", ec="none")) - - # make offset box - offsetbox = AuxTransformBox(IdentityTransform()) - offsetbox.add_artist(shadow1) - offsetbox.add_artist(shadow2) - offsetbox.add_artist(text_patch) - - # place the anchored offset box using AnnotationBbox - ab = AnnotationBbox(offsetbox, (0.5, 0.5), - xycoords='data', - boxcoords="offset points", - box_alignment=(0.5, 0.5), - ) - #text_path.set_size(10) - - ax.add_artist(ab) - - ax.set_xlim(0, 1) - ax.set_ylim(0, 1) - - plt.draw() - plt.show() diff --git a/examples/pylab_examples/demo_text_rotation_mode.py b/examples/pylab_examples/demo_text_rotation_mode.py deleted file mode 100644 index 264aa49e927d..000000000000 --- a/examples/pylab_examples/demo_text_rotation_mode.py +++ /dev/null @@ -1,45 +0,0 @@ -from mpl_toolkits.axes_grid1.axes_grid import ImageGrid - - -def test_rotation_mode(fig, mode, subplot_location): - ha_list = "left center right".split() - va_list = "top center baseline bottom".split() - grid = ImageGrid(fig, subplot_location, - nrows_ncols=(len(va_list), len(ha_list)), - share_all=True, aspect=True, - #label_mode='1', - cbar_mode=None) - - for ha, ax in zip(ha_list, grid.axes_row[-1]): - ax.axis["bottom"].label.set_text(ha) - - grid.axes_row[0][1].set_title(mode, size="large") - - for va, ax in zip(va_list, grid.axes_column[0]): - ax.axis["left"].label.set_text(va) - - i = 0 - for va in va_list: - for ha in ha_list: - ax = grid[i] - for axis in ax.axis.values(): - axis.toggle(ticks=False, ticklabels=False) - - ax.text(0.5, 0.5, "Tpg", - size="large", rotation=40, - bbox=dict(boxstyle="square,pad=0.", - ec="none", fc="0.5", alpha=0.5), - ha=ha, va=va, - rotation_mode=mode) - ax.axvline(0.5) - ax.axhline(0.5) - i += 1 - -if 1: - import matplotlib.pyplot as plt - fig = plt.figure(1, figsize=(5.5, 4)) - fig.clf() - - test_rotation_mode(fig, "default", 121) - test_rotation_mode(fig, "anchor", 122) - plt.show() diff --git a/examples/pylab_examples/demo_tight_layout.py b/examples/pylab_examples/demo_tight_layout.py deleted file mode 100644 index 544fb25db666..000000000000 --- a/examples/pylab_examples/demo_tight_layout.py +++ /dev/null @@ -1,116 +0,0 @@ - -import matplotlib.pyplot as plt -import itertools -import warnings - - -fontsizes = itertools.cycle([8, 16, 24, 32]) - - -def example_plot(ax): - ax.plot([1, 2]) - ax.set_xlabel('x-label', fontsize=next(fontsizes)) - ax.set_ylabel('y-label', fontsize=next(fontsizes)) - ax.set_title('Title', fontsize=next(fontsizes)) - - -fig, ax = plt.subplots() -example_plot(ax) -plt.tight_layout() - -fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2) -example_plot(ax1) -example_plot(ax2) -example_plot(ax3) -example_plot(ax4) -plt.tight_layout() - -fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1) -example_plot(ax1) -example_plot(ax2) -plt.tight_layout() - -fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2) -example_plot(ax1) -example_plot(ax2) -plt.tight_layout() - -fig, axes = plt.subplots(nrows=3, ncols=3) -for row in axes: - for ax in row: - example_plot(ax) -plt.tight_layout() - - -fig = plt.figure() - -ax1 = plt.subplot(221) -ax2 = plt.subplot(223) -ax3 = plt.subplot(122) - -example_plot(ax1) -example_plot(ax2) -example_plot(ax3) - -plt.tight_layout() - - -fig = plt.figure() - -ax1 = plt.subplot2grid((3, 3), (0, 0)) -ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2) -ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2) -ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2) - -example_plot(ax1) -example_plot(ax2) -example_plot(ax3) -example_plot(ax4) - -plt.tight_layout() - -plt.show() - - -fig = plt.figure() - -import matplotlib.gridspec as gridspec - -gs1 = gridspec.GridSpec(3, 1) -ax1 = fig.add_subplot(gs1[0]) -ax2 = fig.add_subplot(gs1[1]) -ax3 = fig.add_subplot(gs1[2]) - -example_plot(ax1) -example_plot(ax2) -example_plot(ax3) - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", UserWarning) - # This raises warnings since tight layout cannot - # handle gridspec automatically. We are going to - # do that manually so we can filter the warning. - gs1.tight_layout(fig, rect=[None, None, 0.45, None]) - -gs2 = gridspec.GridSpec(2, 1) -ax4 = fig.add_subplot(gs2[0]) -ax5 = fig.add_subplot(gs2[1]) - -example_plot(ax4) -example_plot(ax5) - -with warnings.catch_warnings(): - # This raises warnings since tight layout cannot - # handle gridspec automatically. We are going to - # do that manually so we can filter the warning. - warnings.simplefilter("ignore", UserWarning) - gs2.tight_layout(fig, rect=[0.45, None, None, None]) - -# now match the top and bottom of two gridspecs. -top = min(gs1.top, gs2.top) -bottom = max(gs1.bottom, gs2.bottom) - -gs1.update(top=top, bottom=bottom) -gs2.update(top=top, bottom=bottom) - -plt.show() diff --git a/examples/pylab_examples/dolphin.py b/examples/pylab_examples/dolphin.py deleted file mode 100644 index 10fdd88f29df..000000000000 --- a/examples/pylab_examples/dolphin.py +++ /dev/null @@ -1,91 +0,0 @@ -import matplotlib.cm as cm -import matplotlib.pyplot as plt -from matplotlib.patches import Circle, PathPatch -from matplotlib.path import Path -from matplotlib.transforms import Affine2D -import numpy as np - - -r = np.random.rand(50) -t = np.random.rand(50) * np.pi * 2.0 -x = r * np.cos(t) -y = r * np.sin(t) - -fig, ax = plt.subplots(figsize=(6, 6)) -circle = Circle((0, 0), 1, facecolor='none', - edgecolor=(0, 0.8, 0.8), linewidth=3, alpha=0.5) -ax.add_patch(circle) - -im = plt.imshow(np.random.random((100, 100)), - origin='lower', cmap=cm.winter, - interpolation='spline36', - extent=([-1, 1, -1, 1])) -im.set_clip_path(circle) - -plt.plot(x, y, 'o', color=(0.9, 0.9, 1.0), alpha=0.8) - -# Dolphin from OpenClipart library by Andy Fitzsimon -# -# -# -# -# - -dolphin = """ -M -0.59739425,160.18173 C -0.62740401,160.18885 -0.57867129,160.11183 --0.57867129,160.11183 C -0.57867129,160.11183 -0.5438361,159.89315 --0.39514638,159.81496 C -0.24645668,159.73678 -0.18316813,159.71981 --0.18316813,159.71981 C -0.18316813,159.71981 -0.10322971,159.58124 --0.057804323,159.58725 C -0.029723983,159.58913 -0.061841603,159.60356 --0.071265813,159.62815 C -0.080250183,159.65325 -0.082918513,159.70554 --0.061841203,159.71248 C -0.040763903,159.7194 -0.0066711426,159.71091 -0.077336307,159.73612 C 0.16879567,159.76377 0.28380306,159.86448 -0.31516668,159.91533 C 0.3465303,159.96618 0.5011127,160.1771 -0.5011127,160.1771 C 0.63668998,160.19238 0.67763022,160.31259 -0.66556395,160.32668 C 0.65339985,160.34212 0.66350443,160.33642 -0.64907098,160.33088 C 0.63463742,160.32533 0.61309688,160.297 -0.5789627,160.29339 C 0.54348657,160.28968 0.52329693,160.27674 -0.50728856,160.27737 C 0.49060916,160.27795 0.48965803,160.31565 -0.46114204,160.33673 C 0.43329696,160.35786 0.4570711,160.39871 -0.43309565,160.40685 C 0.4105108,160.41442 0.39416631,160.33027 -0.3954995,160.2935 C 0.39683269,160.25672 0.43807996,160.21522 -0.44567915,160.19734 C 0.45327833,160.17946 0.27946869,159.9424 --0.061852613,159.99845 C -0.083965233,160.0427 -0.26176109,160.06683 --0.26176109,160.06683 C -0.30127962,160.07028 -0.21167141,160.09731 --0.24649368,160.1011 C -0.32642366,160.11569 -0.34521187,160.06895 --0.40622293,160.0819 C -0.467234,160.09485 -0.56738444,160.17461 --0.59739425,160.18173 -""" - -vertices = [] -codes = [] -parts = dolphin.split() -i = 0 -code_map = { - 'M': (Path.MOVETO, 1), - 'C': (Path.CURVE4, 3), - 'L': (Path.LINETO, 1) - } - -while i < len(parts): - code = parts[i] - path_code, npoints = code_map[code] - codes.extend([path_code] * npoints) - vertices.extend([[float(x) for x in y.split(',')] for y in - parts[i + 1:i + npoints + 1]]) - i += npoints + 1 -vertices = np.array(vertices, float) -vertices[:, 1] -= 160 - -dolphin_path = Path(vertices, codes) -dolphin_patch = PathPatch(dolphin_path, facecolor=(0.6, 0.6, 0.6), - edgecolor=(0.0, 0.0, 0.0)) -ax.add_patch(dolphin_patch) - -vertices = Affine2D().rotate_deg(60).transform(vertices) -dolphin_path2 = Path(vertices, codes) -dolphin_patch2 = PathPatch(dolphin_path2, facecolor=(0.5, 0.5, 0.5), - edgecolor=(0.0, 0.0, 0.0)) -ax.add_patch(dolphin_patch2) - -plt.show() diff --git a/examples/pylab_examples/ellipse_collection.py b/examples/pylab_examples/ellipse_collection.py deleted file mode 100644 index 3d98c0c12467..000000000000 --- a/examples/pylab_examples/ellipse_collection.py +++ /dev/null @@ -1,27 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.collections import EllipseCollection - -x = np.arange(10) -y = np.arange(15) -X, Y = np.meshgrid(x, y) - -XY = np.hstack((X.ravel()[:, np.newaxis], Y.ravel()[:, np.newaxis])) - -ww = X/10.0 -hh = Y/15.0 -aa = X*9 - - -fig, ax = plt.subplots() - -ec = EllipseCollection(ww, hh, aa, units='x', offsets=XY, - transOffset=ax.transData) -ec.set_array((X + Y).ravel()) -ax.add_collection(ec) -ax.autoscale_view() -ax.set_xlabel('X') -ax.set_ylabel('y') -cbar = plt.colorbar(ec) -cbar.set_label('X+Y') -plt.show() diff --git a/examples/pylab_examples/ellipse_demo.py b/examples/pylab_examples/ellipse_demo.py deleted file mode 100644 index d77e06fa8673..000000000000 --- a/examples/pylab_examples/ellipse_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -import matplotlib.pyplot as plt -import numpy.random as rnd -from matplotlib.patches import Ellipse - -NUM = 250 - -ells = [Ellipse(xy=rnd.rand(2)*10, width=rnd.rand(), height=rnd.rand(), angle=rnd.rand()*360) - for i in range(NUM)] - -fig = plt.figure(0) -ax = fig.add_subplot(111, aspect='equal') -for e in ells: - ax.add_artist(e) - e.set_clip_box(ax.bbox) - e.set_alpha(rnd.rand()) - e.set_facecolor(rnd.rand(3)) - -ax.set_xlim(0, 10) -ax.set_ylim(0, 10) - -plt.show() diff --git a/examples/pylab_examples/ellipse_rotated.py b/examples/pylab_examples/ellipse_rotated.py deleted file mode 100644 index f90f8690a034..000000000000 --- a/examples/pylab_examples/ellipse_rotated.py +++ /dev/null @@ -1,20 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.patches import Ellipse - -delta = 45.0 # degrees - -angles = np.arange(0, 360 + delta, delta) -ells = [Ellipse((1, 1), 4, 2, a) for a in angles] - -a = plt.subplot(111, aspect='equal') - -for e in ells: - e.set_clip_box(a.bbox) - e.set_alpha(0.1) - a.add_artist(e) - -plt.xlim(-2, 4) -plt.ylim(-1, 3) - -plt.show() diff --git a/examples/pylab_examples/equal_aspect_ratio.py b/examples/pylab_examples/equal_aspect_ratio.py deleted file mode 100755 index 9ed478095e6d..000000000000 --- a/examples/pylab_examples/equal_aspect_ratio.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Example: simple line plot. -Show how to make a plot that has equal aspect ratio -""" -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 1.0 + 0.01, 0.01) -s = np.cos(2*2*np.pi*t) -plt.plot(t, s, '-', lw=2) - -plt.xlabel('time (s)') -plt.ylabel('voltage (mV)') -plt.title('About as simple as it gets, folks') -plt.grid(True) - -plt.axes().set_aspect('equal', 'datalim') - - -plt.show() diff --git a/examples/pylab_examples/errorbar_limits.py b/examples/pylab_examples/errorbar_limits.py deleted file mode 100644 index 7f62dfecdab7..000000000000 --- a/examples/pylab_examples/errorbar_limits.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Illustration of upper and lower limit symbols on errorbars -""" - -import numpy as np -import matplotlib.pyplot as plt - -fig = plt.figure(0) -x = np.arange(10.0) -y = np.sin(np.arange(10.0)/20.0*np.pi) - -plt.errorbar(x, y, yerr=0.1) - -y = np.sin(np.arange(10.0)/20.0*np.pi) + 1 -plt.errorbar(x, y, yerr=0.1, uplims=True) - -y = np.sin(np.arange(10.0)/20.0*np.pi) + 2 -upperlimits = np.array([1, 0]*5) -lowerlimits = np.array([0, 1]*5) -plt.errorbar(x, y, yerr=0.1, uplims=upperlimits, lolims=lowerlimits) - -plt.xlim(-1, 10) - -fig = plt.figure(1) -x = np.arange(10.0)/10.0 -y = (x + 0.1)**2 - -plt.errorbar(x, y, xerr=0.1, xlolims=True) -y = (x + 0.1)**3 - -plt.errorbar(x + 0.6, y, xerr=0.1, xuplims=upperlimits, xlolims=lowerlimits) - -y = (x + 0.1)**4 -plt.errorbar(x + 1.2, y, xerr=0.1, xuplims=True) - -plt.xlim(-0.2, 2.4) -plt.ylim(-0.1, 1.3) - -plt.show() diff --git a/examples/pylab_examples/errorbar_subsample.py b/examples/pylab_examples/errorbar_subsample.py deleted file mode 100644 index 6b21a36d4920..000000000000 --- a/examples/pylab_examples/errorbar_subsample.py +++ /dev/null @@ -1,30 +0,0 @@ -''' -Demo for the errorevery keyword to show data full accuracy data plots with -few errorbars. -''' - -import numpy as np -import matplotlib.pyplot as plt - -# example data -x = np.arange(0.1, 4, 0.1) -y = np.exp(-x) - -# example variable error bar values -yerr = 0.1 + 0.1*np.sqrt(x) - - -# Now switch to a more OO interface to exercise more features. -fig, axs = plt.subplots(nrows=1, ncols=2, sharex=True) -ax = axs[0] -ax.errorbar(x, y, yerr=yerr) -ax.set_title('all errorbars') - -ax = axs[1] -ax.errorbar(x, y, yerr=yerr, errorevery=5) -ax.set_title('only every 5th errorbar') - - -fig.suptitle('Errorbar subsampling for better visualibility') - -plt.show() diff --git a/examples/pylab_examples/eventcollection_demo.py b/examples/pylab_examples/eventcollection_demo.py deleted file mode 100644 index 96d389baff05..000000000000 --- a/examples/pylab_examples/eventcollection_demo.py +++ /dev/null @@ -1,54 +0,0 @@ -''' -Plot two curves, then use EventCollections to mark the locations of the x -and y data points on the respective axes for each curve -''' - -import matplotlib.pyplot as plt -from matplotlib.collections import EventCollection -import numpy as np - -# create random data -np.random.seed(50) -xdata = np.random.random([2, 10]) - -# split the data into two parts -xdata1 = xdata[0, :] -xdata2 = xdata[1, :] - -# sort the data so it makes clean curves -xdata1.sort() -xdata2.sort() - -# create some y data points -ydata1 = xdata1 ** 2 -ydata2 = 1 - xdata2 ** 3 - -# plot the data -fig = plt.figure() -ax = fig.add_subplot(1, 1, 1) -ax.plot(xdata1, ydata1, 'r', xdata2, ydata2, 'b') - -# create the events marking the x data points -xevents1 = EventCollection(xdata1, color=[1, 0, 0], linelength=0.05) -xevents2 = EventCollection(xdata2, color=[0, 0, 1], linelength=0.05) - -# create the events marking the y data points -yevents1 = EventCollection(ydata1, color=[1, 0, 0], linelength=0.05, - orientation='vertical') -yevents2 = EventCollection(ydata2, color=[0, 0, 1], linelength=0.05, - orientation='vertical') - -# add the events to the axis -ax.add_collection(xevents1) -ax.add_collection(xevents2) -ax.add_collection(yevents1) -ax.add_collection(yevents2) - -# set the limits -ax.set_xlim([0, 1]) -ax.set_ylim([0, 1]) - -ax.set_title('line plot with data points') - -# display the plot -plt.show() diff --git a/examples/pylab_examples/eventplot_demo.py b/examples/pylab_examples/eventplot_demo.py deleted file mode 100755 index dd6d8fa1c48f..000000000000 --- a/examples/pylab_examples/eventplot_demo.py +++ /dev/null @@ -1,65 +0,0 @@ -''' -An eventplot showing sequences of events with various line properties. -The plot is shown in both horizontal and vertical orientations. -''' - -import matplotlib.pyplot as plt -import numpy as np -import matplotlib -matplotlib.rcParams['font.size'] = 8.0 - -# set the random seed -np.random.seed(0) - -# create random data -data1 = np.random.random([6, 50]) - -# set different colors for each set of positions -colors1 = np.array([[1, 0, 0], - [0, 1, 0], - [0, 0, 1], - [1, 1, 0], - [1, 0, 1], - [0, 1, 1]]) - -# set different line properties for each set of positions -# note that some overlap -lineoffsets1 = np.array([-15, -3, 1, 1.5, 6, 10]) -linelengths1 = [5, 2, 1, 1, 3, 1.5] - -fig = plt.figure() - -# create a horizontal plot -ax1 = fig.add_subplot(221) -ax1.eventplot(data1, colors=colors1, lineoffsets=lineoffsets1, - linelengths=linelengths1) - - -# create a vertical plot -ax2 = fig.add_subplot(223) -ax2.eventplot(data1, colors=colors1, lineoffsets=lineoffsets1, - linelengths=linelengths1, orientation='vertical') - -# create another set of random data. -# the gamma distribution is only used fo aesthetic purposes -data2 = np.random.gamma(4, size=[60, 50]) - -# use individual values for the parameters this time -# these values will be used for all data sets (except lineoffsets2, which -# sets the increment between each data set in this usage) -colors2 = [[0, 0, 0]] -lineoffsets2 = 1 -linelengths2 = 1 - -# create a horizontal plot -ax1 = fig.add_subplot(222) -ax1.eventplot(data2, colors=colors2, lineoffsets=lineoffsets2, - linelengths=linelengths2) - - -# create a vertical plot -ax2 = fig.add_subplot(224) -ax2.eventplot(data2, colors=colors2, lineoffsets=lineoffsets2, - linelengths=linelengths2, orientation='vertical') - -plt.show() diff --git a/examples/pylab_examples/fancyarrow_demo.py b/examples/pylab_examples/fancyarrow_demo.py deleted file mode 100644 index 227e53bb36e0..000000000000 --- a/examples/pylab_examples/fancyarrow_demo.py +++ /dev/null @@ -1,50 +0,0 @@ -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - -styles = mpatches.ArrowStyle.get_styles() - -ncol = 2 -nrow = (len(styles) + 1) // ncol -figheight = (nrow + 0.5) -fig1 = plt.figure(1, (4.*ncol/1.5, figheight/1.5)) -fontsize = 0.2 * 70 - - -ax = fig1.add_axes([0, 0, 1, 1], frameon=False, aspect=1.) - -ax.set_xlim(0, 4*ncol) -ax.set_ylim(0, figheight) - - -def to_texstring(s): - s = s.replace("<", r"$<$") - s = s.replace(">", r"$>$") - s = s.replace("|", r"$|$") - return s - -for i, (stylename, styleclass) in enumerate(sorted(styles.items())): - x = 3.2 + (i//nrow)*4 - y = (figheight - 0.7 - i % nrow) # /figheight - p = mpatches.Circle((x, y), 0.2) - ax.add_patch(p) - - ax.annotate(to_texstring(stylename), (x, y), - (x - 1.2, y), - #xycoords="figure fraction", textcoords="figure fraction", - ha="right", va="center", - size=fontsize, - arrowprops=dict(arrowstyle=stylename, - patchB=p, - shrinkA=5, - shrinkB=5, - fc="k", ec="k", - connectionstyle="arc3,rad=-0.05", - ), - bbox=dict(boxstyle="square", fc="w")) - -ax.xaxis.set_visible(False) -ax.yaxis.set_visible(False) - - -plt.draw() -plt.show() diff --git a/examples/pylab_examples/fancybox_demo.py b/examples/pylab_examples/fancybox_demo.py deleted file mode 100644 index 410fbf8890ee..000000000000 --- a/examples/pylab_examples/fancybox_demo.py +++ /dev/null @@ -1,163 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.transforms as mtransforms -from matplotlib.patches import FancyBboxPatch - - -# Bbox object around which the fancy box will be drawn. -bb = mtransforms.Bbox([[0.3, 0.4], [0.7, 0.6]]) - - -def draw_bbox(ax, bb): - # boxstyle=square with pad=0, i.e. bbox itself. - p_bbox = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="square,pad=0.", - ec="k", fc="none", zorder=10., - ) - ax.add_patch(p_bbox) - - -def test1(ax): - - # a fancy box with round corners. pad=0.1 - p_fancy = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="round,pad=0.1", - fc=(1., .8, 1.), - ec=(1., 0.5, 1.)) - - ax.add_patch(p_fancy) - - ax.text(0.1, 0.8, - r' boxstyle="round,pad=0.1"', - size=10, transform=ax.transAxes) - - # draws control points for the fancy box. - #l = p_fancy.get_path().vertices - #ax.plot(l[:,0], l[:,1], ".") - - # draw the original bbox in black - draw_bbox(ax, bb) - - -def test2(ax): - - # bbox=round has two optional argument. pad and rounding_size. - # They can be set during the initialization. - p_fancy = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="round,pad=0.1", - fc=(1., .8, 1.), - ec=(1., 0.5, 1.)) - - ax.add_patch(p_fancy) - - # boxstyle and its argument can be later modified with - # set_boxstyle method. Note that the old attributes are simply - # forgotten even if the boxstyle name is same. - - p_fancy.set_boxstyle("round,pad=0.1, rounding_size=0.2") - # or - #p_fancy.set_boxstyle("round", pad=0.1, rounding_size=0.2) - - ax.text(0.1, 0.8, - ' boxstyle="round,pad=0.1\n rounding\\_size=0.2"', - size=10, transform=ax.transAxes) - - # draws control points for the fancy box. - #l = p_fancy.get_path().vertices - #ax.plot(l[:,0], l[:,1], ".") - - draw_bbox(ax, bb) - - -def test3(ax): - - # mutation_scale determine overall scale of the mutation, - # i.e. both pad and rounding_size is scaled according to this - # value. - p_fancy = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="round,pad=0.1", - mutation_scale=2., - fc=(1., .8, 1.), - ec=(1., 0.5, 1.)) - - ax.add_patch(p_fancy) - - ax.text(0.1, 0.8, - ' boxstyle="round,pad=0.1"\n mutation\\_scale=2', - size=10, transform=ax.transAxes) - - # draws control points for the fancy box. - #l = p_fancy.get_path().vertices - #ax.plot(l[:,0], l[:,1], ".") - - draw_bbox(ax, bb) - - -def test4(ax): - - # When the aspect ratio of the axes is not 1, the fancy box may - # not be what you expected (green) - - p_fancy = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="round,pad=0.2", - fc="none", - ec=(0., .5, 0.), zorder=4) - - ax.add_patch(p_fancy) - - # You can compensate this by setting the mutation_aspect (pink). - p_fancy = FancyBboxPatch((bb.xmin, bb.ymin), - abs(bb.width), abs(bb.height), - boxstyle="round,pad=0.3", - mutation_aspect=.5, - fc=(1., 0.8, 1.), - ec=(1., 0.5, 1.)) - - ax.add_patch(p_fancy) - - ax.text(0.1, 0.8, - ' boxstyle="round,pad=0.3"\n mutation\\_aspect=.5', - size=10, transform=ax.transAxes) - - draw_bbox(ax, bb) - - -def test_all(): - plt.clf() - - ax = plt.subplot(2, 2, 1) - test1(ax) - ax.set_xlim(0., 1.) - ax.set_ylim(0., 1.) - ax.set_title("test1") - ax.set_aspect(1.) - - ax = plt.subplot(2, 2, 2) - ax.set_title("test2") - test2(ax) - ax.set_xlim(0., 1.) - ax.set_ylim(0., 1.) - ax.set_aspect(1.) - - ax = plt.subplot(2, 2, 3) - ax.set_title("test3") - test3(ax) - ax.set_xlim(0., 1.) - ax.set_ylim(0., 1.) - ax.set_aspect(1) - - ax = plt.subplot(2, 2, 4) - ax.set_title("test4") - test4(ax) - ax.set_xlim(-0.5, 1.5) - ax.set_ylim(0., 1.) - ax.set_aspect(2.) - - plt.draw() - plt.show() - -test_all() diff --git a/examples/pylab_examples/fancybox_demo2.py b/examples/pylab_examples/fancybox_demo2.py deleted file mode 100644 index dc953d4866d5..000000000000 --- a/examples/pylab_examples/fancybox_demo2.py +++ /dev/null @@ -1,18 +0,0 @@ -import matplotlib.patches as mpatch -import matplotlib.pyplot as plt - -styles = mpatch.BoxStyle.get_styles() -spacing = 1.2 - -figheight = (spacing * len(styles) + .5) -fig1 = plt.figure(1, (4/1.5, figheight/1.5)) -fontsize = 0.3 * 72 - -for i, stylename in enumerate(sorted(styles.keys())): - fig1.text(0.5, (spacing * (float(len(styles)) - i) - 0.5)/figheight, stylename, - ha="center", - size=fontsize, - transform=fig1.transFigure, - bbox=dict(boxstyle=stylename, fc="w", ec="k")) -plt.draw() -plt.show() diff --git a/examples/pylab_examples/fancytextbox_demo.py b/examples/pylab_examples/fancytextbox_demo.py deleted file mode 100644 index cd339f02ecd8..000000000000 --- a/examples/pylab_examples/fancytextbox_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -import matplotlib.pyplot as plt - -plt.text(0.6, 0.5, "test", size=50, rotation=30., - ha="center", va="center", - bbox=dict(boxstyle="round", - ec=(1., 0.5, 0.5), - fc=(1., 0.8, 0.8), - ) - ) - -plt.text(0.5, 0.4, "test", size=50, rotation=-30., - ha="right", va="top", - bbox=dict(boxstyle="square", - ec=(1., 0.5, 0.5), - fc=(1., 0.8, 0.8), - ) - ) - - -plt.draw() -plt.show() diff --git a/examples/pylab_examples/figimage_demo.py b/examples/pylab_examples/figimage_demo.py deleted file mode 100644 index 56c78d3bdbf1..000000000000 --- a/examples/pylab_examples/figimage_demo.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -This illustrates placing images directly in the figure, with no axes. - -""" -import numpy as np -import matplotlib -import matplotlib.cm as cm -import matplotlib.pyplot as plt - - -fig = plt.figure() -Z = np.arange(10000.0) -Z.shape = 100, 100 -Z[:, 50:] = 1. - -im1 = plt.figimage(Z, xo=50, yo=0, origin='lower') -im2 = plt.figimage(Z, xo=100, yo=100, alpha=.8, origin='lower') - -plt.show() diff --git a/examples/pylab_examples/figlegend_demo.py b/examples/pylab_examples/figlegend_demo.py deleted file mode 100644 index 8cda7bedd46f..000000000000 --- a/examples/pylab_examples/figlegend_demo.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -fig = plt.figure() -ax1 = fig.add_axes([0.1, 0.1, 0.4, 0.7]) -ax2 = fig.add_axes([0.55, 0.1, 0.4, 0.7]) - -x = np.arange(0.0, 2.0, 0.02) -y1 = np.sin(2*np.pi*x) -y2 = np.exp(-x) -l1, l2 = ax1.plot(x, y1, 'rs-', x, y2, 'go') - -y3 = np.sin(4*np.pi*x) -y4 = np.exp(-2*x) -l3, l4 = ax2.plot(x, y3, 'yd-', x, y4, 'k^') - -fig.legend((l1, l2), ('Line 1', 'Line 2'), 'upper left') -fig.legend((l3, l4), ('Line 3', 'Line 4'), 'upper right') -plt.show() diff --git a/examples/pylab_examples/figure_title.py b/examples/pylab_examples/figure_title.py deleted file mode 100644 index c01ff4986cf8..000000000000 --- a/examples/pylab_examples/figure_title.py +++ /dev/null @@ -1,31 +0,0 @@ -from matplotlib.font_manager import FontProperties -import matplotlib.pyplot as plt -import numpy as np - - -def f(t): - s1 = np.cos(2*np.pi*t) - e1 = np.exp(-t) - return s1 * e1 - -t1 = np.arange(0.0, 5.0, 0.1) -t2 = np.arange(0.0, 5.0, 0.02) -t3 = np.arange(0.0, 2.0, 0.01) - - -plt.subplot(121) -plt.plot(t1, f(t1), 'o', t2, f(t2), '-') -plt.title('subplot 1') -plt.ylabel('Damped oscillation') -plt.suptitle('This is a somewhat long figure title', fontsize=16) - - -plt.subplot(122) -plt.plot(t3, np.cos(2*np.pi*t3), '--') -plt.xlabel('time (s)') -plt.title('subplot 2') -plt.ylabel('Undamped') - -plt.subplots_adjust(left=0.2, wspace=0.8, top=0.8) - -plt.show() diff --git a/examples/pylab_examples/fill_between_demo.py b/examples/pylab_examples/fill_between_demo.py deleted file mode 100644 index b3653e456ce5..000000000000 --- a/examples/pylab_examples/fill_between_demo.py +++ /dev/null @@ -1,57 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(0.0, 2, 0.01) -y1 = np.sin(2*np.pi*x) -y2 = 1.2*np.sin(4*np.pi*x) - -fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True) - -ax1.fill_between(x, 0, y1) -ax1.set_ylabel('between y1 and 0') - -ax2.fill_between(x, y1, 1) -ax2.set_ylabel('between y1 and 1') - -ax3.fill_between(x, y1, y2) -ax3.set_ylabel('between y1 and y2') -ax3.set_xlabel('x') - -# now fill between y1 and y2 where a logical condition is met. Note -# this is different than calling -# fill_between(x[where], y1[where],y2[where] -# because of edge effects over multiple contiguous regions. -fig, (ax, ax1) = plt.subplots(2, 1, sharex=True) -ax.plot(x, y1, x, y2, color='black') -ax.fill_between(x, y1, y2, where=y2 >= y1, facecolor='green', interpolate=True) -ax.fill_between(x, y1, y2, where=y2 <= y1, facecolor='red', interpolate=True) -ax.set_title('fill between where') - -# Test support for masked arrays. -y2 = np.ma.masked_greater(y2, 1.0) -ax1.plot(x, y1, x, y2, color='black') -ax1.fill_between(x, y1, y2, where=y2 >= y1, facecolor='green', interpolate=True) -ax1.fill_between(x, y1, y2, where=y2 <= y1, facecolor='red', interpolate=True) -ax1.set_title('Now regions with y2>1 are masked') - -# This example illustrates a problem; because of the data -# gridding, there are undesired unfilled triangles at the crossover -# points. A brute-force solution would be to interpolate all -# arrays to a very fine grid before plotting. - -# show how to use transforms to create axes spans where a certain condition is satisfied -fig, ax = plt.subplots() -y = np.sin(4*np.pi*x) -ax.plot(x, y, color='black') - -# use the data coordinates for the x-axis and the axes coordinates for the y-axis -import matplotlib.transforms as mtransforms -trans = mtransforms.blended_transform_factory(ax.transData, ax.transAxes) -theta = 0.9 -ax.axhline(theta, color='green', lw=2, alpha=0.5) -ax.axhline(-theta, color='red', lw=2, alpha=0.5) -ax.fill_between(x, 0, 1, where=y > theta, facecolor='green', alpha=0.5, transform=trans) -ax.fill_between(x, 0, 1, where=y < -theta, facecolor='red', alpha=0.5, transform=trans) - - -plt.show() diff --git a/examples/pylab_examples/fill_betweenx_demo.py b/examples/pylab_examples/fill_betweenx_demo.py deleted file mode 100644 index 91812615e248..000000000000 --- a/examples/pylab_examples/fill_betweenx_demo.py +++ /dev/null @@ -1,50 +0,0 @@ -import matplotlib.mlab as mlab -from matplotlib.pyplot import figure, show -import numpy as np - - -y = np.arange(0.0, 2, 0.01) -x1 = np.sin(2*np.pi*y) -x2 = 1.2*np.sin(4*np.pi*y) - -fig = figure() -ax1 = fig.add_subplot(311) -ax2 = fig.add_subplot(312, sharex=ax1) -ax3 = fig.add_subplot(313, sharex=ax1) - -ax1.fill_betweenx(y, 0, x1) -ax1.set_ylabel('(x1, 0)') - -ax2.fill_betweenx(y, x1, 1) -ax2.set_ylabel('(x1, 1)') - -ax3.fill_betweenx(y, x1, x2) -ax3.set_ylabel('(x1, x2)') -ax3.set_xlabel('x') - -# now fill between x1 and x2 where a logical condition is met. Note -# this is different than calling -# fill_between(y[where], x1[where], x2[where]) -# because of edge effects over multiple contiguous regions. - -fig = figure() -ax = fig.add_subplot(211) -ax.plot(x1, y, x2, y, color='black') -ax.fill_betweenx(y, x1, x2, where=x2 >= x1, facecolor='green') -ax.fill_betweenx(y, x1, x2, where=x2 <= x1, facecolor='red') -ax.set_title('fill between where') - -# Test support for masked arrays. -x2 = np.ma.masked_greater(x2, 1.0) -ax1 = fig.add_subplot(212, sharex=ax) -ax1.plot(x1, y, x2, y, color='black') -ax1.fill_betweenx(y, x1, x2, where=x2 >= x1, facecolor='green') -ax1.fill_betweenx(y, x1, x2, where=x2 <= x1, facecolor='red') -ax1.set_title('Now regions with x2 > 1 are masked') - -# This example illustrates a problem; because of the data -# gridding, there are undesired unfilled triangles at the crossover -# points. A brute-force solution would be to interpolate all -# arrays to a very fine grid before plotting. - -show() diff --git a/examples/pylab_examples/font_table_ttf.py b/examples/pylab_examples/font_table_ttf.py deleted file mode 100755 index b59ca990bb33..000000000000 --- a/examples/pylab_examples/font_table_ttf.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- noplot -*- -""" -matplotlib has support for FreeType fonts. Here's a little example -using the 'table' command to build a font table that shows the glyphs -by character code. - -Usage python font_table_ttf.py somefile.ttf -""" - -import sys -import os - -import matplotlib -from matplotlib.ft2font import FT2Font -from matplotlib.font_manager import FontProperties -import matplotlib.pyplot as plt - -import six -from six import unichr - -# the font table grid - -labelc = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F'] -labelr = ['00', '10', '20', '30', '40', '50', '60', '70', '80', '90', - 'A0', 'B0', 'C0', 'D0', 'E0', 'F0'] - -if len(sys.argv) > 1: - fontname = sys.argv[1] -else: - fontname = os.path.join(matplotlib.get_data_path(), - 'fonts', 'ttf', 'DejaVuSans.ttf') - -font = FT2Font(fontname) -codes = list(font.get_charmap().items()) -codes.sort() - -# a 16,16 array of character strings -chars = [['' for c in range(16)] for r in range(16)] -colors = [[(0.95, 0.95, 0.95) for c in range(16)] for r in range(16)] - -plt.figure(figsize=(8, 4), dpi=120) -for ccode, glyphind in codes: - if ccode >= 256: - continue - r, c = divmod(ccode, 16) - s = unichr(ccode) - chars[r][c] = s - -lightgrn = (0.5, 0.8, 0.5) -plt.title(fontname) -tab = plt.table(cellText=chars, - rowLabels=labelr, - colLabels=labelc, - rowColours=[lightgrn]*16, - colColours=[lightgrn]*16, - cellColours=colors, - cellLoc='center', - loc='upper left') - -for key, cell in tab.get_celld().items(): - row, col = key - if row > 0 and col > 0: - cell.set_text_props(fontproperties=FontProperties(fname=fontname)) -plt.axis('off') -plt.show() diff --git a/examples/pylab_examples/fonts_demo.py b/examples/pylab_examples/fonts_demo.py deleted file mode 100644 index 5fbd40e8644b..000000000000 --- a/examples/pylab_examples/fonts_demo.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Show how to set custom font properties. - -For interactive users, you can also use kwargs to the text command, -which requires less typing. See examples/fonts_demo_kw.py -""" -from matplotlib.font_manager import FontProperties -import matplotlib.pyplot as plt - -plt.subplot(111, facecolor='w') - -font0 = FontProperties() -alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'} -# Show family options - -families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] - -font1 = font0.copy() -font1.set_size('large') - -t = plt.text(-0.8, 0.9, 'family', fontproperties=font1, - **alignment) - -yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2] - -for k, family in enumerate(families): - font = font0.copy() - font.set_family(family) - t = plt.text(-0.8, yp[k], family, fontproperties=font, - **alignment) - -# Show style options - -styles = ['normal', 'italic', 'oblique'] - -t = plt.text(-0.4, 0.9, 'style', fontproperties=font1, - **alignment) - -for k, style in enumerate(styles): - font = font0.copy() - font.set_family('sans-serif') - font.set_style(style) - t = plt.text(-0.4, yp[k], style, fontproperties=font, - **alignment) - -# Show variant options - -variants = ['normal', 'small-caps'] - -t = plt.text(0.0, 0.9, 'variant', fontproperties=font1, - **alignment) - -for k, variant in enumerate(variants): - font = font0.copy() - font.set_family('serif') - font.set_variant(variant) - t = plt.text(0.0, yp[k], variant, fontproperties=font, - **alignment) - -# Show weight options - -weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black'] - -t = plt.text(0.4, 0.9, 'weight', fontproperties=font1, - **alignment) - -for k, weight in enumerate(weights): - font = font0.copy() - font.set_weight(weight) - t = plt.text(0.4, yp[k], weight, fontproperties=font, - **alignment) - -# Show size options - -sizes = ['xx-small', 'x-small', 'small', 'medium', 'large', - 'x-large', 'xx-large'] - -t = plt.text(0.8, 0.9, 'size', fontproperties=font1, - **alignment) - -for k, size in enumerate(sizes): - font = font0.copy() - font.set_size(size) - t = plt.text(0.8, yp[k], size, fontproperties=font, - **alignment) - -# Show bold italic - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('x-small') -t = plt.text(-0.4, 0.1, 'bold italic', fontproperties=font, - **alignment) - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('medium') -t = plt.text(-0.4, 0.2, 'bold italic', fontproperties=font, - **alignment) - -font = font0.copy() -font.set_style('italic') -font.set_weight('bold') -font.set_size('x-large') -t = plt.text(-0.4, 0.3, 'bold italic', fontproperties=font, - **alignment) - -plt.axis([-1, 1, 0, 1]) - -plt.show() diff --git a/examples/pylab_examples/fonts_demo_kw.py b/examples/pylab_examples/fonts_demo_kw.py deleted file mode 100644 index 0beefd74a55a..000000000000 --- a/examples/pylab_examples/fonts_demo_kw.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Same as fonts_demo using kwargs. If you prefer a more pythonic, OO -style of coding, see examples/fonts_demo.py. - -""" -from matplotlib.font_manager import FontProperties -import matplotlib.pyplot as plt -import numpy as np - -plt.subplot(111, facecolor='w') -alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'} - -# Show family options - -families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'] - -t = plt.text(-0.8, 0.9, 'family', size='large', **alignment) - -yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2] - -for k, family in enumerate(families): - t = plt.text(-0.8, yp[k], family, family=family, **alignment) - -# Show style options - -styles = ['normal', 'italic', 'oblique'] - -t = plt.text(-0.4, 0.9, 'style', **alignment) - -for k, style in enumerate(styles): - t = plt.text(-0.4, yp[k], style, family='sans-serif', style=style, - **alignment) - -# Show variant options - -variants = ['normal', 'small-caps'] - -t = plt.text(0.0, 0.9, 'variant', **alignment) - -for k, variant in enumerate(variants): - t = plt.text(0.0, yp[k], variant, family='serif', variant=variant, - **alignment) - -# Show weight options - -weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black'] - -t = plt.text(0.4, 0.9, 'weight', **alignment) - -for k, weight in enumerate(weights): - t = plt.text(0.4, yp[k], weight, weight=weight, - **alignment) - -# Show size options - -sizes = ['xx-small', 'x-small', 'small', 'medium', 'large', - 'x-large', 'xx-large'] - -t = plt.text(0.8, 0.9, 'size', **alignment) - -for k, size in enumerate(sizes): - t = plt.text(0.8, yp[k], size, size=size, - **alignment) - -x = -0.4 -# Show bold italic -t = plt.text(x, 0.1, 'bold italic', style='italic', - weight='bold', size='x-small', - **alignment) - -t = plt.text(x, 0.2, 'bold italic', - style='italic', weight='bold', size='medium', - **alignment) - -t = plt.text(x, 0.3, 'bold italic', - style='italic', weight='bold', size='x-large', - **alignment) - -plt.axis([-1, 1, 0, 1]) - -plt.show() diff --git a/examples/pylab_examples/ganged_plots.py b/examples/pylab_examples/ganged_plots.py deleted file mode 100644 index 9d14a522c546..000000000000 --- a/examples/pylab_examples/ganged_plots.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -To create plots that share a common axes (visually) you can set the -hspace between the subplots close to zero (do not use zero itself). -Normally you'll want to turn off the tick labels on all but one of the -axes. - -In this example the plots share a common xaxis but you can follow the -same logic to supply a common y axis. -""" -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 2.0, 0.01) - -s1 = np.sin(2*np.pi*t) -s2 = np.exp(-t) -s3 = s1*s2 - -# axes rect in relative 0,1 coords left, bottom, width, height. Turn -# off xtick labels on all but the lower plot - - -f = plt.figure() -plt.subplots_adjust(hspace=0.001) - - -ax1 = plt.subplot(311) -ax1.plot(t, s1) -plt.yticks(np.arange(-0.9, 1.0, 0.4)) -plt.ylim(-1, 1) - -ax2 = plt.subplot(312, sharex=ax1) -ax2.plot(t, s2) -plt.yticks(np.arange(0.1, 1.0, 0.2)) -plt.ylim(0, 1) - -ax3 = plt.subplot(313, sharex=ax1) -ax3.plot(t, s3) -plt.yticks(np.arange(-0.9, 1.0, 0.4)) -plt.ylim(-1, 1) - -xticklabels = ax1.get_xticklabels() + ax2.get_xticklabels() -plt.setp(xticklabels, visible=False) - -plt.show() diff --git a/examples/pylab_examples/geo_demo.py b/examples/pylab_examples/geo_demo.py deleted file mode 100644 index 9ef96b2b564c..000000000000 --- a/examples/pylab_examples/geo_demo.py +++ /dev/null @@ -1,23 +0,0 @@ -import matplotlib.pyplot as plt - -plt.figure() -plt.subplot(111, projection="aitoff") -plt.title("Aitoff") -plt.grid(True) - -plt.figure() -plt.subplot(111, projection="hammer") -plt.title("Hammer") -plt.grid(True) - -plt.figure() -plt.subplot(111, projection="lambert") -plt.title("Lambert") -plt.grid(True) - -plt.figure() -plt.subplot(111, projection="mollweide") -plt.title("Mollweide") -plt.grid(True) - -plt.show() diff --git a/examples/pylab_examples/ginput_demo.py b/examples/pylab_examples/ginput_demo.py deleted file mode 100644 index f92f16917a71..000000000000 --- a/examples/pylab_examples/ginput_demo.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- noplot -*- - -from __future__ import print_function - -import matplotlib.pyplot as plt -import numpy as np -t = np.arange(10) -plt.plot(t, np.sin(t)) -print("Please click") -x = plt.ginput(3) -print("clicked", x) -plt.show() diff --git a/examples/pylab_examples/ginput_manual_clabel.py b/examples/pylab_examples/ginput_manual_clabel.py deleted file mode 100755 index 9cbf85cbde9b..000000000000 --- a/examples/pylab_examples/ginput_manual_clabel.py +++ /dev/null @@ -1,99 +0,0 @@ -# -*- noplot -*- - -from __future__ import print_function -""" -This provides examples of uses of interactive functions, such as ginput, -waitforbuttonpress and manual clabel placement. - -This script must be run interactively using a backend that has a -graphical user interface (for example, using GTKAgg backend, but not -PS backend). - -See also ginput_demo.py -""" -import time -import matplotlib -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt - - -def tellme(s): - print(s) - plt.title(s, fontsize=16) - plt.draw() - -################################################## -# Define a triangle by clicking three points -################################################## -plt.clf() -plt.axis([-1., 1., -1., 1.]) -plt.setp(plt.gca(), autoscale_on=False) - -tellme('You will define a triangle, click to begin') - -plt.waitforbuttonpress() - -happy = False -while not happy: - pts = [] - while len(pts) < 3: - tellme('Select 3 corners with mouse') - pts = np.asarray(plt.ginput(3, timeout=-1)) - if len(pts) < 3: - tellme('Too few points, starting over') - time.sleep(1) # Wait a second - - ph = plt.fill(pts[:, 0], pts[:, 1], 'r', lw=2) - - tellme('Happy? Key click for yes, mouse click for no') - - happy = plt.waitforbuttonpress() - - # Get rid of fill - if not happy: - for p in ph: - p.remove() - -################################################## -# Now contour according to distance from triangle -# corners - just an example -################################################## - - -# Define a nice function of distance from individual pts -def f(x, y, pts): - z = np.zeros_like(x) - for p in pts: - z = z + 1/(np.sqrt((x - p[0])**2 + (y - p[1])**2)) - return 1/z - -X, Y = np.meshgrid(np.linspace(-1, 1, 51), np.linspace(-1, 1, 51)) -Z = f(X, Y, pts) - -CS = plt.contour(X, Y, Z, 20) - -tellme('Use mouse to select contour label locations, middle button to finish') -CL = plt.clabel(CS, manual=True) - -################################################## -# Now do a zoom -################################################## -tellme('Now do a nested zoom, click to begin') -plt.waitforbuttonpress() - -happy = False -while not happy: - tellme('Select two corners of zoom, middle mouse button to finish') - pts = np.asarray(plt.ginput(2, timeout=-1)) - - happy = len(pts) < 2 - if happy: - break - - pts = np.sort(pts, axis=0) - plt.axis(pts.T.ravel()) - -tellme('All Done!') -plt.show() diff --git a/examples/pylab_examples/gradient_bar.py b/examples/pylab_examples/gradient_bar.py deleted file mode 100644 index 714f45f5ac96..000000000000 --- a/examples/pylab_examples/gradient_bar.py +++ /dev/null @@ -1,29 +0,0 @@ -from matplotlib.pyplot import figure, show, cm -from numpy import arange -from numpy.random import rand - - -def gbar(ax, x, y, width=0.5, bottom=0): - X = [[.6, .6], [.7, .7]] - for left, top in zip(x, y): - right = left + width - ax.imshow(X, interpolation='bicubic', cmap=cm.Blues, - extent=(left, right, bottom, top), alpha=1) - -fig = figure() - -xmin, xmax = xlim = 0, 10 -ymin, ymax = ylim = 0, 1 -ax = fig.add_subplot(111, xlim=xlim, ylim=ylim, - autoscale_on=False) -X = [[.6, .6], [.7, .7]] - -ax.imshow(X, interpolation='bicubic', cmap=cm.copper, - extent=(xmin, xmax, ymin, ymax), alpha=1) - -N = 10 -x = arange(N) + 0.25 -y = rand(N) -gbar(ax, x, y, width=0.7) -ax.set_aspect('auto') -show() diff --git a/examples/pylab_examples/griddata_demo.py b/examples/pylab_examples/griddata_demo.py deleted file mode 100644 index 74af8424fb83..000000000000 --- a/examples/pylab_examples/griddata_demo.py +++ /dev/null @@ -1,27 +0,0 @@ -from numpy.random import uniform, seed -from matplotlib.mlab import griddata -import matplotlib.pyplot as plt -import numpy as np -# make up data. -#npts = int(raw_input('enter # of random points to plot:')) -seed(0) -npts = 200 -x = uniform(-2, 2, npts) -y = uniform(-2, 2, npts) -z = x*np.exp(-x**2 - y**2) -# define grid. -xi = np.linspace(-2.1, 2.1, 100) -yi = np.linspace(-2.1, 2.1, 200) -# grid the data. -zi = griddata(x, y, z, xi, yi, interp='linear') -# contour the gridded data, plotting dots at the nonuniform data points. -CS = plt.contour(xi, yi, zi, 15, linewidths=0.5, colors='k') -CS = plt.contourf(xi, yi, zi, 15, - vmax=abs(zi).max(), vmin=-abs(zi).max()) -plt.colorbar() # draw colorbar -# plot data points. -plt.scatter(x, y, marker='o', s=5, zorder=10) -plt.xlim(-2, 2) -plt.ylim(-2, 2) -plt.title('griddata test (%d points)' % npts) -plt.show() diff --git a/examples/pylab_examples/hatch_demo.py b/examples/pylab_examples/hatch_demo.py deleted file mode 100644 index 0488e3fb2000..000000000000 --- a/examples/pylab_examples/hatch_demo.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Hatching (pattern filled polygons) is supported currently in the PS, -PDF, SVG and Agg backends only. -""" -import matplotlib.pyplot as plt -from matplotlib.patches import Ellipse, Polygon - -fig = plt.figure() -ax1 = fig.add_subplot(131) -ax1.bar(range(1, 5), range(1, 5), color='red', edgecolor='black', hatch="/") -ax1.bar(range(1, 5), [6] * 4, bottom=range(1, 5), color='blue', edgecolor='black', hatch='//') -ax1.set_xticks([1.5, 2.5, 3.5, 4.5]) - -ax2 = fig.add_subplot(132) -bars = ax2.bar(range(1, 5), range(1, 5), color='yellow', ecolor='black') + \ - ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5), color='green', ecolor='black') -ax2.set_xticks([1.5, 2.5, 3.5, 4.5]) - -patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.') -for bar, pattern in zip(bars, patterns): - bar.set_hatch(pattern) - -ax3 = fig.add_subplot(133) -ax3.fill([1, 3, 3, 1], [1, 1, 2, 2], fill=False, hatch='\\') -ax3.add_patch(Ellipse((4, 1.5), 4, 0.5, fill=False, hatch='*')) -ax3.add_patch(Polygon([[0, 0], [4, 1.1], [6, 2.5], [2, 1.4]], closed=True, - fill=False, hatch='/')) -ax3.set_xlim((0, 6)) -ax3.set_ylim((0, 2.5)) - -plt.show() diff --git a/examples/pylab_examples/hexbin_demo.py b/examples/pylab_examples/hexbin_demo.py deleted file mode 100644 index 0d8bc7a31301..000000000000 --- a/examples/pylab_examples/hexbin_demo.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -hexbin is an axes method or pyplot function that is essentially -a pcolor of a 2-D histogram with hexagonal cells. It can be -much more informative than a scatter plot; in the first subplot -below, try substituting 'scatter' for 'hexbin'. -""" - -import numpy as np -import matplotlib.pyplot as plt - -np.random.seed(0) -n = 100000 -x = np.random.standard_normal(n) -y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n) -xmin = x.min() -xmax = x.max() -ymin = y.min() -ymax = y.max() - -fig, axs = plt.subplots(ncols=2, sharey=True, figsize=(7, 4)) -fig.subplots_adjust(hspace=0.5, left=0.07, right=0.93) -ax = axs[0] -hb = ax.hexbin(x, y, gridsize=50, cmap='inferno') -ax.axis([xmin, xmax, ymin, ymax]) -ax.set_title("Hexagon binning") -cb = fig.colorbar(hb, ax=ax) -cb.set_label('counts') - -ax = axs[1] -hb = ax.hexbin(x, y, gridsize=50, bins='log', cmap='inferno') -ax.axis([xmin, xmax, ymin, ymax]) -ax.set_title("With a log color scale") -cb = fig.colorbar(hb, ax=ax) -cb.set_label('log10(N)') - -plt.show() diff --git a/examples/pylab_examples/hexbin_demo2.py b/examples/pylab_examples/hexbin_demo2.py deleted file mode 100644 index 7bcdf97549e5..000000000000 --- a/examples/pylab_examples/hexbin_demo2.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -hexbin is an axes method or pyplot function that is essentially a -pcolor of a 2-D histogram with hexagonal cells. -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab - -delta = 0.025 -x = y = np.arange(-3.0, 3.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = Z2 - Z1 # difference of Gaussians - -x = X.ravel() -y = Y.ravel() -z = Z.ravel() - -if 1: - # make some points 20 times more common than others, but same mean - xcond = (-1 < x) & (x < 1) - ycond = (-2 < y) & (y < 0) - cond = xcond & ycond - xnew = x[cond] - ynew = y[cond] - znew = z[cond] - for i in range(20): - x = np.hstack((x, xnew)) - y = np.hstack((y, ynew)) - z = np.hstack((z, znew)) - -xmin = x.min() -xmax = x.max() -ymin = y.min() -ymax = y.max() - -gridsize = 30 - -plt.subplot(211) -plt.hexbin(x, y, C=z, gridsize=gridsize, marginals=True, cmap=plt.cm.RdBu, - vmax=abs(z).max(), vmin=-abs(z).max()) -plt.axis([xmin, xmax, ymin, ymax]) -cb = plt.colorbar() -cb.set_label('mean value') - - -plt.subplot(212) -plt.hexbin(x, y, gridsize=gridsize, cmap=plt.cm.Blues_r) -plt.axis([xmin, xmax, ymin, ymax]) -cb = plt.colorbar() -cb.set_label('N observations') - -plt.show() diff --git a/examples/pylab_examples/hist2d_demo.py b/examples/pylab_examples/hist2d_demo.py deleted file mode 100644 index f8f0e61cbe92..000000000000 --- a/examples/pylab_examples/hist2d_demo.py +++ /dev/null @@ -1,8 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -x = np.random.randn(1000) -y = np.random.randn(1000) + 5 - -# normal distribution center at x=0 and y=5 -plt.hist2d(x, y, bins=40) -plt.show() diff --git a/examples/pylab_examples/hist2d_log_demo.py b/examples/pylab_examples/hist2d_log_demo.py deleted file mode 100644 index 48936b8bdb0b..000000000000 --- a/examples/pylab_examples/hist2d_log_demo.py +++ /dev/null @@ -1,11 +0,0 @@ -from matplotlib.colors import LogNorm -import matplotlib.pyplot as plt -import numpy as np - -# normal distribution center at x=0 and y=5 -x = np.random.randn(100000) -y = np.random.randn(100000) + 5 - -plt.hist2d(x, y, bins=40, norm=LogNorm()) -plt.colorbar() -plt.show() diff --git a/examples/pylab_examples/hist_colormapped.py b/examples/pylab_examples/hist_colormapped.py deleted file mode 100644 index 3af198e2d09f..000000000000 --- a/examples/pylab_examples/hist_colormapped.py +++ /dev/null @@ -1,23 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cm as cm -import matplotlib.colors as colors - -fig, ax = plt.subplots() -Ntotal = 1000 -N, bins, patches = ax.hist(np.random.rand(Ntotal), 20) - -# I'll color code by height, but you could use any scalar - - -# we need to normalize the data to 0..1 for the full -# range of the colormap -fracs = N.astype(float)/N.max() -norm = colors.Normalize(fracs.min(), fracs.max()) - -for thisfrac, thispatch in zip(fracs, patches): - color = cm.viridis(norm(thisfrac)) - thispatch.set_facecolor(color) - - -plt.show() diff --git a/examples/pylab_examples/histogram_percent_demo.py b/examples/pylab_examples/histogram_percent_demo.py deleted file mode 100644 index 9d7b5d34423f..000000000000 --- a/examples/pylab_examples/histogram_percent_demo.py +++ /dev/null @@ -1,30 +0,0 @@ -import matplotlib -from numpy.random import randn -import matplotlib.pyplot as plt -from matplotlib.ticker import FuncFormatter - - -def to_percent(y, position): - # Ignore the passed in position. This has the effect of scaling the default - # tick locations. - s = str(100 * y) - - # The percent symbol needs escaping in latex - if matplotlib.rcParams['text.usetex'] is True: - return s + r'$\%$' - else: - return s + '%' - -x = randn(5000) - -# Make a normed histogram. It'll be multiplied by 100 later. -plt.hist(x, bins=50, normed=True) - -# Create the formatter using the function to_percent. This multiplies all the -# default labels by 100, making them all percentages -formatter = FuncFormatter(to_percent) - -# Set the formatter -plt.gca().yaxis.set_major_formatter(formatter) - -plt.show() diff --git a/examples/pylab_examples/hyperlinks.py b/examples/pylab_examples/hyperlinks.py deleted file mode 100644 index 599f637b88f7..000000000000 --- a/examples/pylab_examples/hyperlinks.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- noplot -*- - -""" -This example demonstrates how to set a hyperlinks on various kinds of elements. - -This currently only works with the SVG backend. -""" - -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt - -f = plt.figure() -s = plt.scatter([1, 2, 3], [4, 5, 6]) -s.set_urls(['http://www.bbc.co.uk/news', 'http://www.google.com', None]) -f.canvas.print_figure('scatter.svg') - -f = plt.figure() -delta = 0.025 -x = y = np.arange(-3.0, 3.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = Z2 - Z1 # difference of Gaussians - -im = plt.imshow(Z, interpolation='bilinear', cmap=cm.gray, - origin='lower', extent=[-3, 3, -3, 3]) - -im.set_url('http://www.google.com') -f.canvas.print_figure('image.svg') diff --git a/examples/pylab_examples/image_clip_path.py b/examples/pylab_examples/image_clip_path.py deleted file mode 100755 index fbcb0870d75c..000000000000 --- a/examples/pylab_examples/image_clip_path.py +++ /dev/null @@ -1,24 +0,0 @@ -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt -from matplotlib.path import Path -from matplotlib.patches import PathPatch - -delta = 0.025 -x = y = np.arange(-3.0, 3.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = Z2 - Z1 # difference of Gaussians - -path = Path([[0, 1], [1, 0], [0, -1], [-1, 0], [0, 1]]) -patch = PathPatch(path, facecolor='none') -plt.gca().add_patch(patch) - -im = plt.imshow(Z, interpolation='bilinear', cmap=cm.gray, - origin='lower', extent=[-3, 3, -3, 3], - clip_path=patch, clip_on=True) -im.set_clip_path(patch) - -plt.show() diff --git a/examples/pylab_examples/image_demo.py b/examples/pylab_examples/image_demo.py deleted file mode 100644 index a189ef2ce3eb..000000000000 --- a/examples/pylab_examples/image_demo.py +++ /dev/null @@ -1,17 +0,0 @@ -import numpy as np -import matplotlib.cm as cm -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt - -delta = 0.025 -x = y = np.arange(-3.0, 3.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = Z2 - Z1 # difference of Gaussians - -im = plt.imshow(Z, interpolation='bilinear', cmap=cm.RdYlGn, - origin='lower', extent=[-3, 3, -3, 3], - vmax=abs(Z).max(), vmin=-abs(Z).max()) - -plt.show() diff --git a/examples/pylab_examples/image_demo2.py b/examples/pylab_examples/image_demo2.py deleted file mode 100755 index 998e383d2181..000000000000 --- a/examples/pylab_examples/image_demo2.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import print_function -import matplotlib.pyplot as plt -import numpy as np -import matplotlib.cbook as cbook - -w, h = 512, 512 - -datafile = cbook.get_sample_data('ct.raw.gz', asfileobj=True) -s = datafile.read() -A = np.fromstring(s, np.uint16).astype(float) -A *= 1.0 / max(A) -A.shape = w, h - -extent = (0, 25, 0, 25) -im = plt.imshow(A, cmap=plt.cm.hot, origin='upper', extent=extent) - -markers = [(15.9, 14.5), (16.8, 15)] -x, y = zip(*markers) -plt.plot(x, y, 'o') - -plt.title('CT density') - -plt.show() diff --git a/examples/pylab_examples/image_interp.py b/examples/pylab_examples/image_interp.py deleted file mode 100644 index 3ad6a5f37d63..000000000000 --- a/examples/pylab_examples/image_interp.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -The same (small) array, interpolated with three different -interpolation methods. - -The center of the pixel at A[i,j] is plotted at i+0.5, i+0.5. If you -are using interpolation='nearest', the region bounded by (i,j) and -(i+1,j+1) will have the same color. If you are using interpolation, -the pixel center will have the same color as it does with nearest, but -other pixels will be interpolated between the neighboring pixels. - -Earlier versions of matplotlib (<0.63) tried to hide the edge effects -from you by setting the view limits so that they would not be visible. -A recent bugfix in antigrain, and a new implementation in the -matplotlib._image module which takes advantage of this fix, no longer -makes this necessary. To prevent edge effects, when doing -interpolation, the matplotlib._image module now pads the input array -with identical pixels around the edge. e.g., if you have a 5x5 array -with colors a-y as below - - - a b c d e - f g h i j - k l m n o - p q r s t - u v w x y - -the _image module creates the padded array, - - a a b c d e e - a a b c d e e - f f g h i j j - k k l m n o o - p p q r s t t - o u v w x y y - o u v w x y y - -does the interpolation/resizing, and then extracts the central region. -This allows you to plot the full range of your array w/o edge effects, -and for example to layer multiple images of different sizes over one -another with different interpolation methods - see -examples/layer_images.py. It also implies a performance hit, as this -new temporary, padded array must be created. Sophisticated -interpolation also implies a performance hit, so if you need maximal -performance or have very large images, interpolation='nearest' is -suggested. - -""" -import matplotlib.pyplot as plt -import numpy as np - -A = np.random.rand(5, 5) -plt.figure(1) -plt.imshow(A, interpolation='nearest') -plt.grid(True) - -plt.figure(2) -plt.imshow(A, interpolation='bilinear') -plt.grid(True) - -plt.figure(3) -plt.imshow(A, interpolation='bicubic') -plt.grid(True) - -plt.show() diff --git a/examples/pylab_examples/image_masked.py b/examples/pylab_examples/image_masked.py deleted file mode 100644 index af38a13f3ae3..000000000000 --- a/examples/pylab_examples/image_masked.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -imshow with masked array input and out-of-range colors. - -The second subplot illustrates the use of BoundaryNorm to -get a filled contour effect. -""" -from copy import copy -from numpy import ma -import matplotlib.colors as colors -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab -import numpy as np - -# compute some interesting data -delta = 0.025 -x = y = np.arange(-3.0, 3.0, delta) -X, Y = np.meshgrid(x, y) -Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) -Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) -Z = 10*(Z2 - Z1) # difference of Gaussians - -# Set up a colormap: -# use copy so that we do not mutate the global colormap instance -palette = copy(plt.cm.gray) -palette.set_over('r', 1.0) -palette.set_under('g', 1.0) -palette.set_bad('b', 1.0) -# Alternatively, we could use -# palette.set_bad(alpha = 0.0) -# to make the bad region transparent. This is the default. -# If you comment out all the palette.set* lines, you will see -# all the defaults; under and over will be colored with the -# first and last colors in the palette, respectively. -Zm = ma.masked_where(Z > 1.2, Z) - -# By setting vmin and vmax in the norm, we establish the -# range to which the regular palette color scale is applied. -# Anything above that range is colored based on palette.set_over, etc. - -# set up the axes -fig, (ax1, ax2) = plt.subplots(1, 2) - -# plot using 'continuous' color map -im = ax1.imshow(Zm, interpolation='bilinear', - cmap=palette, - norm=colors.Normalize(vmin=-1.0, vmax=1.0, clip=False), - origin='lower', extent=[-3, 3, -3, 3]) -ax1.set_title('Green=low, Red=high, Blue=bad') -fig.colorbar(im, extend='both', orientation='horizontal', shrink=0.8, ax=ax1) - -# plot using 'discrete' color map -im = ax2.imshow(Zm, interpolation='nearest', - cmap=palette, - norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1], - ncolors=256, clip=False), - origin='lower', extent=[-3, 3, -3, 3]) -ax2.set_title('With BoundaryNorm') -fig.colorbar(im, extend='both', spacing='proportional', - orientation='horizontal', shrink=0.8, ax=ax2) - -plt.show() diff --git a/examples/pylab_examples/image_nonuniform.py b/examples/pylab_examples/image_nonuniform.py deleted file mode 100644 index 7a222dc800b4..000000000000 --- a/examples/pylab_examples/image_nonuniform.py +++ /dev/null @@ -1,65 +0,0 @@ -''' -This illustrates the NonUniformImage class. It is not -available via an Axes method but it is easily added to an -Axes instance as shown here. -''' - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.image import NonUniformImage -from matplotlib import cm - -interp = 'nearest' - -# Linear x array for cell centers: -x = np.linspace(-4, 4, 9) - -# Highly nonlinear x array: -x2 = x**3 - -y = np.linspace(-4, 4, 9) - -z = np.sqrt(x[np.newaxis, :]**2 + y[:, np.newaxis]**2) - -fig, axs = plt.subplots(nrows=2, ncols=2) -fig.subplots_adjust(bottom=0.07, hspace=0.3) -fig.suptitle('NonUniformImage class', fontsize='large') -ax = axs[0, 0] -im = NonUniformImage(ax, interpolation=interp, extent=(-4, 4, -4, 4), - cmap=cm.Purples) -im.set_data(x, y, z) -ax.images.append(im) -ax.set_xlim(-4, 4) -ax.set_ylim(-4, 4) -ax.set_title(interp) - -ax = axs[0, 1] -im = NonUniformImage(ax, interpolation=interp, extent=(-64, 64, -4, 4), - cmap=cm.Purples) -im.set_data(x2, y, z) -ax.images.append(im) -ax.set_xlim(-64, 64) -ax.set_ylim(-4, 4) -ax.set_title(interp) - -interp = 'bilinear' - -ax = axs[1, 0] -im = NonUniformImage(ax, interpolation=interp, extent=(-4, 4, -4, 4), - cmap=cm.Purples) -im.set_data(x, y, z) -ax.images.append(im) -ax.set_xlim(-4, 4) -ax.set_ylim(-4, 4) -ax.set_title(interp) - -ax = axs[1, 1] -im = NonUniformImage(ax, interpolation=interp, extent=(-64, 64, -4, 4), - cmap=cm.Purples) -im.set_data(x2, y, z) -ax.images.append(im) -ax.set_xlim(-64, 64) -ax.set_ylim(-4, 4) -ax.set_title(interp) - -plt.show() diff --git a/examples/pylab_examples/image_origin.py b/examples/pylab_examples/image_origin.py deleted file mode 100755 index 0e282dd7fb65..000000000000 --- a/examples/pylab_examples/image_origin.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -You can specify whether images should be plotted with the array origin -x[0,0] in the upper left or upper right by using the origin parameter. -You can also control the default be setting image.origin in your -matplotlibrc file; see http://matplotlib.org/matplotlibrc -""" -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(120) -x.shape = (10, 12) - -interp = 'bilinear' -fig, axs = plt.subplots(nrows=2, sharex=True, figsize=(3, 5)) -axs[0].set_title('blue should be up') -axs[0].imshow(x, origin='upper', interpolation=interp) - -axs[1].set_title('blue should be down') -axs[1].imshow(x, origin='lower', interpolation=interp) -plt.show() diff --git a/examples/pylab_examples/image_slices_viewer.py b/examples/pylab_examples/image_slices_viewer.py deleted file mode 100644 index 24affd3deee4..000000000000 --- a/examples/pylab_examples/image_slices_viewer.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import print_function -import numpy -import matplotlib.pyplot as plt - - -class IndexTracker(object): - def __init__(self, ax, X): - self.ax = ax - ax.set_title('use scroll wheel to navigate images') - - self.X = X - rows, cols, self.slices = X.shape - self.ind = self.slices//2 - - self.im = ax.imshow(self.X[:, :, self.ind]) - self.update() - - def onscroll(self, event): - print("%s %s" % (event.button, event.step)) - if event.button == 'up': - self.ind = numpy.clip(self.ind + 1, 0, self.slices - 1) - else: - self.ind = numpy.clip(self.ind - 1, 0, self.slices - 1) - self.update() - - def update(self): - self.im.set_data(self.X[:, :, self.ind]) - ax.set_ylabel('slice %s' % self.ind) - self.im.axes.figure.canvas.draw() - - -fig, ax = plt.subplots(1, 1) - -X = numpy.random.rand(20, 20, 40) - -tracker = IndexTracker(ax, X) - - -fig.canvas.mpl_connect('scroll_event', tracker.onscroll) -plt.show() diff --git a/examples/pylab_examples/interp_demo.py b/examples/pylab_examples/interp_demo.py deleted file mode 100644 index 9b7c4639eee8..000000000000 --- a/examples/pylab_examples/interp_demo.py +++ /dev/null @@ -1,13 +0,0 @@ -import matplotlib.pyplot as plt -from numpy import pi, sin, linspace -from matplotlib.mlab import stineman_interp - -x = linspace(0, 2*pi, 20) -y = sin(x) -yp = None -xi = linspace(x[0], x[-1], 100) -yi = stineman_interp(xi, x, y, yp) - -fig, ax = plt.subplots() -ax.plot(x, y, 'o', xi, yi, '.') -plt.show() diff --git a/examples/pylab_examples/invert_axes.py b/examples/pylab_examples/invert_axes.py deleted file mode 100644 index 66dc6b41971c..000000000000 --- a/examples/pylab_examples/invert_axes.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -You can use decreasing axes by flipping the normal order of the axis -limits -""" - -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.01, 5.0, 0.01) -s = np.exp(-t) -plt.plot(t, s) - -plt.xlim(5, 0) # decreasing time - -plt.xlabel('decreasing time (s)') -plt.ylabel('voltage (mV)') -plt.title('Should be growing...') -plt.grid(True) - -plt.show() diff --git a/examples/pylab_examples/layer_images.py b/examples/pylab_examples/layer_images.py deleted file mode 100644 index a862ac045163..000000000000 --- a/examples/pylab_examples/layer_images.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Layer images above one another using alpha blending -""" -from __future__ import division -import matplotlib.pyplot as plt -import numpy as np - - -def func3(x, y): - return (1 - x/2 + x**5 + y**3)*np.exp(-(x**2 + y**2)) - -# make these smaller to increase the resolution -dx, dy = 0.05, 0.05 - -x = np.arange(-3.0, 3.0, dx) -y = np.arange(-3.0, 3.0, dy) -X, Y = np.meshgrid(x, y) - -# when layering multiple images, the images need to have the same -# extent. This does not mean they need to have the same shape, but -# they both need to render to the same coordinate system determined by -# xmin, xmax, ymin, ymax. Note if you use different interpolations -# for the images their apparent extent could be different due to -# interpolation edge effects - - -xmin, xmax, ymin, ymax = np.amin(x), np.amax(x), np.amin(y), np.amax(y) -extent = xmin, xmax, ymin, ymax -fig = plt.figure(frameon=False) - -Z1 = np.array(([0, 1]*4 + [1, 0]*4)*4) -Z1.shape = (8, 8) # chessboard -im1 = plt.imshow(Z1, cmap=plt.cm.gray, interpolation='nearest', - extent=extent) -plt.hold(True) - -Z2 = func3(X, Y) - -im2 = plt.imshow(Z2, cmap=plt.cm.viridis, alpha=.9, interpolation='bilinear', - extent=extent) - -plt.show() diff --git a/examples/pylab_examples/leftventricle_bulleye.py b/examples/pylab_examples/leftventricle_bulleye.py deleted file mode 100644 index e96a4cc8e7f8..000000000000 --- a/examples/pylab_examples/leftventricle_bulleye.py +++ /dev/null @@ -1,206 +0,0 @@ -""" -This example demonstrates how to create the 17 segment model for the left -ventricle recommended by the American Heart Association (AHA). -""" - -import numpy as np -import matplotlib as mpl -import matplotlib.pyplot as plt - - -def bullseye_plot(ax, data, segBold=None, cmap=None, norm=None): - """ - Bullseye representation for the left ventricle. - - Parameters - ---------- - ax : axes - data : list of int and float - The intensity values for each of the 17 segments - segBold: list of int, optional - A list with the segments to highlight - cmap : ColorMap or None, optional - Optional argument to set the desired colormap - norm : Normalize or None, optional - Optional argument to normalize data into the [0.0, 1.0] range - - - Notes - ----- - This function create the 17 segment model for the left ventricle according - to the American Heart Association (AHA) [1]_ - - References - ---------- - .. [1] M. D. Cerqueira, N. J. Weissman, V. Dilsizian, A. K. Jacobs, - S. Kaul, W. K. Laskey, D. J. Pennell, J. A. Rumberger, T. Ryan, - and M. S. Verani, "Standardized myocardial segmentation and - nomenclature for tomographic imaging of the heart", - Circulation, vol. 105, no. 4, pp. 539-542, 2002. - """ - if segBold is None: - segBold = [] - - linewidth = 2 - data = np.array(data).ravel() - - if cmap is None: - cmap = plt.cm.viridis - - if norm is None: - norm = mpl.colors.Normalize(vmin=data.min(), vmax=data.max()) - - theta = np.linspace(0, 2*np.pi, 768) - r = np.linspace(0.2, 1, 4) - - # Create the bound for the segment 17 - for i in range(r.shape[0]): - ax.plot(theta, np.repeat(r[i], theta.shape), '-k', lw=linewidth) - - # Create the bounds for the segments 1-12 - for i in range(6): - theta_i = i*60*np.pi/180 - ax.plot([theta_i, theta_i], [r[1], 1], '-k', lw=linewidth) - - # Create the bounds for the segmentss 13-16 - for i in range(4): - theta_i = i*90*np.pi/180 - 45*np.pi/180 - ax.plot([theta_i, theta_i], [r[0], r[1]], '-k', lw=linewidth) - - # Fill the segments 1-6 - r0 = r[2:4] - r0 = np.repeat(r0[:, np.newaxis], 128, axis=1).T - for i in range(6): - # First segment start at 60 degrees - theta0 = theta[i*128:i*128+128] + 60*np.pi/180 - theta0 = np.repeat(theta0[:, np.newaxis], 2, axis=1) - z = np.ones((128, 2))*data[i] - ax.pcolormesh(theta0, r0, z, cmap=cmap, norm=norm) - if i+1 in segBold: - ax.plot(theta0, r0, '-k', lw=linewidth+2) - ax.plot(theta0[0], [r[2], r[3]], '-k', lw=linewidth+1) - ax.plot(theta0[-1], [r[2], r[3]], '-k', lw=linewidth+1) - - # Fill the segments 7-12 - r0 = r[1:3] - r0 = np.repeat(r0[:, np.newaxis], 128, axis=1).T - for i in range(6): - # First segment start at 60 degrees - theta0 = theta[i*128:i*128+128] + 60*np.pi/180 - theta0 = np.repeat(theta0[:, np.newaxis], 2, axis=1) - z = np.ones((128, 2))*data[i+6] - ax.pcolormesh(theta0, r0, z, cmap=cmap, norm=norm) - if i+7 in segBold: - ax.plot(theta0, r0, '-k', lw=linewidth+2) - ax.plot(theta0[0], [r[1], r[2]], '-k', lw=linewidth+1) - ax.plot(theta0[-1], [r[1], r[2]], '-k', lw=linewidth+1) - - # Fill the segments 13-16 - r0 = r[0:2] - r0 = np.repeat(r0[:, np.newaxis], 192, axis=1).T - for i in range(4): - # First segment start at 45 degrees - theta0 = theta[i*192:i*192+192] + 45*np.pi/180 - theta0 = np.repeat(theta0[:, np.newaxis], 2, axis=1) - z = np.ones((192, 2))*data[i+12] - ax.pcolormesh(theta0, r0, z, cmap=cmap, norm=norm) - if i+13 in segBold: - ax.plot(theta0, r0, '-k', lw=linewidth+2) - ax.plot(theta0[0], [r[0], r[1]], '-k', lw=linewidth+1) - ax.plot(theta0[-1], [r[0], r[1]], '-k', lw=linewidth+1) - - # Fill the segments 17 - if data.size == 17: - r0 = np.array([0, r[0]]) - r0 = np.repeat(r0[:, np.newaxis], theta.size, axis=1).T - theta0 = np.repeat(theta[:, np.newaxis], 2, axis=1) - z = np.ones((theta.size, 2))*data[16] - ax.pcolormesh(theta0, r0, z, cmap=cmap, norm=norm) - if 17 in segBold: - ax.plot(theta0, r0, '-k', lw=linewidth+2) - - ax.set_ylim([0, 1]) - ax.set_yticklabels([]) - ax.set_xticklabels([]) - - -# Create the fake data -data = np.array(range(17)) + 1 - - -# Make a figure and axes with dimensions as desired. -fig, ax = plt.subplots(figsize=(12, 8), nrows=1, ncols=3, - subplot_kw=dict(projection='polar')) -fig.canvas.set_window_title('Left Ventricle Bulls Eyes (AHA)') - -# Create the axis for the colorbars -axl = fig.add_axes([0.14, 0.15, 0.2, 0.05]) -axl2 = fig.add_axes([0.41, 0.15, 0.2, 0.05]) -axl3 = fig.add_axes([0.69, 0.15, 0.2, 0.05]) - - -# Set the colormap and norm to correspond to the data for which -# the colorbar will be used. -cmap = mpl.cm.viridis -norm = mpl.colors.Normalize(vmin=1, vmax=17) - -# ColorbarBase derives from ScalarMappable and puts a colorbar -# in a specified axes, so it has everything needed for a -# standalone colorbar. There are many more kwargs, but the -# following gives a basic continuous colorbar with ticks -# and labels. -cb1 = mpl.colorbar.ColorbarBase(axl, cmap=cmap, norm=norm, - orientation='horizontal') -cb1.set_label('Some Units') - - -# Set the colormap and norm to correspond to the data for which -# the colorbar will be used. -cmap2 = mpl.cm.cool -norm2 = mpl.colors.Normalize(vmin=1, vmax=17) - -# ColorbarBase derives from ScalarMappable and puts a colorbar -# in a specified axes, so it has everything needed for a -# standalone colorbar. There are many more kwargs, but the -# following gives a basic continuous colorbar with ticks -# and labels. -cb2 = mpl.colorbar.ColorbarBase(axl2, cmap=cmap2, norm=norm2, - orientation='horizontal') -cb2.set_label('Some other units') - - -# The second example illustrates the use of a ListedColormap, a -# BoundaryNorm, and extended ends to show the "over" and "under" -# value colors. -cmap3 = mpl.colors.ListedColormap(['r', 'g', 'b', 'c']) -cmap3.set_over('0.35') -cmap3.set_under('0.75') - -# If a ListedColormap is used, the length of the bounds array must be -# one greater than the length of the color list. The bounds must be -# monotonically increasing. -bounds = [2, 3, 7, 9, 15] -norm3 = mpl.colors.BoundaryNorm(bounds, cmap3.N) -cb3 = mpl.colorbar.ColorbarBase(axl3, cmap=cmap3, norm=norm3, - # to use 'extend', you must - # specify two extra boundaries: - boundaries=[0]+bounds+[18], - extend='both', - ticks=bounds, # optional - spacing='proportional', - orientation='horizontal') -cb3.set_label('Discrete intervals, some other units') - - -# Create the 17 segment model -bullseye_plot(ax[0], data, cmap=cmap, norm=norm) -ax[0].set_title('Bulls Eye (AHA)') - -bullseye_plot(ax[1], data, cmap=cmap2, norm=norm2) -ax[1].set_title('Bulls Eye (AHA)') - -bullseye_plot(ax[2], data, segBold=[3, 5, 6, 11, 12, 16], - cmap=cmap3, norm=norm3) -ax[2].set_title('Segments [3,5,6,11,12,16] in bold') - -plt.show() diff --git a/examples/pylab_examples/legend_demo2.py b/examples/pylab_examples/legend_demo2.py deleted file mode 100644 index 01619f3fd897..000000000000 --- a/examples/pylab_examples/legend_demo2.py +++ /dev/null @@ -1,20 +0,0 @@ -# Make a legend for specific lines. -import matplotlib.pyplot as plt -import numpy as np - - -t1 = np.arange(0.0, 2.0, 0.1) -t2 = np.arange(0.0, 2.0, 0.01) - -# note that plot returns a list of lines. The "l1, = plot" usage -# extracts the first element of the list into l1 using tuple -# unpacking. So l1 is a Line2D instance, not a sequence of lines -l1, = plt.plot(t2, np.exp(-t2)) -l2, l3 = plt.plot(t2, np.sin(2 * np.pi * t2), '--o', t1, np.log(1 + t1), '.') -l4, = plt.plot(t2, np.exp(-t2) * np.sin(2 * np.pi * t2), 's-.') - -plt.legend((l2, l4), ('oscillatory', 'damped'), loc='upper right', shadow=True) -plt.xlabel('time') -plt.ylabel('volts') -plt.title('Damped oscillation') -plt.show() diff --git a/examples/pylab_examples/legend_demo3.py b/examples/pylab_examples/legend_demo3.py deleted file mode 100644 index 083c6e86a0c1..000000000000 --- a/examples/pylab_examples/legend_demo3.py +++ /dev/null @@ -1,22 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -x = np.linspace(0, 1) - -# Plot the lines y=x**n for n=1..4. -ax = plt.subplot(2, 1, 1) -for n in range(1, 5): - plt.plot(x, x**n, label="n={0}".format(n)) -plt.legend(loc="upper left", bbox_to_anchor=[0, 1], - ncol=2, shadow=True, title="Legend", fancybox=True) -ax.get_legend().get_title().set_color("red") - -# Demonstrate some more complex labels. -ax = plt.subplot(2, 1, 2) -plt.plot(x, x**2, label="multi\nline") -half_pi = np.linspace(0, np.pi / 2) -plt.plot(np.sin(half_pi), np.cos(half_pi), label=r"$\frac{1}{2}\pi$") -plt.plot(x, 2**(x**2), label="$2^{x^2}$") -plt.legend(shadow=True, fancybox=True) - -plt.show() diff --git a/examples/pylab_examples/legend_demo4.py b/examples/pylab_examples/legend_demo4.py deleted file mode 100644 index 2fd076a97400..000000000000 --- a/examples/pylab_examples/legend_demo4.py +++ /dev/null @@ -1,22 +0,0 @@ -import matplotlib.pyplot as plt - -fig, axes = plt.subplots(3, 1) -top_ax, middle_ax, bottom_ax = axes - -top_ax.bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, label="Bar 1", - align="center") -top_ax.bar([0.5, 1.5, 2.5], [0.3, 0.2, 0.2], color="red", width=0.4, - label="Bar 2", align="center") -top_ax.legend() - -middle_ax.errorbar([0, 1, 2], [2, 3, 1], xerr=0.4, fmt="s", label="test 1") -middle_ax.errorbar([0, 1, 2], [3, 2, 4], yerr=0.3, fmt="o", label="test 2") -middle_ax.errorbar([0, 1, 2], [1, 1, 3], xerr=0.4, yerr=0.3, fmt="^", - label="test 3") -middle_ax.legend() - -bottom_ax.stem([0.3, 1.5, 2.7], [1, 3.6, 2.7], label="stem test") -bottom_ax.legend() - -plt.subplots_adjust(hspace=0.7) -plt.show() diff --git a/examples/pylab_examples/legend_demo5.py b/examples/pylab_examples/legend_demo5.py deleted file mode 100644 index 1294cfce4b7a..000000000000 --- a/examples/pylab_examples/legend_demo5.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import (absolute_import, division, - print_function, unicode_literals) -import six -from matplotlib import pyplot as plt -import numpy as np -from matplotlib.legend_handler import HandlerLineCollection -import matplotlib.collections as mcol -from matplotlib.lines import Line2D - - -class HandlerDashedLines(HandlerLineCollection): - """ - Custom Handler for LineCollection instances. - """ - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - # figure out how many lines there are - numlines = len(orig_handle.get_segments()) - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - leglines = [] - # divide the vertical space where the lines will go - # into equal parts based on the number of lines - ydata = ((height) / (numlines + 1)) * np.ones(xdata.shape, float) - # for each line, create the line at the proper location - # and set the dash pattern - for i in range(numlines): - legline = Line2D(xdata, ydata * (numlines - i) - ydescent) - self.update_prop(legline, orig_handle, legend) - # set color, dash pattern, and linewidth to that - # of the lines in linecollection - try: - color = orig_handle.get_colors()[i] - except IndexError: - color = orig_handle.get_colors()[0] - try: - dashes = orig_handle.get_dashes()[i] - except IndexError: - dashes = orig_handle.get_dashes()[0] - try: - lw = orig_handle.get_linewidths()[i] - except IndexError: - lw = orig_handle.get_linewidths()[0] - if dashes[0] is not None: - legline.set_dashes(dashes[1]) - legline.set_color(color) - legline.set_transform(trans) - legline.set_linewidth(lw) - leglines.append(legline) - return leglines - -x = np.linspace(0, 5, 100) - -plt.figure() -colors = plt.rcParams['axes.prop_cycle'].by_key()['color'][:5] -styles = ['solid', 'dashed', 'dashed', 'dashed', 'solid'] -lines = [] -for i, color, style in zip(range(5), colors, styles): - plt.plot(x, np.sin(x) - .1 * i, c=color, ls=style) - - -# make proxy artists -# make list of one line -- doesn't matter what the coordinates are -line = [[(0, 0)]] -# set up the proxy artist -lc = mcol.LineCollection(5 * line, linestyles=styles, colors=colors) -# create the legend -plt.legend([lc], ['multi-line'], handler_map={type(lc): HandlerDashedLines()}, - handlelength=2.5, handleheight=3) - -plt.show() diff --git a/examples/pylab_examples/legend_demo6.py b/examples/pylab_examples/legend_demo6.py deleted file mode 100644 index 9fef8a5d17e9..000000000000 --- a/examples/pylab_examples/legend_demo6.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Showcases legend entries with more than one legend key. -""" -import matplotlib.pyplot as plt -from matplotlib.legend_handler import HandlerTuple - -fig, (ax1, ax2) = plt.subplots(2, 1) - -# First plot: two legend keys for a single entry -p1 = ax1.scatter([1], [5], c='r', marker='s', s=100) -p2 = ax1.scatter([3], [2], c='b', marker='o', s=100) -# `plot` returns a list, but we want the handle - thus the comma on the left -p3, = ax1.plot([1, 5], [4, 4], 'm-d') - -# Assign two of the handles to the same legend entry by putting them in a tuple -# and using a generic handler map (which would be used for any additional -# tuples of handles like (p1, p3)). -l = ax1.legend([(p1, p3), p2], ['two keys', 'one key'], scatterpoints=1, - numpoints=1, handler_map={tuple: HandlerTuple(ndivide=None)}) - -# Second plot: plot two bar charts on top of each other and change the padding -# between the legend keys -x_left = [1, 2, 3] -y_pos = [1, 3, 2] -y_neg = [2, 1, 4] - -rneg = ax2.bar(x_left, y_neg, width=0.5, color='w', hatch='///', label='-1') -rpos = ax2.bar(x_left, y_pos, width=0.5, color='k', label='+1') - -# Treat each legend entry differently by using specific `HandlerTuple`s -l = ax2.legend([(rpos, rneg), (rneg, rpos)], ['pad!=0', 'pad=0'], - handler_map={(rpos, rneg): HandlerTuple(ndivide=None), - (rneg, rpos): HandlerTuple(ndivide=None, pad=0.)}) - -plt.show() diff --git a/examples/pylab_examples/line_collection.py b/examples/pylab_examples/line_collection.py deleted file mode 100644 index 22ccbc37c0bc..000000000000 --- a/examples/pylab_examples/line_collection.py +++ /dev/null @@ -1,40 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib.collections import LineCollection -from matplotlib import colors as mcolors - -import numpy as np - -# In order to efficiently plot many lines in a single set of axes, -# Matplotlib has the ability to add the lines all at once. Here is a -# simple example showing how it is done. - -x = np.arange(100) -# Here are many sets of y to plot vs x -ys = x[:50, np.newaxis] + x[np.newaxis, :] - -segs = np.zeros((50, 100, 2), float) -segs[:, :, 1] = ys -segs[:, :, 0] = x - -# Mask some values to test masked array support: -segs = np.ma.masked_where((segs > 50) & (segs < 60), segs) - -# We need to set the plot limits. -ax = plt.axes() -ax.set_xlim(x.min(), x.max()) -ax.set_ylim(ys.min(), ys.max()) - -# colors is sequence of rgba tuples -# linestyle is a string or dash tuple. Legal string values are -# solid|dashed|dashdot|dotted. The dash tuple is (offset, onoffseq) -# where onoffseq is an even length tuple of on and off ink in points. -# If linestyle is omitted, 'solid' is used -# See matplotlib.collections.LineCollection for more information -colors = [mcolors.to_rgba(c) - for c in plt.rcParams['axes.prop_cycle'].by_key()['color']] - -line_segments = LineCollection(segs, linewidths=(0.5, 1, 1.5, 2), - colors=colors, linestyle='solid') -ax.add_collection(line_segments) -ax.set_title('Line collection with masked arrays') -plt.show() diff --git a/examples/pylab_examples/line_collection2.py b/examples/pylab_examples/line_collection2.py deleted file mode 100644 index 8165ca87eab4..000000000000 --- a/examples/pylab_examples/line_collection2.py +++ /dev/null @@ -1,37 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.collections import LineCollection - -# In order to efficiently plot many lines in a single set of axes, -# Matplotlib has the ability to add the lines all at once. Here is a -# simple example showing how it is done. - -N = 50 -x = np.arange(N) -# Here are many sets of y to plot vs x -ys = [x + i for i in x] - -# We need to set the plot limits, they will not autoscale -ax = plt.axes() -ax.set_xlim((np.amin(x), np.amax(x))) -ax.set_ylim((np.amin(np.amin(ys)), np.amax(np.amax(ys)))) - -# colors is sequence of rgba tuples -# linestyle is a string or dash tuple. Legal string values are -# solid|dashed|dashdot|dotted. The dash tuple is (offset, onoffseq) -# where onoffseq is an even length tuple of on and off ink in points. -# If linestyle is omitted, 'solid' is used -# See matplotlib.collections.LineCollection for more information - -# Make a sequence of x,y pairs -line_segments = LineCollection([list(zip(x, y)) for y in ys], - linewidths=(0.5, 1, 1.5, 2), - linestyles='solid') -line_segments.set_array(x) -ax.add_collection(line_segments) -fig = plt.gcf() -axcb = fig.colorbar(line_segments) -axcb.set_label('Line Number') -ax.set_title('Line Collection with mapped colors') -plt.sci(line_segments) # This allows interactive changing of the colormap. -plt.show() diff --git a/examples/pylab_examples/load_converter.py b/examples/pylab_examples/load_converter.py deleted file mode 100644 index faf8f4d785e5..000000000000 --- a/examples/pylab_examples/load_converter.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import print_function -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook -import matplotlib.dates as mdates -from matplotlib.dates import bytespdate2num - -datafile = cbook.get_sample_data('msft.csv', asfileobj=False) -print('loading', datafile) - -dates, closes = np.loadtxt(datafile, delimiter=',', - converters={0: bytespdate2num('%d-%b-%y')}, - skiprows=1, usecols=(0, 2), unpack=True) - -fig = plt.figure() -ax = fig.add_subplot(111) -ax.plot_date(dates, closes, '-') -fig.autofmt_xdate() -plt.show() diff --git a/examples/pylab_examples/loadrec.py b/examples/pylab_examples/loadrec.py deleted file mode 100644 index 2174cc7841b0..000000000000 --- a/examples/pylab_examples/loadrec.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import print_function -from matplotlib import mlab -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -datafile = cbook.get_sample_data('msft.csv', asfileobj=False) -print('loading', datafile) -a = mlab.csv2rec(datafile) -a.sort() -print(a.dtype) - -fig = plt.figure() -ax = fig.add_subplot(111) -ax.plot(a.date, a.adj_close, '-') -fig.autofmt_xdate() - -# if you have xlwt installed, you can output excel -try: - import mpl_toolkits.exceltools as exceltools - exceltools.rec2excel(a, 'test.xls') -except ImportError: - pass -plt.show() diff --git a/examples/pylab_examples/log_bar.py b/examples/pylab_examples/log_bar.py deleted file mode 100644 index 8e18c00767a4..000000000000 --- a/examples/pylab_examples/log_bar.py +++ /dev/null @@ -1,22 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -data = ((3, 1000), (10, 3), (100, 30), (500, 800), (50, 1)) - -plt.xlabel("FOO") -plt.ylabel("FOO") -plt.title("Testing") -plt.yscale('log') - -dim = len(data[0]) -w = 0.75 -dimw = w / dim - -x = np.arange(len(data)) -for i in range(len(data[0])): - y = [d[i] for d in data] - b = plt.bar(x + i * dimw, y, dimw, bottom=0.001) -plt.xticks(x + w / 2) -plt.ylim((0.001, 1000)) - -plt.show() diff --git a/examples/pylab_examples/log_demo.py b/examples/pylab_examples/log_demo.py deleted file mode 100644 index bf7372191fc1..000000000000 --- a/examples/pylab_examples/log_demo.py +++ /dev/null @@ -1,38 +0,0 @@ - -import numpy as np -import matplotlib.pyplot as plt - -plt.subplots_adjust(hspace=0.4) -t = np.arange(0.01, 20.0, 0.01) - -# log y axis -plt.subplot(221) -plt.semilogy(t, np.exp(-t/5.0)) -plt.title('semilogy') -plt.grid(True) - -# log x axis -plt.subplot(222) -plt.semilogx(t, np.sin(2*np.pi*t)) -plt.title('semilogx') -plt.grid(True) - -# log x and y axis -plt.subplot(223) -plt.loglog(t, 20*np.exp(-t/10.0), basex=2) -plt.grid(True) -plt.title('loglog base 4 on x') - -# with errorbars: clip non-positive values -ax = plt.subplot(224) -ax.set_xscale("log", nonposx='clip') -ax.set_yscale("log", nonposy='clip') - -x = 10.0**np.linspace(0.0, 2.0, 20) -y = x**2.0 -plt.errorbar(x, y, xerr=0.1*x, yerr=5.0 + 0.75*y) -ax.set_ylim(ymin=0.1) -ax.set_title('Errorbars go negative') - - -plt.show() diff --git a/examples/pylab_examples/log_test.py b/examples/pylab_examples/log_test.py deleted file mode 100644 index 0aa2a410ee8b..000000000000 --- a/examples/pylab_examples/log_test.py +++ /dev/null @@ -1,10 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -dt = 0.01 -t = np.arange(dt, 20.0, dt) - -plt.semilogx(t, np.exp(-t/5.0)) -plt.grid(True) - -plt.show() diff --git a/examples/pylab_examples/logo.py b/examples/pylab_examples/logo.py deleted file mode 100755 index 2c65e55581c3..000000000000 --- a/examples/pylab_examples/logo.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file generates an old version of the matplotlib logo - -from __future__ import print_function -# Above import not necessary for Python 3 onwards. Recommend taking this -# out in examples in the future, since we should all move to Python 3. -import matplotlib.pyplot as plt -import numpy as np -import matplotlib.cbook as cbook - -# convert data to mV -datafile = cbook.get_sample_data('membrane.dat', asfileobj=False) -print('loading', datafile) - -x = 1000 * 0.1 * np.fromstring(open(datafile, 'rb').read(), np.float32) -# 0.0005 is the sample interval -t = 0.0005 * np.arange(len(x)) -plt.figure(1, figsize=(7, 1), dpi=100) -ax = plt.subplot(111, facecolor='y') -plt.plot(t, x) -plt.text(0.5, 0.5, 'matplotlib', color='r', - fontsize=40, fontname=['Courier', 'DejaVu Sans Mono'], - horizontalalignment='center', - verticalalignment='center', - transform=ax.transAxes, - ) -plt.axis([1, 1.72, -60, 10]) -plt.gca().set_xticklabels([]) -plt.gca().set_yticklabels([]) - -plt.show() diff --git a/examples/pylab_examples/major_minor_demo1.py b/examples/pylab_examples/major_minor_demo1.py deleted file mode 100644 index df5e887ffd4a..000000000000 --- a/examples/pylab_examples/major_minor_demo1.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Demonstrate how to use major and minor tickers. - -The two relevant userland classes are Locators and Formatters. -Locators determine where the ticks are and formatters control the -formatting of ticks. - -Minor ticks are off by default (NullLocator and NullFormatter). You -can turn minor ticks on w/o labels by setting the minor locator. You -can also turn labeling on for the minor ticker by setting the minor -formatter - -Make a plot with major ticks that are multiples of 20 and minor ticks -that are multiples of 5. Label major ticks with %d formatting but -don't label minor ticks - -The MultipleLocator ticker class is used to place ticks on multiples of -some base. The FormatStrFormatter uses a string format string (e.g., -'%d' or '%1.2f' or '%1.1f cm' ) to format the tick - -The pyplot interface grid command changes the grid settings of the -major ticks of the y and y axis together. If you want to control the -grid of the minor ticks for a given axis, use for example - - ax.xaxis.grid(True, which='minor') - -Note, you should not use the same locator between different Axis -because the locator stores references to the Axis data and view limits - -""" - -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.ticker import MultipleLocator, FormatStrFormatter - -majorLocator = MultipleLocator(20) -majorFormatter = FormatStrFormatter('%d') -minorLocator = MultipleLocator(5) - - -t = np.arange(0.0, 100.0, 0.1) -s = np.sin(0.1*np.pi*t)*np.exp(-t*0.01) - -fig, ax = plt.subplots() -plt.plot(t, s) - -ax.xaxis.set_major_locator(majorLocator) -ax.xaxis.set_major_formatter(majorFormatter) - -# for the minor ticks, use no labels; default NullFormatter -ax.xaxis.set_minor_locator(minorLocator) - -plt.show() diff --git a/examples/pylab_examples/major_minor_demo2.py b/examples/pylab_examples/major_minor_demo2.py deleted file mode 100644 index 1bdfefabdb94..000000000000 --- a/examples/pylab_examples/major_minor_demo2.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Automatic tick selection for major and minor ticks. - -Use interactive pan and zoom to see how the tick intervals -change. There will be either 4 or 5 minor tick intervals -per major interval, depending on the major interval. -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.ticker import AutoMinorLocator - -# One can supply an argument to AutoMinorLocator to -# specify a fixed number of minor intervals per major interval, e.g.: -# minorLocator = AutoMinorLocator(2) -# would lead to a single minor tick between major ticks. - -minorLocator = AutoMinorLocator() - - -t = np.arange(0.0, 100.0, 0.01) -s = np.sin(2*np.pi*t)*np.exp(-t*0.01) - -fig, ax = plt.subplots() -plt.plot(t, s) - -ax.xaxis.set_minor_locator(minorLocator) - -plt.tick_params(which='both', width=2) -plt.tick_params(which='major', length=7) -plt.tick_params(which='minor', length=4, color='r') - -plt.show() diff --git a/examples/pylab_examples/manual_axis.py b/examples/pylab_examples/manual_axis.py deleted file mode 100644 index f1f8b2aedc51..000000000000 --- a/examples/pylab_examples/manual_axis.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -The techniques here are no longer required with the new support for -spines in matplotlib -- see -http://matplotlib.org/examples/pylab_examples/spine_placement_demo.html. - -This example should be considered deprecated and is left just for demo -purposes for folks wanting to make a pseudo-axis -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.lines as lines - - -def make_xaxis(ax, yloc, offset=0.05, **props): - xmin, xmax = ax.get_xlim() - locs = [loc for loc in ax.xaxis.get_majorticklocs() - if loc >= xmin and loc <= xmax] - tickline, = ax.plot(locs, [yloc]*len(locs), linestyle='', - marker=lines.TICKDOWN, **props) - axline, = ax.plot([xmin, xmax], [yloc, yloc], **props) - tickline.set_clip_on(False) - axline.set_clip_on(False) - for loc in locs: - ax.text(loc, yloc - offset, '%1.1f' % loc, - horizontalalignment='center', - verticalalignment='top') - - -def make_yaxis(ax, xloc=0, offset=0.05, **props): - ymin, ymax = ax.get_ylim() - locs = [loc for loc in ax.yaxis.get_majorticklocs() - if loc >= ymin and loc <= ymax] - tickline, = ax.plot([xloc]*len(locs), locs, linestyle='', - marker=lines.TICKLEFT, **props) - axline, = ax.plot([xloc, xloc], [ymin, ymax], **props) - tickline.set_clip_on(False) - axline.set_clip_on(False) - - for loc in locs: - ax.text(xloc - offset, loc, '%1.1f' % loc, - verticalalignment='center', - horizontalalignment='right') - - -props = dict(color='black', linewidth=2, markeredgewidth=2) -x = np.arange(200.) -y = np.sin(2*np.pi*x/200.) + np.random.rand(200) - 0.5 -fig = plt.figure(facecolor='white') -ax = fig.add_subplot(111, frame_on=False) -ax.axison = False -ax.plot(x, y, 'd', markersize=8, markerfacecolor='blue') -ax.set_xlim(0, 200) -ax.set_ylim(-1.5, 1.5) -make_xaxis(ax, 0, offset=0.1, **props) -make_yaxis(ax, 0, offset=5, **props) - -plt.show() diff --git a/examples/pylab_examples/marker_path.py b/examples/pylab_examples/marker_path.py deleted file mode 100644 index ac18d1335133..000000000000 --- a/examples/pylab_examples/marker_path.py +++ /dev/null @@ -1,16 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.path as mpath -import numpy as np - - -star = mpath.Path.unit_regular_star(6) -circle = mpath.Path.unit_circle() -# concatenate the circle with an internal cutout of the star -verts = np.concatenate([circle.vertices, star.vertices[::-1, ...]]) -codes = np.concatenate([circle.codes, star.codes]) -cut_star = mpath.Path(verts, codes) - - -plt.plot(np.arange(10)**2, '--r', marker=cut_star, markersize=15) - -plt.show() diff --git a/examples/pylab_examples/markevery_demo.py b/examples/pylab_examples/markevery_demo.py deleted file mode 100644 index 7136472dba0e..000000000000 --- a/examples/pylab_examples/markevery_demo.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -This example demonstrates the various options for showing a marker at a -subset of data points using the `markevery` property of a Line2D object. - -Integer arguments are fairly intuitive. e.g. `markevery`=5 will plot every -5th marker starting from the first data point. - -Float arguments allow markers to be spaced at approximately equal distances -along the line. The theoretical distance along the line between markers is -determined by multiplying the display-coordinate distance of the axes -bounding-box diagonal by the value of `markevery`. The data points closest -to the theoretical distances will be shown. - -A slice or list/array can also be used with `markevery` to specify the markers -to show. - -""" - -from __future__ import division -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec - -# define a list of markevery cases to plot -cases = [None, - 8, - (30, 8), - [16, 24, 30], [0, -1], - slice(100, 200, 3), - 0.1, 0.3, 1.5, - (0.0, 0.1), (0.45, 0.1)] - -# define the figure size and grid layout properties -figsize = (10, 8) -cols = 3 -gs = gridspec.GridSpec(len(cases) // cols + 1, cols) - -# define the data for cartesian plots -delta = 0.11 -x = np.linspace(0, 10 - 2 * delta, 200) + delta -y = np.sin(x) + 1.0 + delta - -# plot each markevery case for linear x and y scales -fig1 = plt.figure(num=1, figsize=figsize) -ax = [] -for i, case in enumerate(cases): - row = (i // cols) - col = i % cols - ax.append(fig1.add_subplot(gs[row, col])) - ax[-1].set_title('markevery=%s' % str(case)) - ax[-1].plot(x, y, 'o', ls='-', ms=4, markevery=case) -#fig1.tight_layout() - -# plot each markevery case for log x and y scales -fig2 = plt.figure(num=2, figsize=figsize) -axlog = [] -for i, case in enumerate(cases): - row = (i // cols) - col = i % cols - axlog.append(fig2.add_subplot(gs[row, col])) - axlog[-1].set_title('markevery=%s' % str(case)) - axlog[-1].set_xscale('log') - axlog[-1].set_yscale('log') - axlog[-1].plot(x, y, 'o', ls='-', ms=4, markevery=case) -fig2.tight_layout() - -# plot each markevery case for linear x and y scales but zoomed in -# note the behaviour when zoomed in. When a start marker offset is specified -# it is always interpreted with respect to the first data point which might be -# different to the first visible data point. -fig3 = plt.figure(num=3, figsize=figsize) -axzoom = [] -for i, case in enumerate(cases): - row = (i // cols) - col = i % cols - axzoom.append(fig3.add_subplot(gs[row, col])) - axzoom[-1].set_title('markevery=%s' % str(case)) - axzoom[-1].plot(x, y, 'o', ls='-', ms=4, markevery=case) - axzoom[-1].set_xlim((6, 6.7)) - axzoom[-1].set_ylim((1.1, 1.7)) -fig3.tight_layout() - -# define data for polar plots -r = np.linspace(0, 3.0, 200) -theta = 2 * np.pi * r - -# plot each markevery case for polar plots -fig4 = plt.figure(num=4, figsize=figsize) -axpolar = [] -for i, case in enumerate(cases): - row = (i // cols) - col = i % cols - axpolar.append(fig4.add_subplot(gs[row, col], projection='polar')) - axpolar[-1].set_title('markevery=%s' % str(case)) - axpolar[-1].plot(theta, r, 'o', ls='-', ms=4, markevery=case) -fig4.tight_layout() - -plt.show() diff --git a/examples/pylab_examples/masked_demo.py b/examples/pylab_examples/masked_demo.py deleted file mode 100644 index f349aadb5abf..000000000000 --- a/examples/pylab_examples/masked_demo.py +++ /dev/null @@ -1,26 +0,0 @@ -''' -Plot lines with points masked out. - -This would typically be used with gappy data, to -break the line at the data gaps. -''' - -import matplotlib.pyplot as plt -import numpy as np - -x = np.arange(0, 2*np.pi, 0.02) -y = np.sin(x) -y1 = np.sin(2*x) -y2 = np.sin(3*x) -ym1 = np.ma.masked_where(y1 > 0.5, y1) -ym2 = np.ma.masked_where(y2 < -0.5, y2) - -lines = plt.plot(x, y, x, ym1, x, ym2, 'o') -plt.setp(lines[0], linewidth=4) -plt.setp(lines[1], linewidth=2) -plt.setp(lines[2], markersize=10) - -plt.legend(('No mask', 'Masked if > 0.5', 'Masked if < -0.5'), - loc='upper right') -plt.title('Masked line demo') -plt.show() diff --git a/examples/pylab_examples/mathtext_demo.py b/examples/pylab_examples/mathtext_demo.py deleted file mode 100755 index 085d557cddd4..000000000000 --- a/examples/pylab_examples/mathtext_demo.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Use matplotlib's internal LaTeX parser and layout engine. For true -latex rendering, see the text.usetex option -""" -import numpy as np -from matplotlib.pyplot import figure, show - -fig = figure() -fig.subplots_adjust(bottom=0.2) - -ax = fig.add_subplot(111) -ax.plot([1, 2, 3], 'r') -x = np.arange(0.0, 3.0, 0.1) - -ax.grid(True) -ax.set_xlabel(r'$\Delta_i^j$', fontsize=20) -ax.set_ylabel(r'$\Delta_{i+1}^j$', fontsize=20) -tex = r'$\mathcal{R}\prod_{i=\alpha_{i+1}}^\infty a_i\sin(2 \pi f x_i)$' - -ax.text(1, 1.6, tex, fontsize=20, va='bottom') - -ax.legend([r"$\sqrt{x^2}$"]) - -ax.set_title(r'$\Delta_i^j \hspace{0.4} \mathrm{versus} \hspace{0.4} \Delta_{i+1}^j$', fontsize=20) - -show() diff --git a/examples/pylab_examples/mathtext_examples.py b/examples/pylab_examples/mathtext_examples.py deleted file mode 100755 index bee528de214d..000000000000 --- a/examples/pylab_examples/mathtext_examples.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Selected features of Matplotlib's math rendering engine. -""" -from __future__ import print_function -import matplotlib.pyplot as plt -import os -import sys -import re -import gc - -# Selection of features following "Writing mathematical expressions" tutorial -mathtext_titles = { - 0: "Header demo", - 1: "Subscripts and superscripts", - 2: "Fractions, binomials and stacked numbers", - 3: "Radicals", - 4: "Fonts", - 5: "Accents", - 6: "Greek, Hebrew", - 7: "Delimiters, functions and Symbols"} -n_lines = len(mathtext_titles) - -# Randomly picked examples -mathext_demos = { - 0: r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = " - r"U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} " - r"\int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ " - r"U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_" - r"{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$", - - 1: r"$\alpha_i > \beta_i,\ " - r"\alpha_{i+1}^j = {\rm sin}(2\pi f_j t_i) e^{-5 t_i/\tau},\ " - r"\ldots$", - - 2: r"$\frac{3}{4},\ \binom{3}{4},\ \stackrel{3}{4},\ " - r"\left(\frac{5 - \frac{1}{x}}{4}\right),\ \ldots$", - - 3: r"$\sqrt{2},\ \sqrt[3]{x},\ \ldots$", - - 4: r"$\mathrm{Roman}\ , \ \mathit{Italic}\ , \ \mathtt{Typewriter} \ " - r"\mathrm{or}\ \mathcal{CALLIGRAPHY}$", - - 5: r"$\acute a,\ \bar a,\ \breve a,\ \dot a,\ \ddot a, \ \grave a, \ " - r"\hat a,\ \tilde a,\ \vec a,\ \widehat{xyz},\ \widetilde{xyz},\ " - r"\ldots$", - - 6: r"$\alpha,\ \beta,\ \chi,\ \delta,\ \lambda,\ \mu,\ " - r"\Delta,\ \Gamma,\ \Omega,\ \Phi,\ \Pi,\ \Upsilon,\ \nabla,\ " - r"\aleph,\ \beth,\ \daleth,\ \gimel,\ \ldots$", - - 7: r"$\coprod,\ \int,\ \oint,\ \prod,\ \sum,\ " - r"\log,\ \sin,\ \approx,\ \oplus,\ \star,\ \varpropto,\ " - r"\infty,\ \partial,\ \Re,\ \leftrightsquigarrow, \ \ldots$"} - - -def doall(): - # Colors used in mpl online documentation. - mpl_blue_rvb = (191./255., 209./256., 212./255.) - mpl_orange_rvb = (202/255., 121/256., 0./255.) - mpl_grey_rvb = (51./255., 51./255., 51./255.) - - # Creating figure and axis. - plt.figure(figsize=(6, 7)) - plt.axes([0.01, 0.01, 0.98, 0.90], facecolor="white", frameon=True) - plt.gca().set_xlim(0., 1.) - plt.gca().set_ylim(0., 1.) - plt.gca().set_title("Matplotlib's math rendering engine", - color=mpl_grey_rvb, fontsize=14, weight='bold') - plt.gca().set_xticklabels("", visible=False) - plt.gca().set_yticklabels("", visible=False) - - # Gap between lines in axes coords - line_axesfrac = (1. / (n_lines)) - - # Plotting header demonstration formula - full_demo = mathext_demos[0] - plt.annotate(full_demo, - xy=(0.5, 1. - 0.59*line_axesfrac), - xycoords='data', color=mpl_orange_rvb, ha='center', - fontsize=20) - - # Plotting features demonstration formulae - for i_line in range(1, n_lines): - baseline = 1. - (i_line)*line_axesfrac - baseline_next = baseline - line_axesfrac*1. - title = mathtext_titles[i_line] + ":" - fill_color = ['white', mpl_blue_rvb][i_line % 2] - plt.fill_between([0., 1.], [baseline, baseline], - [baseline_next, baseline_next], - color=fill_color, alpha=0.5) - plt.annotate(title, - xy=(0.07, baseline - 0.3*line_axesfrac), - xycoords='data', color=mpl_grey_rvb, weight='bold') - demo = mathext_demos[i_line] - plt.annotate(demo, - xy=(0.05, baseline - 0.75*line_axesfrac), - xycoords='data', color=mpl_grey_rvb, - fontsize=16) - - for i in range(n_lines): - s = mathext_demos[i] - print(i, s) - plt.show() - -if '--latex' in sys.argv: - # Run: python mathtext_examples.py --latex - # Need amsmath and amssymb packages. - fd = open("mathtext_examples.ltx", "w") - fd.write("\\documentclass{article}\n") - fd.write("\\usepackage{amsmath, amssymb}\n") - fd.write("\\begin{document}\n") - fd.write("\\begin{enumerate}\n") - - for i in range(n_lines): - s = mathext_demos[i] - s = re.sub(r"(? 0: - a.set_xticklabels([]) - # Make some fake data with a range that varies - # somewhat from one plot to the next. - data = ((1 + i + j)/10.0)*rand(10, 20)*1e-6 - dd = ravel(data) - # Manually find the min and max of all colors for - # use in setting the color scale. - vmin = min(vmin, amin(dd)) - vmax = max(vmax, amax(dd)) - images.append(a.imshow(data, cmap=cmap)) - - ax.append(a) - -# Set the first image as the master, with all the others -# observing it for changes in cmap or norm. - - -class ImageFollower(object): - 'update image in response to changes in clim or cmap on another image' - - def __init__(self, follower): - self.follower = follower - - def __call__(self, leader): - self.follower.set_cmap(leader.get_cmap()) - self.follower.set_clim(leader.get_clim()) - -norm = colors.Normalize(vmin=vmin, vmax=vmax) -for i, im in enumerate(images): - im.set_norm(norm) - if i > 0: - images[0].callbacksSM.connect('changed', ImageFollower(im)) - -# The colorbar is also based on this master image. -fig.colorbar(images[0], cax, orientation='horizontal') - -# We need the following only if we want to run this interactively and -# modify the colormap: - -axes(ax[0]) # Return the current axes to the first one, -sci(images[0]) # because the current image must be in current axes. - -show() diff --git a/examples/pylab_examples/multicolored_line.py b/examples/pylab_examples/multicolored_line.py deleted file mode 100644 index 386cea0a438b..000000000000 --- a/examples/pylab_examples/multicolored_line.py +++ /dev/null @@ -1,53 +0,0 @@ -''' -Color parts of a line based on its properties, e.g., slope. -''' - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.collections import LineCollection -from matplotlib.colors import ListedColormap, BoundaryNorm - -x = np.linspace(0, 3 * np.pi, 500) -y = np.sin(x) -z = np.cos(0.5 * (x[:-1] + x[1:])) # first derivative - -# Create a colormap for red, green and blue and a norm to color -# f' < -0.5 red, f' > 0.5 blue, and the rest green -cmap = ListedColormap(['r', 'g', 'b']) -norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N) - -# Create a set of line segments so that we can color them individually -# This creates the points as a N x 1 x 2 array so that we can stack points -# together easily to get the segments. The segments array for line collection -# needs to be numlines x points per line x 2 (x and y) -points = np.array([x, y]).T.reshape(-1, 1, 2) -segments = np.concatenate([points[:-1], points[1:]], axis=1) - -# Create the line collection object, setting the colormapping parameters. -# Have to set the actual values used for colormapping separately. -lc = LineCollection(segments, cmap=cmap, norm=norm) -lc.set_array(z) -lc.set_linewidth(3) - -fig1 = plt.figure() -plt.gca().add_collection(lc) -plt.xlim(x.min(), x.max()) -plt.ylim(-1.1, 1.1) - -# Now do a second plot coloring the curve using a continuous colormap -t = np.linspace(0, 10, 200) -x = np.cos(np.pi * t) -y = np.sin(t) -points = np.array([x, y]).T.reshape(-1, 1, 2) -segments = np.concatenate([points[:-1], points[1:]], axis=1) - -lc = LineCollection(segments, cmap=plt.get_cmap('copper'), - norm=plt.Normalize(0, 10)) -lc.set_array(t) -lc.set_linewidth(3) - -fig2 = plt.figure() -plt.gca().add_collection(lc) -plt.xlim(-1, 1) -plt.ylim(-1, 1) -plt.show() diff --git a/examples/pylab_examples/multiline.py b/examples/pylab_examples/multiline.py deleted file mode 100644 index 5d0d19a588a1..000000000000 --- a/examples/pylab_examples/multiline.py +++ /dev/null @@ -1,40 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -plt.figure(figsize=(7, 4)) -ax = plt.subplot(121) -ax.set_aspect(1) -plt.plot(np.arange(10)) -plt.xlabel('this is a xlabel\n(with newlines!)') -plt.ylabel('this is vertical\ntest', multialignment='center') -plt.text(2, 7, 'this is\nyet another test', - rotation=45, - horizontalalignment='center', - verticalalignment='top', - multialignment='center') - -plt.grid(True) - -plt.subplot(122) - -plt.text(0.29, 0.4, "Mat\nTTp\n123", size=18, - va="baseline", ha="right", multialignment="left", - bbox=dict(fc="none")) - -plt.text(0.34, 0.4, "Mag\nTTT\n123", size=18, - va="baseline", ha="left", multialignment="left", - bbox=dict(fc="none")) - -plt.text(0.95, 0.4, "Mag\nTTT$^{A^A}$\n123", size=18, - va="baseline", ha="right", multialignment="left", - bbox=dict(fc="none")) - -plt.xticks([0.2, 0.4, 0.6, 0.8, 1.], - ["Jan\n2009", "Feb\n2009", "Mar\n2009", "Apr\n2009", "May\n2009"]) - -plt.axhline(0.4) -plt.title("test line spacing for multiline text") - -plt.subplots_adjust(bottom=0.25, top=0.75) -plt.draw() -plt.show() diff --git a/examples/pylab_examples/multiple_figs_demo.py b/examples/pylab_examples/multiple_figs_demo.py deleted file mode 100644 index 9097c51e4e6b..000000000000 --- a/examples/pylab_examples/multiple_figs_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -# Working with multiple figure windows and subplots -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 2.0, 0.01) -s1 = np.sin(2*np.pi*t) -s2 = np.sin(4*np.pi*t) - -plt.figure(1) -plt.subplot(211) -plt.plot(t, s1) -plt.subplot(212) -plt.plot(t, 2*s1) - -plt.figure(2) -plt.plot(t, s2) - -# now switch back to figure 1 and make some changes -plt.figure(1) -plt.subplot(211) -plt.plot(t, s2, 's') -ax = plt.gca() -ax.set_xticklabels([]) - -plt.show() diff --git a/examples/pylab_examples/multiple_yaxis_with_spines.py b/examples/pylab_examples/multiple_yaxis_with_spines.py deleted file mode 100644 index fde330ec5528..000000000000 --- a/examples/pylab_examples/multiple_yaxis_with_spines.py +++ /dev/null @@ -1,54 +0,0 @@ -import matplotlib.pyplot as plt - - -def make_patch_spines_invisible(ax): - ax.set_frame_on(True) - ax.patch.set_visible(False) - for sp in ax.spines.values(): - sp.set_visible(False) - -fig, host = plt.subplots() -fig.subplots_adjust(right=0.75) - -par1 = host.twinx() -par2 = host.twinx() - -# Offset the right spine of par2. The ticks and label have already been -# placed on the right by twinx above. -par2.spines["right"].set_position(("axes", 1.2)) -# Having been created by twinx, par2 has its frame off, so the line of its -# detached spine is invisible. First, activate the frame but make the patch -# and spines invisible. -make_patch_spines_invisible(par2) -# Second, show the right spine. -par2.spines["right"].set_visible(True) - -p1, = host.plot([0, 1, 2], [0, 1, 2], "b-", label="Density") -p2, = par1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature") -p3, = par2.plot([0, 1, 2], [50, 30, 15], "g-", label="Velocity") - -host.set_xlim(0, 2) -host.set_ylim(0, 2) -par1.set_ylim(0, 4) -par2.set_ylim(1, 65) - -host.set_xlabel("Distance") -host.set_ylabel("Density") -par1.set_ylabel("Temperature") -par2.set_ylabel("Velocity") - -host.yaxis.label.set_color(p1.get_color()) -par1.yaxis.label.set_color(p2.get_color()) -par2.yaxis.label.set_color(p3.get_color()) - -tkw = dict(size=4, width=1.5) -host.tick_params(axis='y', colors=p1.get_color(), **tkw) -par1.tick_params(axis='y', colors=p2.get_color(), **tkw) -par2.tick_params(axis='y', colors=p3.get_color(), **tkw) -host.tick_params(axis='x', **tkw) - -lines = [p1, p2, p3] - -host.legend(lines, [l.get_label() for l in lines]) - -plt.show() diff --git a/examples/pylab_examples/nan_test.py b/examples/pylab_examples/nan_test.py deleted file mode 100755 index 5a54c928b6b9..000000000000 --- a/examples/pylab_examples/nan_test.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Example: simple line plots with NaNs inserted. -""" -import numpy as np -import matplotlib.pyplot as plt - -t = np.arange(0.0, 1.0 + 0.01, 0.01) -s = np.cos(2 * 2 * np.pi * t) -t[41:60] = np.nan - -plt.subplot(2, 1, 1) -plt.plot(t, s, '-', lw=2) - -plt.xlabel('time (s)') -plt.ylabel('voltage (mV)') -plt.title('A sine wave with a gap of NaNs between 0.4 and 0.6') -plt.grid(True) - -plt.subplot(2, 1, 2) -t[0] = np.nan -t[-1] = np.nan -plt.plot(t, s, '-', lw=2) -plt.title('Also with NaN in first and last point') - -plt.xlabel('time (s)') -plt.ylabel('more nans') -plt.grid(True) - -plt.show() diff --git a/examples/pylab_examples/newscalarformatter_demo.py b/examples/pylab_examples/newscalarformatter_demo.py deleted file mode 100644 index 381ced7f3b49..000000000000 --- a/examples/pylab_examples/newscalarformatter_demo.py +++ /dev/null @@ -1,103 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np -from matplotlib.ticker import OldScalarFormatter, ScalarFormatter - -# Example 1 -x = np.arange(0, 1, .01) -fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2, figsize=(6, 6)) -fig.text(0.5, 0.975, 'The old formatter', - horizontalalignment='center', verticalalignment='top') -ax1.plot(x * 1e5 + 1e10, x * 1e-10 + 1e-5) -ax1.xaxis.set_major_formatter(OldScalarFormatter()) -ax1.yaxis.set_major_formatter(OldScalarFormatter()) - -ax2.plot(x * 1e5, x * 1e-4) -ax2.xaxis.set_major_formatter(OldScalarFormatter()) -ax2.yaxis.set_major_formatter(OldScalarFormatter()) - -ax3.plot(-x * 1e5 - 1e10, -x * 1e-5 - 1e-10) -ax3.xaxis.set_major_formatter(OldScalarFormatter()) -ax3.yaxis.set_major_formatter(OldScalarFormatter()) - -ax4.plot(-x * 1e5, -x * 1e-4) -ax4.xaxis.set_major_formatter(OldScalarFormatter()) -ax4.yaxis.set_major_formatter(OldScalarFormatter()) - -fig.subplots_adjust(wspace=0.7, hspace=0.6) - -# Example 2 -x = np.arange(0, 1, .01) -fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2, figsize=(6, 6)) -fig.text(0.5, 0.975, 'The new formatter, default settings', - horizontalalignment='center', - verticalalignment='top') - -ax1.plot(x * 1e5 + 1e10, x * 1e-10 + 1e-5) -ax1.xaxis.set_major_formatter(ScalarFormatter()) -ax1.yaxis.set_major_formatter(ScalarFormatter()) - -ax2.plot(x * 1e5, x * 1e-4) -ax2.xaxis.set_major_formatter(ScalarFormatter()) -ax2.yaxis.set_major_formatter(ScalarFormatter()) - -ax3.plot(-x * 1e5 - 1e10, -x * 1e-5 - 1e-10) -ax3.xaxis.set_major_formatter(ScalarFormatter()) -ax3.yaxis.set_major_formatter(ScalarFormatter()) - -ax4.plot(-x * 1e5, -x * 1e-4) -ax4.xaxis.set_major_formatter(ScalarFormatter()) -ax4.yaxis.set_major_formatter(ScalarFormatter()) - -fig.subplots_adjust(wspace=0.7, hspace=0.6) - -# Example 3 -x = np.arange(0, 1, .01) -fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2, figsize=(6, 6)) -fig.text(0.5, 0.975, 'The new formatter, no numerical offset', - horizontalalignment='center', - verticalalignment='top') - -ax1.plot(x * 1e5 + 1e10, x * 1e-10 + 1e-5) -ax1.xaxis.set_major_formatter(ScalarFormatter(useOffset=False)) -ax1.yaxis.set_major_formatter(ScalarFormatter(useOffset=False)) - -ax2.plot(x * 1e5, x * 1e-4) -ax2.xaxis.set_major_formatter(ScalarFormatter(useOffset=False)) -ax2.yaxis.set_major_formatter(ScalarFormatter(useOffset=False)) - -ax3.plot(-x * 1e5 - 1e10, -x * 1e-5 - 1e-10) -ax3.xaxis.set_major_formatter(ScalarFormatter(useOffset=False)) -ax3.yaxis.set_major_formatter(ScalarFormatter(useOffset=False)) - -ax4.plot(-x * 1e5, -x * 1e-4) -ax4.xaxis.set_major_formatter(ScalarFormatter(useOffset=False)) -ax4.yaxis.set_major_formatter(ScalarFormatter(useOffset=False)) - -fig.subplots_adjust(wspace=0.7, hspace=0.6) - -# Example 4 -x = np.arange(0, 1, .01) -fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2, figsize=(6, 6)) -fig.text(0.5, 0.975, 'The new formatter, with mathtext', - horizontalalignment='center', - verticalalignment='top') - -ax1.plot(x * 1e5 + 1e10, x * 1e-10 + 1e-5) -ax1.xaxis.set_major_formatter(ScalarFormatter(useMathText=True)) -ax1.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) - -ax2.plot(x * 1e5, x * 1e-4) -ax2.xaxis.set_major_formatter(ScalarFormatter(useMathText=True)) -ax2.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) - -ax3.plot(-x * 1e5 - 1e10, -x * 1e-5 - 1e-10) -ax3.xaxis.set_major_formatter(ScalarFormatter(useMathText=True)) -ax3.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) - -ax4.plot(-x * 1e5, -x * 1e-4) -ax4.xaxis.set_major_formatter(ScalarFormatter(useMathText=True)) -ax4.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) - -fig.subplots_adjust(wspace=0.7, hspace=0.6) - -plt.show() diff --git a/examples/pylab_examples/patheffect_demo.py b/examples/pylab_examples/patheffect_demo.py deleted file mode 100644 index 0adac2911987..000000000000 --- a/examples/pylab_examples/patheffect_demo.py +++ /dev/null @@ -1,43 +0,0 @@ -import matplotlib.pyplot as plt -import matplotlib.patheffects as PathEffects -import numpy as np - -if 1: - plt.figure(1, figsize=(8, 3)) - ax1 = plt.subplot(131) - ax1.imshow([[1, 2], [2, 3]]) - txt = ax1.annotate("test", (1., 1.), (0., 0), - arrowprops=dict(arrowstyle="->", - connectionstyle="angle3", lw=2), - size=20, ha="center", path_effects=[PathEffects.withStroke(linewidth=3, - foreground="w")]) - txt.arrow_patch.set_path_effects([ - PathEffects.Stroke(linewidth=5, foreground="w"), - PathEffects.Normal()]) - - ax1.grid(True, linestyle="-") - - pe = [PathEffects.withStroke(linewidth=3, - foreground="w")] - for l in ax1.get_xgridlines() + ax1.get_ygridlines(): - l.set_path_effects(pe) - - ax2 = plt.subplot(132) - arr = np.arange(25).reshape((5, 5)) - ax2.imshow(arr) - cntr = ax2.contour(arr, colors="k") - - plt.setp(cntr.collections, path_effects=[ - PathEffects.withStroke(linewidth=3, foreground="w")]) - - clbls = ax2.clabel(cntr, fmt="%2.0f", use_clabeltext=True) - plt.setp(clbls, path_effects=[ - PathEffects.withStroke(linewidth=3, foreground="w")]) - - # shadow as a path effect - ax3 = plt.subplot(133) - p1, = ax3.plot([0, 1], [0, 1]) - leg = ax3.legend([p1], ["Line 1"], fancybox=True, loc=2) - leg.legendPatch.set_path_effects([PathEffects.withSimplePatchShadow()]) - - plt.show() diff --git a/examples/pylab_examples/pcolor_demo.py b/examples/pylab_examples/pcolor_demo.py deleted file mode 100644 index 2529a57b2d67..000000000000 --- a/examples/pylab_examples/pcolor_demo.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Demonstrates similarities between pcolor, pcolormesh, imshow and pcolorfast -for drawing quadrilateral grids. - -""" -import matplotlib.pyplot as plt -import numpy as np - -# make these smaller to increase the resolution -dx, dy = 0.15, 0.05 - -# generate 2 2d grids for the x & y bounds -y, x = np.mgrid[slice(-3, 3 + dy, dy), - slice(-3, 3 + dx, dx)] -z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2) -# x and y are bounds, so z should be the value *inside* those bounds. -# Therefore, remove the last value from the z array. -z = z[:-1, :-1] -z_min, z_max = -np.abs(z).max(), np.abs(z).max() - - -plt.subplot(2, 2, 1) -plt.pcolor(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max) -plt.title('pcolor') -# set the limits of the plot to the limits of the data -plt.axis([x.min(), x.max(), y.min(), y.max()]) -plt.colorbar() - - -plt.subplot(2, 2, 2) -plt.pcolormesh(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max) -plt.title('pcolormesh') -# set the limits of the plot to the limits of the data -plt.axis([x.min(), x.max(), y.min(), y.max()]) -plt.colorbar() - - -plt.subplot(2, 2, 3) -plt.imshow(z, cmap='RdBu', vmin=z_min, vmax=z_max, - extent=[x.min(), x.max(), y.min(), y.max()], - interpolation='nearest', origin='lower') -plt.title('image (nearest)') -plt.colorbar() - - -ax = plt.subplot(2, 2, 4) -ax.pcolorfast(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max) -plt.title('pcolorfast') -plt.colorbar() - -plt.subplots_adjust(wspace=0.5, hspace=0.5) - -plt.show() diff --git a/examples/pylab_examples/pcolor_log.py b/examples/pylab_examples/pcolor_log.py deleted file mode 100644 index 8a51d84ecd0b..000000000000 --- a/examples/pylab_examples/pcolor_log.py +++ /dev/null @@ -1,24 +0,0 @@ -import matplotlib.pyplot as plt -from matplotlib.colors import LogNorm -import numpy as np -from matplotlib.mlab import bivariate_normal - - -N = 100 -X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)] - -# A low hump with a spike coming out of the top right. -# Needs to have z/colour axis on a log scale so we see both hump and spike. -# linear scale only shows the spike. -Z1 = bivariate_normal(X, Y, 0.1, 0.2, 1.0, 1.0) + 0.1 * bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - -plt.subplot(2, 1, 1) -plt.pcolor(X, Y, Z1, norm=LogNorm(vmin=Z1.min(), vmax=Z1.max()), cmap='PuBu_r') -plt.colorbar() - -plt.subplot(2, 1, 2) -plt.pcolor(X, Y, Z1, cmap='PuBu_r') -plt.colorbar() - - -plt.show() diff --git a/examples/pylab_examples/pcolor_small.py b/examples/pylab_examples/pcolor_small.py deleted file mode 100644 index 8034103ef4db..000000000000 --- a/examples/pylab_examples/pcolor_small.py +++ /dev/null @@ -1,14 +0,0 @@ -import matplotlib.pyplot as plt -from numpy.random import rand - -Z = rand(6, 10) - -plt.subplot(2, 1, 1) -c = plt.pcolor(Z) -plt.title('default: no edges') - -plt.subplot(2, 1, 2) -c = plt.pcolor(Z, edgecolors='k', linewidths=4) -plt.title('thick edges') - -plt.show() diff --git a/examples/pylab_examples/pie_demo2.py b/examples/pylab_examples/pie_demo2.py deleted file mode 100644 index c96d5a83ccdc..000000000000 --- a/examples/pylab_examples/pie_demo2.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Make a pie charts of varying size - see -http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.pie for the docstring. - -This example shows a basic pie charts with labels optional features, -like autolabeling the percentage, offsetting a slice with "explode" -and adding a shadow, in different sizes. - -""" -import matplotlib.pyplot as plt -from matplotlib.gridspec import GridSpec - -# Some data - -labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' -fracs = [15, 30, 45, 10] - -explode = (0, 0.05, 0, 0) - -# Make square figures and axes - -the_grid = GridSpec(2, 2) - -plt.subplot(the_grid[0, 0], aspect=1) - -plt.pie(fracs, labels=labels, autopct='%1.1f%%', shadow=True) - -plt.subplot(the_grid[0, 1], aspect=1) - -plt.pie(fracs, explode=explode, labels=labels, autopct='%.0f%%', shadow=True) - -plt.subplot(the_grid[1, 0], aspect=1) - -patches, texts, autotexts = plt.pie(fracs, labels=labels, - autopct='%.0f%%', - shadow=True, radius=0.5) - -# Make the labels on the small plot easier to read. -for t in texts: - t.set_size('smaller') -for t in autotexts: - t.set_size('x-small') -autotexts[0].set_color('y') - -plt.subplot(the_grid[1, 1], aspect=1) - -# Turn off shadow for tiny plot with exploded slice. -patches, texts, autotexts = plt.pie(fracs, explode=explode, - labels=labels, autopct='%.0f%%', - shadow=False, radius=0.5) -for t in texts: - t.set_size('smaller') -for t in autotexts: - t.set_size('x-small') -autotexts[0].set_color('y') - -plt.show() diff --git a/examples/pylab_examples/plotfile_demo.py b/examples/pylab_examples/plotfile_demo.py deleted file mode 100644 index c9ef123c8992..000000000000 --- a/examples/pylab_examples/plotfile_demo.py +++ /dev/null @@ -1,39 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -import matplotlib.cbook as cbook - -fname = cbook.get_sample_data('msft.csv', asfileobj=False) -fname2 = cbook.get_sample_data('data_x_x2_x3.csv', asfileobj=False) - -# test 1; use ints -plt.plotfile(fname, (0, 5, 6)) - -# test 2; use names -plt.plotfile(fname, ('date', 'volume', 'adj_close')) - -# test 3; use semilogy for volume -plt.plotfile(fname, ('date', 'volume', 'adj_close'), - plotfuncs={'volume': 'semilogy'}) - -# test 4; use semilogy for volume -plt.plotfile(fname, (0, 5, 6), plotfuncs={5: 'semilogy'}) - -# test 5; single subplot -plt.plotfile(fname, ('date', 'open', 'high', 'low', 'close'), subplots=False) - -# test 6; labeling, if no names in csv-file -plt.plotfile(fname2, cols=(0, 1, 2), delimiter=' ', - names=['$x$', '$f(x)=x^2$', '$f(x)=x^3$']) - -# test 7; more than one file per figure--illustrated here with a single file -plt.plotfile(fname2, cols=(0, 1), delimiter=' ') -plt.plotfile(fname2, cols=(0, 2), newfig=False, - delimiter=' ') # use current figure -plt.xlabel(r'$x$') -plt.ylabel(r'$f(x) = x^2, x^3$') - -# test 8; use bar for volume -plt.plotfile(fname, (0, 5, 6), plotfuncs={5: 'bar'}) - -plt.show() diff --git a/examples/pylab_examples/polar_demo.py b/examples/pylab_examples/polar_demo.py deleted file mode 100644 index 149f505b2ba3..000000000000 --- a/examples/pylab_examples/polar_demo.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Demo of a line plot on a polar axis. -""" -import numpy as np -import matplotlib.pyplot as plt - - -r = np.arange(0, 3.0, 0.01) -theta = 2 * np.pi * r - -ax = plt.subplot(111, projection='polar') -ax.plot(theta, r) -ax.set_rmax(2.0) -ax.grid(True) - -ax.set_title("A line plot on a polar axis", va='bottom') -plt.show() diff --git a/examples/pylab_examples/polar_legend.py b/examples/pylab_examples/polar_legend.py deleted file mode 100755 index a249755c32b4..000000000000 --- a/examples/pylab_examples/polar_legend.py +++ /dev/null @@ -1,20 +0,0 @@ -import numpy as np -from matplotlib.pyplot import figure, show, rc - -# radar green, solid grid lines -rc('grid', color='#316931', linewidth=1, linestyle='-') -rc('xtick', labelsize=15) -rc('ytick', labelsize=15) - -# force square figure and square axes looks better for polar, IMO -fig = figure(figsize=(8, 8)) -ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], - projection='polar', facecolor='#d5de9c') - -r = np.arange(0, 3.0, 0.01) -theta = 2*np.pi*r -ax.plot(theta, r, color='#ee8d18', lw=3, label='a line') -ax.plot(0.5*theta, r, color='blue', ls='--', lw=3, label='another line') -ax.legend() - -show() diff --git a/examples/pylab_examples/print_stdout.py b/examples/pylab_examples/print_stdout.py deleted file mode 100644 index a5725888e4e6..000000000000 --- a/examples/pylab_examples/print_stdout.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- noplot -*- -# print png to standard out -# usage: python print_stdout.py > somefile.png - -import sys -import matplotlib -matplotlib.use('Agg') -import matplotlib.pyplot as plt - -plt.plot([1, 2, 3]) - -plt.savefig(sys.stdout) -plt.show() diff --git a/examples/pylab_examples/psd_demo.py b/examples/pylab_examples/psd_demo.py deleted file mode 100644 index 61dd7e1c12ca..000000000000 --- a/examples/pylab_examples/psd_demo.py +++ /dev/null @@ -1,35 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -dt = 0.01 -t = np.arange(0, 10, dt) -nse = np.random.randn(len(t)) -r = np.exp(-t/0.05) - -cnse = np.convolve(nse, r)*dt -cnse = cnse[:len(t)] -s = 0.1*np.sin(2*np.pi*t) + cnse - -plt.subplot(211) -plt.plot(t, s) -plt.subplot(212) -plt.psd(s, 512, 1/dt) - -plt.show() - -""" -% compare with MATLAB -dt = 0.01; -t = [0:dt:10]; -nse = randn(size(t)); -r = exp(-t/0.05); -cnse = conv(nse, r)*dt; -cnse = cnse(1:length(t)); -s = 0.1*sin(2*pi*t) + cnse; - -subplot(211) -plot(t,s) -subplot(212) -psd(s, 512, 1/dt) - -""" diff --git a/examples/pylab_examples/psd_demo2.py b/examples/pylab_examples/psd_demo2.py deleted file mode 100644 index 0402c022e9f1..000000000000 --- a/examples/pylab_examples/psd_demo2.py +++ /dev/null @@ -1,42 +0,0 @@ -# This example shows the effects of some of the different PSD parameters -import numpy as np -import matplotlib.pyplot as plt - -dt = np.pi / 100. -fs = 1. / dt -t = np.arange(0, 8, dt) -y = 10. * np.sin(2 * np.pi * 4 * t) + 5. * np.sin(2 * np.pi * 4.25 * t) -y = y + np.random.randn(*t.shape) - -# Plot the raw time series -fig = plt.figure() -fig.subplots_adjust(hspace=0.45, wspace=0.3) -ax = fig.add_subplot(2, 1, 1) -ax.plot(t, y) - -# Plot the PSD with different amounts of zero padding. This uses the entire -# time series at once -ax2 = fig.add_subplot(2, 3, 4) -ax2.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) -ax2.psd(y, NFFT=len(t), pad_to=len(t)*2, Fs=fs) -ax2.psd(y, NFFT=len(t), pad_to=len(t)*4, Fs=fs) -plt.title('zero padding') - -# Plot the PSD with different block sizes, Zero pad to the length of the -# original data sequence. -ax3 = fig.add_subplot(2, 3, 5, sharex=ax2, sharey=ax2) -ax3.psd(y, NFFT=len(t), pad_to=len(t), Fs=fs) -ax3.psd(y, NFFT=len(t)//2, pad_to=len(t), Fs=fs) -ax3.psd(y, NFFT=len(t)//4, pad_to=len(t), Fs=fs) -ax3.set_ylabel('') -plt.title('block size') - -# Plot the PSD with different amounts of overlap between blocks -ax4 = fig.add_subplot(2, 3, 6, sharex=ax2, sharey=ax2) -ax4.psd(y, NFFT=len(t)//2, pad_to=len(t), noverlap=0, Fs=fs) -ax4.psd(y, NFFT=len(t)//2, pad_to=len(t), noverlap=int(0.05*len(t)/2.), Fs=fs) -ax4.psd(y, NFFT=len(t)//2, pad_to=len(t), noverlap=int(0.2*len(t)/2.), Fs=fs) -ax4.set_ylabel('') -plt.title('overlap') - -plt.show() diff --git a/examples/pylab_examples/psd_demo3.py b/examples/pylab_examples/psd_demo3.py deleted file mode 100644 index 7c12026b664b..000000000000 --- a/examples/pylab_examples/psd_demo3.py +++ /dev/null @@ -1,43 +0,0 @@ -"""This is a ported version of a MATLAB example from the signal -processing toolbox that showed some difference at one time between -Matplotlib's and MATLAB's scaling of the PSD. - -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab - -prng = np.random.RandomState(123456) # to ensure reproducibility - -fs = 1000 -t = np.linspace(0, 0.3, 301) -A = np.array([2, 8]).reshape(-1, 1) -f = np.array([150, 140]).reshape(-1, 1) -xn = (A * np.sin(2 * np.pi * f * t)).sum(axis=0) + 5 * prng.randn(*t.shape) - -fig, (ax0, ax1) = plt.subplots(ncols=2) - -fig.subplots_adjust(hspace=0.45, wspace=0.3) -yticks = np.arange(-50, 30, 10) -yrange = (yticks[0], yticks[-1]) -xticks = np.arange(0, 550, 100) - -ax0.psd(xn, NFFT=301, Fs=fs, window=mlab.window_none, pad_to=1024, - scale_by_freq=True) -ax0.set_title('Periodogram') -ax0.set_yticks(yticks) -ax0.set_xticks(xticks) -ax0.grid(True) -ax0.set_ylim(yrange) - -ax1.psd(xn, NFFT=150, Fs=fs, window=mlab.window_none, pad_to=512, noverlap=75, - scale_by_freq=True) -ax1.set_title('Welch') -ax1.set_xticks(xticks) -ax1.set_yticks(yticks) -ax1.set_ylabel('') # overwrite the y-label added by `psd` -ax1.grid(True) -ax1.set_ylim(yrange) - -plt.show() diff --git a/examples/pylab_examples/psd_demo_complex.py b/examples/pylab_examples/psd_demo_complex.py deleted file mode 100644 index 334651837c38..000000000000 --- a/examples/pylab_examples/psd_demo_complex.py +++ /dev/null @@ -1,46 +0,0 @@ -"""This is a ported version of a MATLAB example from the signal -processing toolbox that showed some difference at one time between -Matplotlib's and MATLAB's scaling of the PSD. - -This differs from psd_demo3.py in that this uses a complex signal, -so we can see that complex PSD's work properly - -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab - -prng = np.random.RandomState(123456) # to ensure reproducibility - -fs = 1000 -t = np.linspace(0, 0.3, 301) -A = np.array([2, 8]).reshape(-1, 1) -f = np.array([150, 140]).reshape(-1, 1) -xn = (A * np.exp(2j * np.pi * f * t)).sum(axis=0) + 5 * prng.randn(*t.shape) - -fig, (ax0, ax1) = plt.subplots(ncols=2) - -fig.subplots_adjust(hspace=0.45, wspace=0.3) -yticks = np.arange(-50, 30, 10) -yrange = (yticks[0], yticks[-1]) -xticks = np.arange(-500, 550, 200) - -ax0.psd(xn, NFFT=301, Fs=fs, window=mlab.window_none, pad_to=1024, - scale_by_freq=True) -ax0.set_title('Periodogram') -ax0.set_yticks(yticks) -ax0.set_xticks(xticks) -ax0.grid(True) -ax0.set_ylim(yrange) - -ax1.psd(xn, NFFT=150, Fs=fs, window=mlab.window_none, pad_to=512, noverlap=75, - scale_by_freq=True) -ax1.set_title('Welch') -ax1.set_xticks(xticks) -ax1.set_yticks(yticks) -ax1.set_ylabel('') # overwrite the y-label added by `psd` -ax1.grid(True) -ax1.set_ylim(yrange) - -plt.show() diff --git a/examples/pylab_examples/pythonic_matplotlib.py b/examples/pylab_examples/pythonic_matplotlib.py deleted file mode 100644 index 5afdaabac68d..000000000000 --- a/examples/pylab_examples/pythonic_matplotlib.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Some people prefer to write more pythonic, object-oriented code -rather than use the pyplot interface to matplotlib. This example shows -you how. - -Unless you are an application developer, I recommend using part of the -pyplot interface, particularly the figure, close, subplot, axes, and -show commands. These hide a lot of complexity from you that you don't -need to see in normal figure creation, like instantiating DPI -instances, managing the bounding boxes of the figure elements, -creating and reaslizing GUI windows and embedding figures in them. - - -If you are an application developer and want to embed matplotlib in -your application, follow the lead of examples/embedding_in_wx.py, -examples/embedding_in_gtk.py or examples/embedding_in_tk.py. In this -case you will want to control the creation of all your figures, -embedding them in application windows, etc. - -If you are a web application developer, you may want to use the -example in webapp_demo.py, which shows how to use the backend agg -figure canvase directly, with none of the globals (current figure, -current axes) that are present in the pyplot interface. Note that -there is no reason why the pyplot interface won't work for web -application developers, however. - -If you see an example in the examples dir written in pyplot interface, -and you want to emulate that using the true python method calls, there -is an easy mapping. Many of those examples use 'set' to control -figure properties. Here's how to map those commands onto instance -methods - -The syntax of set is - - plt.setp(object or sequence, somestring, attribute) - -if called with an object, set calls - - object.set_somestring(attribute) - -if called with a sequence, set does - - for object in sequence: - object.set_somestring(attribute) - -So for your example, if a is your axes object, you can do - - a.set_xticklabels([]) - a.set_yticklabels([]) - a.set_xticks([]) - a.set_yticks([]) -""" - - -from matplotlib.pyplot import figure, show -from numpy import arange, sin, pi - -t = arange(0.0, 1.0, 0.01) - -fig = figure(1) - -ax1 = fig.add_subplot(211) -ax1.plot(t, sin(2*pi*t)) -ax1.grid(True) -ax1.set_ylim((-2, 2)) -ax1.set_ylabel('1 Hz') -ax1.set_title('A sine wave or two') - -for label in ax1.get_xticklabels(): - label.set_color('r') - - -ax2 = fig.add_subplot(212) -ax2.plot(t, sin(2*2*pi*t)) -ax2.grid(True) -ax2.set_ylim((-2, 2)) -l = ax2.set_xlabel('Hi mom') -l.set_color('g') -l.set_fontsize('large') - -show() diff --git a/examples/pylab_examples/quadmesh_demo.py b/examples/pylab_examples/quadmesh_demo.py deleted file mode 100755 index 8f6ab8ae189a..000000000000 --- a/examples/pylab_examples/quadmesh_demo.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -pcolormesh uses a QuadMesh, a faster generalization of pcolor, but -with some restrictions. - -This demo illustrates a bug in quadmesh with masked data. -""" - -import numpy as np -from matplotlib.pyplot import figure, show, savefig -from matplotlib import cm, colors -from numpy import ma - -n = 12 -x = np.linspace(-1.5, 1.5, n) -y = np.linspace(-1.5, 1.5, n*2) -X, Y = np.meshgrid(x, y) -Qx = np.cos(Y) - np.cos(X) -Qz = np.sin(Y) + np.sin(X) -Qx = (Qx + 1.1) -Z = np.sqrt(X**2 + Y**2)/5 -Z = (Z - Z.min()) / (Z.max() - Z.min()) - -# The color array can include masked values: -Zm = ma.masked_where(np.fabs(Qz) < 0.5*np.amax(Qz), Z) - - -fig = figure() -ax = fig.add_subplot(121) -ax.pcolormesh(Qx, Qz, Z, shading='gouraud') -ax.set_title('Without masked values') - -ax = fig.add_subplot(122) -# You can control the color of the masked region: -# cmap = cm.RdBu -# cmap.set_bad('y', 1.0) -# ax.pcolormesh(Qx, Qz, Zm, cmap=cmap) -# Or use the default, which is transparent: -col = ax.pcolormesh(Qx, Qz, Zm, shading='gouraud') -ax.set_title('With masked values') - - -show() diff --git a/examples/pylab_examples/quiver_demo.py b/examples/pylab_examples/quiver_demo.py deleted file mode 100644 index 72bb275808be..000000000000 --- a/examples/pylab_examples/quiver_demo.py +++ /dev/null @@ -1,96 +0,0 @@ -''' -Demonstration of quiver and quiverkey functions. This is using the -new version coming from the code in quiver.py. - -Known problem: the plot autoscaling does not take into account -the arrows, so those on the boundaries are often out of the picture. -This is *not* an easy problem to solve in a perfectly general way. -The workaround is to manually expand the axes. - -''' -import matplotlib.pyplot as plt -import numpy as np -from numpy import ma - -X, Y = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2)) -U = np.cos(X) -V = np.sin(Y) - -# 1 -plt.figure() -Q = plt.quiver(U, V) -qk = plt.quiverkey(Q, 0.5, 0.98, 2, r'$2 \frac{m}{s}$', labelpos='W', - fontproperties={'weight': 'bold'}) -l, r, b, t = plt.axis() -dx, dy = r - l, t - b -plt.axis([l - 0.05*dx, r + 0.05*dx, b - 0.05*dy, t + 0.05*dy]) - -plt.title('Minimal arguments, no kwargs') - -# 2 -plt.figure() -Q = plt.quiver(X, Y, U, V, units='width') -qk = plt.quiverkey(Q, 0.9, 0.95, 2, r'$2 \frac{m}{s}$', - labelpos='E', - coordinates='figure', - fontproperties={'weight': 'bold'}) -plt.axis([-1, 7, -1, 7]) -plt.title('scales with plot width, not view') - -# 3 -plt.figure() -Q = plt.quiver(X[::3, ::3], Y[::3, ::3], U[::3, ::3], V[::3, ::3], - pivot='mid', color='r', units='inches') -qk = plt.quiverkey(Q, 0.5, 0.03, 1, r'$1 \frac{m}{s}$', - fontproperties={'weight': 'bold'}) -plt.plot(X[::3, ::3], Y[::3, ::3], 'k.') -plt.axis([-1, 7, -1, 7]) -plt.title("pivot='mid'; every third arrow; units='inches'") - -# 4 -plt.figure() -M = np.hypot(U, V) -Q = plt.quiver(X, Y, U, V, M, - units='x', - pivot='tip', - width=0.022, - scale=1 / 0.15) -qk = plt.quiverkey(Q, 0.9, 1.05, 1, r'$1 \frac{m}{s}$', - labelpos='E', - fontproperties={'weight': 'bold'}) -plt.plot(X, Y, 'k.', markersize=2) -plt.axis([-1, 7, -1, 7]) -plt.title("scales with x view; pivot='tip'") - -# 5 -plt.figure() -Q = plt.quiver(X[::3, ::3], Y[::3, ::3], U[::3, ::3], V[::3, ::3], - color='r', units='x', - linewidths=(0.5,), edgecolors=('k'), headaxislength=5) -qk = plt.quiverkey(Q, 0.5, 0.03, 1, r'$1 \frac{m}{s}$', - fontproperties={'weight': 'bold'}) -plt.axis([-1, 7, -1, 7]) -plt.title("triangular head; scale with x view; black edges") - -# 6 -plt.figure() -M = np.zeros(U.shape, dtype='bool') -XMaskStart = U.shape[0]//3 -YMaskStart = U.shape[1]//3 -XMaskStop = 2*U.shape[0]//3 -YMaskStop = 2*U.shape[1]//3 - -M[XMaskStart:XMaskStop, - YMaskStart:YMaskStop] = True -U = ma.masked_array(U, mask=M) -V = ma.masked_array(V, mask=M) -Q = plt.quiver(U, V) -qk = plt.quiverkey(Q, 0.5, 0.98, 2, r'$2 \frac{m}{s}$', labelpos='W', - fontproperties={'weight': 'bold'}) -l, r, b, t = plt.axis() -dx, dy = r - l, t - b -plt.axis([l - 0.05 * dx, r + 0.05 * dx, b - 0.05 * dy, t + 0.05 * dy]) -plt.title('Minimal arguments, no kwargs - masked values') - - -plt.show() diff --git a/examples/pylab_examples/scatter_custom_symbol.py b/examples/pylab_examples/scatter_custom_symbol.py deleted file mode 100644 index abd2ebc4381c..000000000000 --- a/examples/pylab_examples/scatter_custom_symbol.py +++ /dev/null @@ -1,17 +0,0 @@ -import matplotlib.pyplot as plt -from numpy import arange, pi, cos, sin -from numpy.random import rand - -# unit area ellipse -rx, ry = 3., 1. -area = rx * ry * pi -theta = arange(0, 2*pi + 0.01, 0.1) -verts = list(zip(rx/area*cos(theta), ry/area*sin(theta))) - -x, y, s, c = rand(4, 30) -s *= 10**2. - -fig, ax = plt.subplots() -ax.scatter(x, y, s, c, marker=None, verts=verts) - -plt.show() diff --git a/examples/pylab_examples/scatter_demo2.py b/examples/pylab_examples/scatter_demo2.py deleted file mode 100644 index a4bca3a4b372..000000000000 --- a/examples/pylab_examples/scatter_demo2.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Demo of scatter plot with varying marker colors and sizes. -""" -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -# Load a numpy record array from yahoo csv data with fields date, -# open, close, volume, adj_close from the mpl-data/example directory. -# The record array stores python datetime.date as an object array in -# the date column -datafile = cbook.get_sample_data('goog.npy') -try: - # Python3 cannot load python2 .npy files with datetime(object) arrays - # unless the encoding is set to bytes. Hovever this option was - # not added until numpy 1.10 so this example will only work with - # python 2 or with numpy 1.10 and later - price_data = np.load(datafile, encoding='bytes').view(np.recarray) -except TypeError: - price_data = np.load(datafile).view(np.recarray) -price_data = price_data[-250:] # get the most recent 250 trading days - -delta1 = np.diff(price_data.adj_close)/price_data.adj_close[:-1] - -# Marker size in units of points^2 -volume = (15 * price_data.volume[:-2] / price_data.volume[0])**2 -close = 0.003 * price_data.close[:-2] / 0.003 * price_data.open[:-2] - -fig, ax = plt.subplots() -ax.scatter(delta1[:-1], delta1[1:], c=close, s=volume, alpha=0.5) - -ax.set_xlabel(r'$\Delta_i$', fontsize=20) -ax.set_ylabel(r'$\Delta_{i+1}$', fontsize=20) -ax.set_title('Volume and percent change') - -ax.grid(True) -fig.tight_layout() - -plt.show() diff --git a/examples/pylab_examples/scatter_hist.py b/examples/pylab_examples/scatter_hist.py deleted file mode 100644 index a271cf729e61..000000000000 --- a/examples/pylab_examples/scatter_hist.py +++ /dev/null @@ -1,49 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.ticker import NullFormatter - -# the random data -x = np.random.randn(1000) -y = np.random.randn(1000) - -nullfmt = NullFormatter() # no labels - -# definitions for the axes -left, width = 0.1, 0.65 -bottom, height = 0.1, 0.65 -bottom_h = left_h = left + width + 0.02 - -rect_scatter = [left, bottom, width, height] -rect_histx = [left, bottom_h, width, 0.2] -rect_histy = [left_h, bottom, 0.2, height] - -# start with a rectangular Figure -plt.figure(1, figsize=(8, 8)) - -axScatter = plt.axes(rect_scatter) -axHistx = plt.axes(rect_histx) -axHisty = plt.axes(rect_histy) - -# no labels -axHistx.xaxis.set_major_formatter(nullfmt) -axHisty.yaxis.set_major_formatter(nullfmt) - -# the scatter plot: -axScatter.scatter(x, y) - -# now determine nice limits by hand: -binwidth = 0.25 -xymax = np.max([np.max(np.fabs(x)), np.max(np.fabs(y))]) -lim = (int(xymax/binwidth) + 1) * binwidth - -axScatter.set_xlim((-lim, lim)) -axScatter.set_ylim((-lim, lim)) - -bins = np.arange(-lim, lim + binwidth, binwidth) -axHistx.hist(x, bins=bins) -axHisty.hist(y, bins=bins, orientation='horizontal') - -axHistx.set_xlim(axScatter.get_xlim()) -axHisty.set_ylim(axScatter.get_ylim()) - -plt.show() diff --git a/examples/pylab_examples/scatter_masked.py b/examples/pylab_examples/scatter_masked.py deleted file mode 100644 index 5ceb26d2711b..000000000000 --- a/examples/pylab_examples/scatter_masked.py +++ /dev/null @@ -1,19 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -N = 100 -r0 = 0.6 -x = 0.9*np.random.rand(N) -y = 0.9*np.random.rand(N) -area = np.pi*(10 * np.random.rand(N))**2 # 0 to 10 point radiuses -c = np.sqrt(area) -r = np.sqrt(x*x + y*y) -area1 = np.ma.masked_where(r < r0, area) -area2 = np.ma.masked_where(r >= r0, area) -plt.scatter(x, y, s=area1, marker='^', c=c, hold='on') -plt.scatter(x, y, s=area2, marker='o', c=c) -# Show the boundary between the regions: -theta = np.arange(0, np.pi/2, 0.01) -plt.plot(r0*np.cos(theta), r0*np.sin(theta)) - -plt.show() diff --git a/examples/pylab_examples/scatter_profile.py b/examples/pylab_examples/scatter_profile.py deleted file mode 100755 index 8092f3bc171b..000000000000 --- a/examples/pylab_examples/scatter_profile.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -N Classic Base renderer Ext renderer -20 0.22 0.14 0.14 -100 0.16 0.14 0.13 -1000 0.45 0.26 0.17 -10000 3.30 1.31 0.53 -50000 19.30 6.53 1.98 -""" -from __future__ import print_function # only needed for python 2.x -import matplotlib.pyplot as plt -import numpy as np - -import time - -for N in (20, 100, 1000, 10000, 50000): - tstart = time.time() - x = 0.9*np.random.rand(N) - y = 0.9*np.random.rand(N) - s = 20*np.random.rand(N) - plt.scatter(x, y, s) - print('%d symbols in %1.2f s' % (N, time.time() - tstart)) diff --git a/examples/pylab_examples/scatter_star_poly.py b/examples/pylab_examples/scatter_star_poly.py deleted file mode 100644 index 68eb33b32b89..000000000000 --- a/examples/pylab_examples/scatter_star_poly.py +++ /dev/null @@ -1,30 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - - -x = np.random.rand(10) -y = np.random.rand(10) -z = np.sqrt(x**2 + y**2) - -plt.subplot(321) -plt.scatter(x, y, s=80, c=z, marker=">") - -plt.subplot(322) -plt.scatter(x, y, s=80, c=z, marker=(5, 0)) - -verts = list(zip([-1., 1., 1., -1.], [-1., -1., 1., -1.])) -plt.subplot(323) -plt.scatter(x, y, s=80, c=z, marker=(verts, 0)) -# equivalent: -#plt.scatter(x,y,s=80, c=z, marker=None, verts=verts) - -plt.subplot(324) -plt.scatter(x, y, s=80, c=z, marker=(5, 1)) - -plt.subplot(325) -plt.scatter(x, y, s=80, c=z, marker='+') - -plt.subplot(326) -plt.scatter(x, y, s=80, c=z, marker=(5, 2)) - -plt.show() diff --git a/examples/pylab_examples/scatter_symbol.py b/examples/pylab_examples/scatter_symbol.py deleted file mode 100644 index bc805afbed0e..000000000000 --- a/examples/pylab_examples/scatter_symbol.py +++ /dev/null @@ -1,14 +0,0 @@ -from matplotlib import pyplot as plt -import numpy as np -import matplotlib - -x = np.arange(0.0, 50.0, 2.0) -y = x ** 1.3 + np.random.rand(*x.shape) * 30.0 -s = np.random.rand(*x.shape) * 800 + 500 - -plt.scatter(x, y, s, c="g", alpha=0.5, marker=r'$\clubsuit$', - label="Luck") -plt.xlabel("Leprechauns") -plt.ylabel("Gold") -plt.legend(loc=2) -plt.show() diff --git a/examples/pylab_examples/set_and_get.py b/examples/pylab_examples/set_and_get.py deleted file mode 100644 index 7004481bce19..000000000000 --- a/examples/pylab_examples/set_and_get.py +++ /dev/null @@ -1,99 +0,0 @@ -""" - -The pyplot interface allows you to use setp and getp to set and get -object properties, as well as to do introspection on the object - -set: - To set the linestyle of a line to be dashed, you can do - - >>> line, = plt.plot([1,2,3]) - >>> plt.setp(line, linestyle='--') - - If you want to know the valid types of arguments, you can provide the - name of the property you want to set without a value - - >>> plt.setp(line, 'linestyle') - linestyle: [ '-' | '--' | '-.' | ':' | 'steps' | 'None' ] - - If you want to see all the properties that can be set, and their - possible values, you can do - - >>> plt.setp(line) - - set operates on a single instance or a list of instances. If you are - in query mode introspecting the possible values, only the first - instance in the sequence is used. When actually setting values, all - the instances will be set. e.g., suppose you have a list of two lines, - the following will make both lines thicker and red - - >>> x = np.arange(0,1.0,0.01) - >>> y1 = np.sin(2*np.pi*x) - >>> y2 = np.sin(4*np.pi*x) - >>> lines = plt.plot(x, y1, x, y2) - >>> plt.setp(lines, linewidth=2, color='r') - - -get: - - get returns the value of a given attribute. You can use get to query - the value of a single attribute - - >>> plt.getp(line, 'linewidth') - 0.5 - - or all the attribute/value pairs - - >>> plt.getp(line) - aa = True - alpha = 1.0 - antialiased = True - c = b - clip_on = True - color = b - ... long listing skipped ... - -Aliases: - - To reduce keystrokes in interactive mode, a number of properties - have short aliases, e.g., 'lw' for 'linewidth' and 'mec' for - 'markeredgecolor'. When calling set or get in introspection mode, - these properties will be listed as 'fullname or aliasname', as in - - - - -""" - -from __future__ import print_function - -import matplotlib.pyplot as plt -import numpy as np - - -x = np.arange(0, 1.0, 0.01) -y1 = np.sin(2*np.pi*x) -y2 = np.sin(4*np.pi*x) -lines = plt.plot(x, y1, x, y2) -l1, l2 = lines -plt.setp(lines, linestyle='--') # set both to dashed -plt.setp(l1, linewidth=2, color='r') # line1 is thick and red -plt.setp(l2, linewidth=1, color='g') # line2 is thicker and green - - -print('Line setters') -plt.setp(l1) -print('Line getters') -plt.getp(l1) - -print('Rectangle setters') -plt.setp(plt.gca().patch) -print('Rectangle getters') -plt.getp(plt.gca().patch) - -t = plt.title('Hi mom') -print('Text setters') -plt.setp(t) -print('Text getters') -plt.getp(t) - -plt.show() diff --git a/examples/pylab_examples/shading_example.py b/examples/pylab_examples/shading_example.py deleted file mode 100644 index 12ce6b379699..000000000000 --- a/examples/pylab_examples/shading_example.py +++ /dev/null @@ -1,57 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.colors import LightSource -from matplotlib.cbook import get_sample_data - -# Example showing how to make shaded relief plots -# like Mathematica -# (http://reference.wolfram.com/mathematica/ref/ReliefPlot.html) -# or Generic Mapping Tools -# (http://gmt.soest.hawaii.edu/gmt/doc/gmt/html/GMT_Docs/node145.html) - - -def main(): - # Test data - x, y = np.mgrid[-5:5:0.05, -5:5:0.05] - z = 5 * (np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2)) - - filename = get_sample_data('jacksboro_fault_dem.npz', asfileobj=False) - with np.load(filename) as dem: - elev = dem['elevation'] - - fig = compare(z, plt.cm.copper) - fig.suptitle('HSV Blending Looks Best with Smooth Surfaces', y=0.95) - - fig = compare(elev, plt.cm.gist_earth, ve=0.05) - fig.suptitle('Overlay Blending Looks Best with Rough Surfaces', y=0.95) - - plt.show() - - -def compare(z, cmap, ve=1): - # Create subplots and hide ticks - fig, axes = plt.subplots(ncols=2, nrows=2) - for ax in axes.flat: - ax.set(xticks=[], yticks=[]) - - # Illuminate the scene from the northwest - ls = LightSource(azdeg=315, altdeg=45) - - axes[0, 0].imshow(z, cmap=cmap) - axes[0, 0].set(xlabel='Colormapped Data') - - axes[0, 1].imshow(ls.hillshade(z, vert_exag=ve), cmap='gray') - axes[0, 1].set(xlabel='Illumination Intensity') - - rgb = ls.shade(z, cmap=cmap, vert_exag=ve, blend_mode='hsv') - axes[1, 0].imshow(rgb) - axes[1, 0].set(xlabel='Blend Mode: "hsv" (default)') - - rgb = ls.shade(z, cmap=cmap, vert_exag=ve, blend_mode='overlay') - axes[1, 1].imshow(rgb) - axes[1, 1].set(xlabel='Blend Mode: "overlay"') - - return fig - -if __name__ == '__main__': - main() diff --git a/examples/pylab_examples/shared_axis_across_figures.py b/examples/pylab_examples/shared_axis_across_figures.py deleted file mode 100644 index 8cbfb9df680d..000000000000 --- a/examples/pylab_examples/shared_axis_across_figures.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -connect the data limits on the axes in one figure with the axes in -another. This is not the right way to do this for two axes in the -same figure -- use the sharex and sharey property in that case -""" -import numpy as np -import matplotlib.pyplot as plt - -fig1 = plt.figure() -fig2 = plt.figure() - -ax1 = fig1.add_subplot(111) -ax2 = fig2.add_subplot(111, sharex=ax1, sharey=ax1) - -ax1.plot(np.random.rand(100), 'o') -ax2.plot(np.random.rand(100), 'o') - -# In the latest release, it is no longer necessary to do anything -# special to share axes across figures: - -# ax1.sharex_foreign(ax2) -# ax2.sharex_foreign(ax1) - -# ax1.sharey_foreign(ax2) -# ax2.sharey_foreign(ax1) - -plt.show() diff --git a/examples/pylab_examples/shared_axis_demo.py b/examples/pylab_examples/shared_axis_demo.py deleted file mode 100644 index e92b323e0312..000000000000 --- a/examples/pylab_examples/shared_axis_demo.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -You can share the x or y axis limits for one axis with another by -passing an axes instance as a sharex or sharey kwarg. - -Changing the axis limits on one axes will be reflected automatically -in the other, and vice-versa, so when you navigate with the toolbar -the axes will follow each other on their shared axes. Ditto for -changes in the axis scaling (e.g., log vs linear). However, it is -possible to have differences in tick labeling, e.g., you can selectively -turn off the tick labels on one axes. - -The example below shows how to customize the tick labels on the -various axes. Shared axes share the tick locator, tick formatter, -view limits, and transformation (e.g., log, linear). But the ticklabels -themselves do not share properties. This is a feature and not a bug, -because you may want to make the tick labels smaller on the upper -axes, e.g., in the example below. - -If you want to turn off the ticklabels for a given axes (e.g., on -subplot(211) or subplot(212), you cannot do the standard trick - - setp(ax2, xticklabels=[]) - -because this changes the tick Formatter, which is shared among all -axes. But you can alter the visibility of the labels, which is a -property - - setp( ax2.get_xticklabels(), visible=False) - -""" -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.01, 5.0, 0.01) -s1 = np.sin(2*np.pi*t) -s2 = np.exp(-t) -s3 = np.sin(4*np.pi*t) - -ax1 = plt.subplot(311) -plt.plot(t, s1) -plt.setp(ax1.get_xticklabels(), fontsize=6) - -# share x only -ax2 = plt.subplot(312, sharex=ax1) -plt.plot(t, s2) -# make these tick labels invisible -plt.setp(ax2.get_xticklabels(), visible=False) - -# share x and y -ax3 = plt.subplot(313, sharex=ax1, sharey=ax1) -plt.plot(t, s3) -plt.xlim(0.01, 5.0) -plt.show() diff --git a/examples/pylab_examples/simple_plot.py b/examples/pylab_examples/simple_plot.py deleted file mode 100644 index 8afbbea53f46..000000000000 --- a/examples/pylab_examples/simple_plot.py +++ /dev/null @@ -1,13 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -t = np.arange(0.0, 2.0, 0.01) -s = np.sin(2*np.pi*t) -plt.plot(t, s) - -plt.xlabel('time (s)') -plt.ylabel('voltage (mV)') -plt.title('About as simple as it gets, folks') -plt.grid(True) -plt.savefig("test.png") -plt.show() diff --git a/examples/pylab_examples/specgram_demo.py b/examples/pylab_examples/specgram_demo.py deleted file mode 100644 index 63c48ea27fde..000000000000 --- a/examples/pylab_examples/specgram_demo.py +++ /dev/null @@ -1,29 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -dt = 0.0005 -t = np.arange(0.0, 20.0, dt) -s1 = np.sin(2*np.pi*100*t) -s2 = 2*np.sin(2*np.pi*400*t) - -# create a transient "chirp" -mask = np.where(np.logical_and(t > 10, t < 12), 1.0, 0.0) -s2 = s2 * mask - -# add some noise into the mix -nse = 0.01*np.random.random(size=len(t)) - -x = s1 + s2 + nse # the signal -NFFT = 1024 # the length of the windowing segments -Fs = int(1.0/dt) # the sampling frequency - -# Pxx is the segments x freqs array of instantaneous power, freqs is -# the frequency vector, bins are the centers of the time bins in which -# the power is computed, and im is the matplotlib.image.AxesImage -# instance - -ax1 = plt.subplot(211) -plt.plot(t, x) -plt.subplot(212, sharex=ax1) -Pxx, freqs, bins, im = plt.specgram(x, NFFT=NFFT, Fs=Fs, noverlap=900) -plt.show() diff --git a/examples/pylab_examples/spectrum_demo.py b/examples/pylab_examples/spectrum_demo.py deleted file mode 100644 index 3ad242d2c7ae..000000000000 --- a/examples/pylab_examples/spectrum_demo.py +++ /dev/null @@ -1,32 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - - -np.random.seed(0) - -dt = 0.01 -Fs = 1/dt -t = np.arange(0, 10, dt) -nse = np.random.randn(len(t)) -r = np.exp(-t/0.05) - -cnse = np.convolve(nse, r)*dt -cnse = cnse[:len(t)] -s = 0.1*np.sin(2*np.pi*t) + cnse - -plt.subplot(3, 2, 1) -plt.plot(t, s) - -plt.subplot(3, 2, 3) -plt.magnitude_spectrum(s, Fs=Fs) - -plt.subplot(3, 2, 4) -plt.magnitude_spectrum(s, Fs=Fs, scale='dB') - -plt.subplot(3, 2, 5) -plt.angle_spectrum(s, Fs=Fs) - -plt.subplot(3, 2, 6) -plt.phase_spectrum(s, Fs=Fs) - -plt.show() diff --git a/examples/pylab_examples/spine_placement_demo.py b/examples/pylab_examples/spine_placement_demo.py deleted file mode 100644 index 6f955a80aa13..000000000000 --- a/examples/pylab_examples/spine_placement_demo.py +++ /dev/null @@ -1,101 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - - -fig = plt.figure() -x = np.linspace(-np.pi, np.pi, 100) -y = 2*np.sin(x) - -ax = fig.add_subplot(2, 2, 1) -ax.set_title('centered spines') -ax.plot(x, y) -ax.spines['left'].set_position('center') -ax.spines['right'].set_color('none') -ax.spines['bottom'].set_position('center') -ax.spines['top'].set_color('none') -ax.spines['left'].set_smart_bounds(True) -ax.spines['bottom'].set_smart_bounds(True) -ax.xaxis.set_ticks_position('bottom') -ax.yaxis.set_ticks_position('left') - -ax = fig.add_subplot(2, 2, 2) -ax.set_title('zeroed spines') -ax.plot(x, y) -ax.spines['left'].set_position('zero') -ax.spines['right'].set_color('none') -ax.spines['bottom'].set_position('zero') -ax.spines['top'].set_color('none') -ax.spines['left'].set_smart_bounds(True) -ax.spines['bottom'].set_smart_bounds(True) -ax.xaxis.set_ticks_position('bottom') -ax.yaxis.set_ticks_position('left') - -ax = fig.add_subplot(2, 2, 3) -ax.set_title('spines at axes (0.6, 0.1)') -ax.plot(x, y) -ax.spines['left'].set_position(('axes', 0.6)) -ax.spines['right'].set_color('none') -ax.spines['bottom'].set_position(('axes', 0.1)) -ax.spines['top'].set_color('none') -ax.spines['left'].set_smart_bounds(True) -ax.spines['bottom'].set_smart_bounds(True) -ax.xaxis.set_ticks_position('bottom') -ax.yaxis.set_ticks_position('left') - -ax = fig.add_subplot(2, 2, 4) -ax.set_title('spines at data (1, 2)') -ax.plot(x, y) -ax.spines['left'].set_position(('data', 1)) -ax.spines['right'].set_color('none') -ax.spines['bottom'].set_position(('data', 2)) -ax.spines['top'].set_color('none') -ax.spines['left'].set_smart_bounds(True) -ax.spines['bottom'].set_smart_bounds(True) -ax.xaxis.set_ticks_position('bottom') -ax.yaxis.set_ticks_position('left') -# ---------------------------------------------------- - - -def adjust_spines(ax, spines): - for loc, spine in ax.spines.items(): - if loc in spines: - spine.set_position(('outward', 10)) # outward by 10 points - spine.set_smart_bounds(True) - else: - spine.set_color('none') # don't draw spine - - # turn off ticks where there is no spine - if 'left' in spines: - ax.yaxis.set_ticks_position('left') - else: - # no yaxis ticks - ax.yaxis.set_ticks([]) - - if 'bottom' in spines: - ax.xaxis.set_ticks_position('bottom') - else: - # no xaxis ticks - ax.xaxis.set_ticks([]) - -fig = plt.figure() - -x = np.linspace(0, 2*np.pi, 100) -y = 2*np.sin(x) - -ax = fig.add_subplot(2, 2, 1) -ax.plot(x, y, clip_on=False) -adjust_spines(ax, ['left']) - -ax = fig.add_subplot(2, 2, 2) -ax.plot(x, y, clip_on=False) -adjust_spines(ax, []) - -ax = fig.add_subplot(2, 2, 3) -ax.plot(x, y, clip_on=False) -adjust_spines(ax, ['left', 'bottom']) - -ax = fig.add_subplot(2, 2, 4) -ax.plot(x, y, clip_on=False) -adjust_spines(ax, ['bottom']) - -plt.show() diff --git a/examples/pylab_examples/spy_demos.py b/examples/pylab_examples/spy_demos.py deleted file mode 100644 index 2b80b3526063..000000000000 --- a/examples/pylab_examples/spy_demos.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Plot the sparsity pattern of arrays -""" - -from matplotlib.pyplot import figure, show -import numpy - -fig = figure() -ax1 = fig.add_subplot(221) -ax2 = fig.add_subplot(222) -ax3 = fig.add_subplot(223) -ax4 = fig.add_subplot(224) - -x = numpy.random.randn(20, 20) -x[5] = 0. -x[:, 12] = 0. - -ax1.spy(x, markersize=5) -ax2.spy(x, precision=0.1, markersize=5) - -ax3.spy(x) -ax4.spy(x, precision=0.1) - -show() diff --git a/examples/pylab_examples/stackplot_demo.py b/examples/pylab_examples/stackplot_demo.py deleted file mode 100644 index 40b16b194683..000000000000 --- a/examples/pylab_examples/stackplot_demo.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - - -def fnx(): - return np.random.randint(5, 50, 10) - -y = np.row_stack((fnx(), fnx(), fnx())) -x = np.arange(10) - -y1, y2, y3 = fnx(), fnx(), fnx() - -fig, ax = plt.subplots() -ax.stackplot(x, y) -plt.show() - -fig, ax = plt.subplots() -ax.stackplot(x, y1, y2, y3) -plt.show() diff --git a/examples/pylab_examples/stackplot_demo2.py b/examples/pylab_examples/stackplot_demo2.py deleted file mode 100644 index 844a9b11f808..000000000000 --- a/examples/pylab_examples/stackplot_demo2.py +++ /dev/null @@ -1,28 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -np.random.seed(0) - - -def layers(n, m): - """ - Return *n* random Gaussian mixtures, each of length *m*. - """ - def bump(a): - x = 1 / (.1 + np.random.random()) - y = 2 * np.random.random() - .5 - z = 10 / (.1 + np.random.random()) - for i in range(m): - w = (i / float(m) - y) * z - a[i] += x * np.exp(-w * w) - a = np.zeros((m, n)) - for i in range(n): - for j in range(5): - bump(a[:, i]) - return a - -d = layers(3, 100) - -plt.subplots() -plt.stackplot(range(100), d.T, baseline='wiggle') -plt.show() diff --git a/examples/pylab_examples/stem_plot.py b/examples/pylab_examples/stem_plot.py deleted file mode 100644 index 6f10003a8e3f..000000000000 --- a/examples/pylab_examples/stem_plot.py +++ /dev/null @@ -1,8 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -x = np.linspace(0.1, 2*np.pi, 10) -markerline, stemlines, baseline = plt.stem(x, np.cos(x), '-.') -plt.setp(baseline, 'color', 'r', 'linewidth', 2) - -plt.show() diff --git a/examples/pylab_examples/step_demo.py b/examples/pylab_examples/step_demo.py deleted file mode 100644 index a0101be6d4af..000000000000 --- a/examples/pylab_examples/step_demo.py +++ /dev/null @@ -1,25 +0,0 @@ -import numpy as np -from numpy import ma -import matplotlib.pyplot as plt - -x = np.arange(1, 7, 0.4) -y0 = np.sin(x) -y = y0.copy() + 2.5 - -plt.step(x, y, label='pre (default)') - -y -= 0.5 -plt.step(x, y, where='mid', label='mid') - -y -= 0.5 -plt.step(x, y, where='post', label='post') - -y = ma.masked_where((y0 > -0.15) & (y0 < 0.15), y - 0.5) -plt.step(x, y, label='masked (pre)') - -plt.legend() - -plt.xlim(0, 7) -plt.ylim(-0.5, 4) - -plt.show() diff --git a/examples/pylab_examples/stix_fonts_demo.py b/examples/pylab_examples/stix_fonts_demo.py deleted file mode 100755 index 81bd73575cdc..000000000000 --- a/examples/pylab_examples/stix_fonts_demo.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import unicode_literals - -import os -import sys -import re -import gc -import matplotlib.pyplot as plt -import numpy as np - -stests = [ - r'$\mathcircled{123} \mathrm{\mathcircled{123}}' - r' \mathbf{\mathcircled{123}}$', - r'$\mathsf{Sans \Omega} \mathrm{\mathsf{Sans \Omega}}' - r' \mathbf{\mathsf{Sans \Omega}}$', - r'$\mathtt{Monospace}$', - r'$\mathcal{CALLIGRAPHIC}$', - r'$\mathbb{Blackboard \pi}$', - r'$\mathrm{\mathbb{Blackboard \pi}}$', - r'$\mathbf{\mathbb{Blackboard \pi}}$', - r'$\mathfrak{Fraktur} \mathbf{\mathfrak{Fraktur}}$', - r'$\mathscr{Script}$'] - -if sys.maxunicode > 0xffff: - s = r'Direct Unicode: $\u23ce \mathrm{\ue0f2 \U0001D538}$' - - -def doall(): - tests = stests - - plt.figure(figsize=(8, (len(tests) * 1) + 2)) - plt.plot([0, 0], 'r') - plt.grid(False) - plt.axis([0, 3, -len(tests), 0]) - plt.yticks(np.arange(len(tests)) * -1) - for i, s in enumerate(tests): - plt.text(0.1, -i, s, fontsize=32) - - plt.savefig('stix_fonts_example') - plt.show() - - -if '--latex' in sys.argv: - fd = open("stix_fonts_examples.ltx", "w") - fd.write("\\documentclass{article}\n") - fd.write("\\begin{document}\n") - fd.write("\\begin{enumerate}\n") - - for i, s in enumerate(stests): - s = re.sub(r"(?3 might result in a very high number of triangles - # for the refine mesh: new triangles numbering = (4**subdiv)*ntri - -init_mask_frac = 0.0 # Float > 0. adjusting the proportion of - # (invalid) initial triangles which will be masked - # out. Enter 0 for no mask. - -min_circle_ratio = .01 # Minimum circle ratio - border triangles with circle - # ratio below this will be masked if they touch a - # border. Suggested value 0.01 ; Use -1 to keep - # all triangles. - -# Random points -random_gen = np.random.mtrand.RandomState(seed=127260) -x_test = random_gen.uniform(-1., 1., size=n_test) -y_test = random_gen.uniform(-1., 1., size=n_test) -z_test = experiment_res(x_test, y_test) - -# meshing with Delaunay triangulation -tri = Triangulation(x_test, y_test) -ntri = tri.triangles.shape[0] - -# Some invalid data are masked out -mask_init = np.zeros(ntri, dtype=np.bool) -masked_tri = random_gen.randint(0, ntri, int(ntri*init_mask_frac)) -mask_init[masked_tri] = True -tri.set_mask(mask_init) - - -#----------------------------------------------------------------------------- -# Improving the triangulation before high-res plots: removing flat triangles -#----------------------------------------------------------------------------- -# masking badly shaped triangles at the border of the triangular mesh. -mask = TriAnalyzer(tri).get_flat_tri_mask(min_circle_ratio) -tri.set_mask(mask) - -# refining the data -refiner = UniformTriRefiner(tri) -tri_refi, z_test_refi = refiner.refine_field(z_test, subdiv=subdiv) - -# analytical 'results' for comparison -z_expected = experiment_res(tri_refi.x, tri_refi.y) - -# for the demo: loading the 'flat' triangles for plot -flat_tri = Triangulation(x_test, y_test) -flat_tri.set_mask(~mask) - - -#----------------------------------------------------------------------------- -# Now the plots -#----------------------------------------------------------------------------- -# User options for plots -plot_tri = True # plot of base triangulation -plot_masked_tri = True # plot of excessively flat excluded triangles -plot_refi_tri = False # plot of refined triangulation -plot_expected = False # plot of analytical function values for comparison - - -# Graphical options for tricontouring -levels = np.arange(0., 1., 0.025) -cmap = cm.get_cmap(name='Blues', lut=None) - -plt.figure() -plt.gca().set_aspect('equal') -plt.title("Filtering a Delaunay mesh\n" + - "(application to high-resolution tricontouring)") - -# 1) plot of the refined (computed) data countours: -plt.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, - linewidths=[2.0, 0.5, 1.0, 0.5]) -# 2) plot of the expected (analytical) data countours (dashed): -if plot_expected: - plt.tricontour(tri_refi, z_expected, levels=levels, cmap=cmap, - linestyles='--') -# 3) plot of the fine mesh on which interpolation was done: -if plot_refi_tri: - plt.triplot(tri_refi, color='0.97') -# 4) plot of the initial 'coarse' mesh: -if plot_tri: - plt.triplot(tri, color='0.7') -# 4) plot of the unvalidated triangles from naive Delaunay Triangulation: -if plot_masked_tri: - plt.triplot(flat_tri, color='red') - -plt.show() diff --git a/examples/pylab_examples/tricontour_smooth_user.py b/examples/pylab_examples/tricontour_smooth_user.py deleted file mode 100644 index 3c06d553110b..000000000000 --- a/examples/pylab_examples/tricontour_smooth_user.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Demonstrates high-resolution tricontouring on user-defined triangular grids -with matplotlib.tri.UniformTriRefiner -""" -import matplotlib.tri as tri -import matplotlib.pyplot as plt -import matplotlib.cm as cm -import numpy as np -import math - - -#----------------------------------------------------------------------------- -# Analytical test function -#----------------------------------------------------------------------------- -def function_z(x, y): - """ A function of 2 variables """ - r1 = np.sqrt((0.5 - x)**2 + (0.5 - y)**2) - theta1 = np.arctan2(0.5 - x, 0.5 - y) - r2 = np.sqrt((-x - 0.2)**2 + (-y - 0.2)**2) - theta2 = np.arctan2(-x - 0.2, -y - 0.2) - z = -(2*(np.exp((r1/10)**2) - 1)*30. * np.cos(7.*theta1) + - (np.exp((r2/10)**2) - 1)*30. * np.cos(11.*theta2) + - 0.7*(x**2 + y**2)) - return (np.max(z) - z)/(np.max(z) - np.min(z)) - -#----------------------------------------------------------------------------- -# Creating a Triangulation -#----------------------------------------------------------------------------- -# First create the x and y coordinates of the points. -n_angles = 20 -n_radii = 10 -min_radius = 0.15 -radii = np.linspace(min_radius, 0.95, n_radii) - -angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += math.pi/n_angles - -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -z = function_z(x, y) - -# Now create the Triangulation. -# (Creating a Triangulation without specifying the triangles results in the -# Delaunay triangulation of the points.) -triang = tri.Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triang.set_mask(mask) - -#----------------------------------------------------------------------------- -# Refine data -#----------------------------------------------------------------------------- -refiner = tri.UniformTriRefiner(triang) -tri_refi, z_test_refi = refiner.refine_field(z, subdiv=3) - -#----------------------------------------------------------------------------- -# Plot the triangulation and the high-res iso-contours -#----------------------------------------------------------------------------- -plt.figure() -plt.gca().set_aspect('equal') -plt.triplot(triang, lw=0.5, color='white') - -levels = np.arange(0., 1., 0.025) -cmap = cm.get_cmap(name='terrain', lut=None) -plt.tricontourf(tri_refi, z_test_refi, levels=levels, cmap=cmap) -plt.tricontour(tri_refi, z_test_refi, levels=levels, - colors=['0.25', '0.5', '0.5', '0.5', '0.5'], - linewidths=[1.0, 0.5, 0.5, 0.5, 0.5]) - -plt.title("High-resolution tricontouring") - -plt.show() diff --git a/examples/pylab_examples/tricontour_vs_griddata.py b/examples/pylab_examples/tricontour_vs_griddata.py deleted file mode 100644 index 7307d37b76c5..000000000000 --- a/examples/pylab_examples/tricontour_vs_griddata.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Comparison of griddata and tricontour for an unstructured triangular grid. -""" -from __future__ import print_function -import matplotlib.pyplot as plt -import matplotlib.tri as tri -import numpy as np -import numpy.random as rnd -import matplotlib.mlab as mlab -import time - -rnd.seed(0) -npts = 200 -ngridx = 100 -ngridy = 200 -x = rnd.uniform(-2, 2, npts) -y = rnd.uniform(-2, 2, npts) -z = x*np.exp(-x**2 - y**2) - -# griddata and contour. -start = time.clock() -plt.subplot(211) -xi = np.linspace(-2.1, 2.1, ngridx) -yi = np.linspace(-2.1, 2.1, ngridy) -zi = mlab.griddata(x, y, z, xi, yi, interp='linear') -plt.contour(xi, yi, zi, 15, linewidths=0.5, colors='k') -plt.contourf(xi, yi, zi, 15, - norm=plt.Normalize(vmax=abs(zi).max(), vmin=-abs(zi).max())) -plt.colorbar() # draw colorbar -plt.plot(x, y, 'ko', ms=3) -plt.xlim(-2, 2) -plt.ylim(-2, 2) -plt.title('griddata and contour (%d points, %d grid points)' % - (npts, ngridx*ngridy)) -print('griddata and contour seconds: %f' % (time.clock() - start)) - -# tricontour. -start = time.clock() -plt.subplot(212) -triang = tri.Triangulation(x, y) -plt.tricontour(x, y, z, 15, linewidths=0.5, colors='k') -plt.tricontourf(x, y, z, 15, - norm=plt.Normalize(vmax=abs(zi).max(), vmin=-abs(zi).max())) -plt.colorbar() -plt.plot(x, y, 'ko', ms=3) -plt.xlim(-2, 2) -plt.ylim(-2, 2) -plt.title('tricontour (%d points)' % npts) -print('tricontour seconds: %f' % (time.clock() - start)) - -plt.subplots_adjust(hspace=0.5) - -plt.show() diff --git a/examples/pylab_examples/trigradient_demo.py b/examples/pylab_examples/trigradient_demo.py deleted file mode 100644 index 6114b8493de5..000000000000 --- a/examples/pylab_examples/trigradient_demo.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Demonstrates computation of gradient with matplotlib.tri.CubicTriInterpolator. -""" -from matplotlib.tri import Triangulation, UniformTriRefiner,\ - CubicTriInterpolator -import matplotlib.pyplot as plt -import matplotlib.cm as cm -import numpy as np -import math - - -#----------------------------------------------------------------------------- -# Electrical potential of a dipole -#----------------------------------------------------------------------------- -def dipole_potential(x, y): - """ The electric dipole potential V """ - r_sq = x**2 + y**2 - theta = np.arctan2(y, x) - z = np.cos(theta)/r_sq - return (np.max(z) - z) / (np.max(z) - np.min(z)) - - -#----------------------------------------------------------------------------- -# Creating a Triangulation -#----------------------------------------------------------------------------- -# First create the x and y coordinates of the points. -n_angles = 30 -n_radii = 10 -min_radius = 0.2 -radii = np.linspace(min_radius, 0.95, n_radii) - -angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += math.pi/n_angles - -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -V = dipole_potential(x, y) - -# Create the Triangulation; no triangles specified so Delaunay triangulation -# created. -triang = Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triang.set_mask(mask) - -#----------------------------------------------------------------------------- -# Refine data - interpolates the electrical potential V -#----------------------------------------------------------------------------- -refiner = UniformTriRefiner(triang) -tri_refi, z_test_refi = refiner.refine_field(V, subdiv=3) - -#----------------------------------------------------------------------------- -# Computes the electrical field (Ex, Ey) as gradient of electrical potential -#----------------------------------------------------------------------------- -tci = CubicTriInterpolator(triang, -V) -# Gradient requested here at the mesh nodes but could be anywhere else: -(Ex, Ey) = tci.gradient(triang.x, triang.y) -E_norm = np.sqrt(Ex**2 + Ey**2) - -#----------------------------------------------------------------------------- -# Plot the triangulation, the potential iso-contours and the vector field -#----------------------------------------------------------------------------- -plt.figure() -plt.gca().set_aspect('equal') -plt.triplot(triang, color='0.8') - -levels = np.arange(0., 1., 0.01) -cmap = cm.get_cmap(name='hot', lut=None) -plt.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, - linewidths=[2.0, 1.0, 1.0, 1.0]) -# Plots direction of the electrical vector field -plt.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm, - units='xy', scale=10., zorder=3, color='blue', - width=0.007, headwidth=3., headlength=4.) - -plt.title('Gradient plot: an electrical dipole') -plt.show() diff --git a/examples/pylab_examples/triinterp_demo.py b/examples/pylab_examples/triinterp_demo.py deleted file mode 100644 index f955727064dd..000000000000 --- a/examples/pylab_examples/triinterp_demo.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Interpolation from triangular grid to quad grid. -""" -import matplotlib.pyplot as plt -import matplotlib.tri as mtri -import numpy as np - -# Create triangulation. -x = np.asarray([0, 1, 2, 3, 0.5, 1.5, 2.5, 1, 2, 1.5]) -y = np.asarray([0, 0, 0, 0, 1.0, 1.0, 1.0, 2, 2, 3.0]) -triangles = [[0, 1, 4], [1, 2, 5], [2, 3, 6], [1, 5, 4], [2, 6, 5], [4, 5, 7], - [5, 6, 8], [5, 8, 7], [7, 8, 9]] -triang = mtri.Triangulation(x, y, triangles) - -# Interpolate to regularly-spaced quad grid. -z = np.cos(1.5*x)*np.cos(1.5*y) -xi, yi = np.meshgrid(np.linspace(0, 3, 20), np.linspace(0, 3, 20)) - -interp_lin = mtri.LinearTriInterpolator(triang, z) -zi_lin = interp_lin(xi, yi) - -interp_cubic_geom = mtri.CubicTriInterpolator(triang, z, kind='geom') -zi_cubic_geom = interp_cubic_geom(xi, yi) - -interp_cubic_min_E = mtri.CubicTriInterpolator(triang, z, kind='min_E') -zi_cubic_min_E = interp_cubic_min_E(xi, yi) - - -# Plot the triangulation. -plt.subplot(221) -plt.tricontourf(triang, z) -plt.triplot(triang, 'ko-') -plt.title('Triangular grid') - -# Plot linear interpolation to quad grid. -plt.subplot(222) -plt.contourf(xi, yi, zi_lin) -plt.plot(xi, yi, 'k-', lw=0.5, alpha=0.5) -plt.plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5) -plt.title("Linear interpolation") - -# Plot cubic interpolation to quad grid, kind=geom -plt.subplot(223) -plt.contourf(xi, yi, zi_cubic_geom) -plt.plot(xi, yi, 'k-', lw=0.5, alpha=0.5) -plt.plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5) -plt.title("Cubic interpolation,\nkind='geom'") - -# Plot cubic interpolation to quad grid, kind=min_E -plt.subplot(224) -plt.contourf(xi, yi, zi_cubic_min_E) -plt.plot(xi, yi, 'k-', lw=0.5, alpha=0.5) -plt.plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5) -plt.title("Cubic interpolation,\nkind='min_E'") - -plt.tight_layout() -plt.show() diff --git a/examples/pylab_examples/tripcolor_demo.py b/examples/pylab_examples/tripcolor_demo.py deleted file mode 100644 index bb7bb4ed0222..000000000000 --- a/examples/pylab_examples/tripcolor_demo.py +++ /dev/null @@ -1,116 +0,0 @@ -""" -Pseudocolor plots of unstructured triangular grids. -""" -import matplotlib.pyplot as plt -import matplotlib.tri as tri -import numpy as np -import math - -# Creating a Triangulation without specifying the triangles results in the -# Delaunay triangulation of the points. - -# First create the x and y coordinates of the points. -n_angles = 36 -n_radii = 8 -min_radius = 0.25 -radii = np.linspace(min_radius, 0.95, n_radii) - -angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False) -angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1) -angles[:, 1::2] += math.pi/n_angles - -x = (radii*np.cos(angles)).flatten() -y = (radii*np.sin(angles)).flatten() -z = (np.cos(radii)*np.cos(angles*3.0)).flatten() - -# Create the Triangulation; no triangles so Delaunay triangulation created. -triang = tri.Triangulation(x, y) - -# Mask off unwanted triangles. -xmid = x[triang.triangles].mean(axis=1) -ymid = y[triang.triangles].mean(axis=1) -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0) -triang.set_mask(mask) - -# tripcolor plot. -plt.figure() -plt.gca().set_aspect('equal') -plt.tripcolor(triang, z, shading='flat') -plt.colorbar() -plt.title('tripcolor of Delaunay triangulation, flat shading') - -# Illustrate Gouraud shading. -plt.figure() -plt.gca().set_aspect('equal') -plt.tripcolor(triang, z, shading='gouraud') -plt.colorbar() -plt.title('tripcolor of Delaunay triangulation, gouraud shading') - - -# You can specify your own triangulation rather than perform a Delaunay -# triangulation of the points, where each triangle is given by the indices of -# the three points that make up the triangle, ordered in either a clockwise or -# anticlockwise manner. - -xy = np.asarray([ - [-0.101, 0.872], [-0.080, 0.883], [-0.069, 0.888], [-0.054, 0.890], - [-0.045, 0.897], [-0.057, 0.895], [-0.073, 0.900], [-0.087, 0.898], - [-0.090, 0.904], [-0.069, 0.907], [-0.069, 0.921], [-0.080, 0.919], - [-0.073, 0.928], [-0.052, 0.930], [-0.048, 0.942], [-0.062, 0.949], - [-0.054, 0.958], [-0.069, 0.954], [-0.087, 0.952], [-0.087, 0.959], - [-0.080, 0.966], [-0.085, 0.973], [-0.087, 0.965], [-0.097, 0.965], - [-0.097, 0.975], [-0.092, 0.984], [-0.101, 0.980], [-0.108, 0.980], - [-0.104, 0.987], [-0.102, 0.993], [-0.115, 1.001], [-0.099, 0.996], - [-0.101, 1.007], [-0.090, 1.010], [-0.087, 1.021], [-0.069, 1.021], - [-0.052, 1.022], [-0.052, 1.017], [-0.069, 1.010], [-0.064, 1.005], - [-0.048, 1.005], [-0.031, 1.005], [-0.031, 0.996], [-0.040, 0.987], - [-0.045, 0.980], [-0.052, 0.975], [-0.040, 0.973], [-0.026, 0.968], - [-0.020, 0.954], [-0.006, 0.947], [ 0.003, 0.935], [ 0.006, 0.926], - [ 0.005, 0.921], [ 0.022, 0.923], [ 0.033, 0.912], [ 0.029, 0.905], - [ 0.017, 0.900], [ 0.012, 0.895], [ 0.027, 0.893], [ 0.019, 0.886], - [ 0.001, 0.883], [-0.012, 0.884], [-0.029, 0.883], [-0.038, 0.879], - [-0.057, 0.881], [-0.062, 0.876], [-0.078, 0.876], [-0.087, 0.872], - [-0.030, 0.907], [-0.007, 0.905], [-0.057, 0.916], [-0.025, 0.933], - [-0.077, 0.990], [-0.059, 0.993]]) -x = xy[:, 0]*180/3.14159 -y = xy[:, 1]*180/3.14159 - -triangles = np.asarray([ - [67, 66, 1], [65, 2, 66], [ 1, 66, 2], [64, 2, 65], [63, 3, 64], - [60, 59, 57], [ 2, 64, 3], [ 3, 63, 4], [ 0, 67, 1], [62, 4, 63], - [57, 59, 56], [59, 58, 56], [61, 60, 69], [57, 69, 60], [ 4, 62, 68], - [ 6, 5, 9], [61, 68, 62], [69, 68, 61], [ 9, 5, 70], [ 6, 8, 7], - [ 4, 70, 5], [ 8, 6, 9], [56, 69, 57], [69, 56, 52], [70, 10, 9], - [54, 53, 55], [56, 55, 53], [68, 70, 4], [52, 56, 53], [11, 10, 12], - [69, 71, 68], [68, 13, 70], [10, 70, 13], [51, 50, 52], [13, 68, 71], - [52, 71, 69], [12, 10, 13], [71, 52, 50], [71, 14, 13], [50, 49, 71], - [49, 48, 71], [14, 16, 15], [14, 71, 48], [17, 19, 18], [17, 20, 19], - [48, 16, 14], [48, 47, 16], [47, 46, 16], [16, 46, 45], [23, 22, 24], - [21, 24, 22], [17, 16, 45], [20, 17, 45], [21, 25, 24], [27, 26, 28], - [20, 72, 21], [25, 21, 72], [45, 72, 20], [25, 28, 26], [44, 73, 45], - [72, 45, 73], [28, 25, 29], [29, 25, 31], [43, 73, 44], [73, 43, 40], - [72, 73, 39], [72, 31, 25], [42, 40, 43], [31, 30, 29], [39, 73, 40], - [42, 41, 40], [72, 33, 31], [32, 31, 33], [39, 38, 72], [33, 72, 38], - [33, 38, 34], [37, 35, 38], [34, 38, 35], [35, 37, 36]]) - -xmid = x[triangles].mean(axis=1) -ymid = y[triangles].mean(axis=1) -x0 = -5 -y0 = 52 -zfaces = np.exp(-0.01*((xmid - x0)*(xmid - x0) + (ymid - y0)*(ymid - y0))) - -# Rather than create a Triangulation object, can simply pass x, y and triangles -# arrays to tripcolor directly. It would be better to use a Triangulation -# object if the same triangulation was to be used more than once to save -# duplicated calculations. -# Can specify one color value per face rather than one per point by using the -# facecolors kwarg. -plt.figure() -plt.gca().set_aspect('equal') -plt.tripcolor(x, y, triangles, facecolors=zfaces, edgecolors='k') -plt.colorbar() -plt.title('tripcolor of user-specified triangulation') -plt.xlabel('Longitude (degrees)') -plt.ylabel('Latitude (degrees)') - -plt.show() diff --git a/examples/pylab_examples/usetex_baseline_test.py b/examples/pylab_examples/usetex_baseline_test.py deleted file mode 100644 index 09e631b0df7a..000000000000 --- a/examples/pylab_examples/usetex_baseline_test.py +++ /dev/null @@ -1,77 +0,0 @@ - -import matplotlib -import matplotlib.pyplot as plt -import matplotlib.axes as maxes - -from matplotlib import rcParams -rcParams['text.usetex'] = True -rcParams['text.latex.unicode'] = True - - -class Axes(maxes.Axes): - """ - A hackish way to simultaneously draw texts w/ usetex=True and - usetex=False in the same figure. It does not work in the ps backend. - """ - - def __init__(self, *kl, **kw): - self.usetex = kw.pop("usetex", "False") - self.preview = kw.pop("preview", "False") - - maxes.Axes.__init__(self, *kl, **kw) - - def draw(self, renderer): - usetex = plt.rcParams["text.usetex"] - preview = plt.rcParams["text.latex.preview"] - plt.rcParams["text.usetex"] = self.usetex - plt.rcParams["text.latex.preview"] = self.preview - - maxes.Axes.draw(self, renderer) - - plt.rcParams["text.usetex"] = usetex - plt.rcParams["text.latex.preview"] = preview - -subplot = maxes.subplot_class_factory(Axes) - - -def test_window_extent(ax, usetex, preview): - - va = "baseline" - ax.xaxis.set_visible(False) - ax.yaxis.set_visible(False) - - #t = ax.text(0., 0., r"mlp", va="baseline", size=150) - text_kw = dict(va=va, - size=50, - bbox=dict(pad=0., ec="k", fc="none")) - - test_strings = ["lg", r"$\frac{1}{2}\pi$", - r"$p^{3^A}$", r"$p_{3_2}$"] - - ax.axvline(0, color="r") - - for i, s in enumerate(test_strings): - - ax.axhline(i, color="r") - ax.text(0., 3 - i, s, **text_kw) - - ax.set_xlim(-0.1, 1.1) - ax.set_ylim(-.8, 3.9) - - ax.set_title("usetex=%s\npreview=%s" % (str(usetex), str(preview))) - - -fig = plt.figure(figsize=(2.*3, 6.5)) - -for i, usetex, preview in [[0, False, False], - [1, True, False], - [2, True, True]]: - ax = subplot(fig, 1, 3, i + 1, usetex=usetex, preview=preview) - fig.add_subplot(ax) - fig.subplots_adjust(top=0.85) - - test_window_extent(ax, usetex=usetex, preview=preview) - - -plt.draw() -plt.show() diff --git a/examples/pylab_examples/usetex_demo.py b/examples/pylab_examples/usetex_demo.py deleted file mode 100644 index 05797b834597..000000000000 --- a/examples/pylab_examples/usetex_demo.py +++ /dev/null @@ -1,70 +0,0 @@ -import matplotlib -matplotlib.rc('text', usetex=True) -import matplotlib.pyplot as plt -import numpy as np - -# interface tracking profiles -N = 500 -delta = 0.6 -X = np.linspace(-1, 1, N) -plt.plot(X, (1 - np.tanh(4.*X/delta))/2, # phase field tanh profiles - X, (X + 1)/2, # level set distance function - X, (1.4 + np.tanh(4.*X/delta))/4, # composition profile - X, X < 0, 'k--', # sharp interface - ) - -# legend -plt.legend(('phase field', 'level set', 'composition', 'sharp interface'), shadow=True, loc=(0.01, 0.55)) - -ltext = plt.gca().get_legend().get_texts() -plt.setp(ltext[0], fontsize=20) -plt.setp(ltext[1], fontsize=20) -plt.setp(ltext[2], fontsize=20) -plt.setp(ltext[3], fontsize=20) - -# the arrow -height = 0.1 -offset = 0.02 -plt.plot((-delta / 2., delta / 2), (height, height), 'k', linewidth=2) -plt.plot((-delta / 2, -delta / 2 + offset * 2), (height, height - offset), 'k', linewidth=2) -plt.plot((-delta / 2, -delta / 2 + offset * 2), (height, height + offset), 'k', linewidth=2) -plt.plot((delta / 2, delta / 2 - offset * 2), (height, height - offset), 'k', linewidth=2) -plt.plot((delta / 2, delta / 2 - offset * 2), (height, height + offset), 'k', linewidth=2) -plt.text(-0.06, height - 0.06, r'$\delta$', {'color': 'k', 'fontsize': 24}) - -# X-axis label -plt.xticks((-1, 0, 1), ('-1', '0', '1'), color='k', size=20) - -# Left Y-axis labels -plt.ylabel(r'\bf{phase field} $\phi$', {'color': 'b', - 'fontsize': 20}) -plt.yticks((0, 0.5, 1), ('0', '.5', '1'), color='k', size=20) - -# Right Y-axis labels -plt.text(1.05, 0.5, r"\bf{level set} $\phi$", {'color': 'g', 'fontsize': 20}, - horizontalalignment='left', - verticalalignment='center', - rotation=90, - clip_on=False) -plt.text(1.01, -0.02, "-1", {'color': 'k', 'fontsize': 20}) -plt.text(1.01, 0.98, "1", {'color': 'k', 'fontsize': 20}) -plt.text(1.01, 0.48, "0", {'color': 'k', 'fontsize': 20}) - -# level set equations -plt.text(0.1, 0.85, - r'$|\nabla\phi| = 1,$ \newline $ \frac{\partial \phi}{\partial t}' - r'+ U|\nabla \phi| = 0$', - {'color': 'g', 'fontsize': 20}) - -# phase field equations -plt.text(0.2, 0.15, - r'$\mathcal{F} = \int f\left( \phi, c \right) dV,$ \newline ' - r'$ \frac{ \partial \phi } { \partial t } = -M_{ \phi } ' - r'\frac{ \delta \mathcal{F} } { \delta \phi }$', - {'color': 'b', 'fontsize': 20}) - -# these went wrong in pdf in a previous version -plt.text(-.9, .42, r'gamma: $\gamma$', {'color': 'r', 'fontsize': 20}) -plt.text(-.9, .36, r'Omega: $\Omega$', {'color': 'b', 'fontsize': 20}) - -plt.show() diff --git a/examples/pylab_examples/usetex_fonteffects.py b/examples/pylab_examples/usetex_fonteffects.py deleted file mode 100644 index 55bd4adec0cd..000000000000 --- a/examples/pylab_examples/usetex_fonteffects.py +++ /dev/null @@ -1,23 +0,0 @@ -# This script demonstrates that font effects specified in your pdftex.map -# are now supported in pdf usetex. - -import matplotlib -import matplotlib.pyplot as plt -matplotlib.rc('text', usetex=True) - - -def setfont(font): - return r'\font\a %s at 14pt\a ' % font - -for y, font, text in zip(range(5), - ['ptmr8r', 'ptmri8r', 'ptmro8r', 'ptmr8rn', 'ptmrr8re'], - ['Nimbus Roman No9 L ' + x for x in - ['', 'Italics (real italics for comparison)', - '(slanted)', '(condensed)', '(extended)']]): - plt.text(0, y, setfont(font) + text) - -plt.ylim(-1, 5) -plt.xlim(-0.2, 0.6) -plt.setp(plt.gca(), frame_on=False, xticks=(), yticks=()) -plt.title('Usetex font effects') -plt.savefig('usetex_fonteffects.pdf') diff --git a/examples/pylab_examples/vline_hline_demo.py b/examples/pylab_examples/vline_hline_demo.py deleted file mode 100644 index d4fe9e0bf306..000000000000 --- a/examples/pylab_examples/vline_hline_demo.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Small demonstration of the hlines and vlines plots. -""" - -import matplotlib.pyplot as plt -import numpy as np -import numpy.random as rnd - - -def f(t): - s1 = np.sin(2 * np.pi * t) - e1 = np.exp(-t) - return np.absolute((s1 * e1)) + .05 - -t = np.arange(0.0, 5.0, 0.1) -s = f(t) -nse = rnd.normal(0.0, 0.3, t.shape) * s - -fig = plt.figure(figsize=(12, 6)) -vax = fig.add_subplot(121) -hax = fig.add_subplot(122) - -vax.plot(t, s + nse, '^') -vax.vlines(t, [0], s) -vax.set_xlabel('time (s)') -vax.set_title('Vertical lines demo') - -hax.plot(s + nse, t, '^') -hax.hlines(t, [0], s, lw=2) -hax.set_xlabel('time (s)') -hax.set_title('Horizontal lines demo') - -plt.show() diff --git a/examples/pylab_examples/webapp_demo.py b/examples/pylab_examples/webapp_demo.py deleted file mode 100644 index 654e1e8ebfa7..000000000000 --- a/examples/pylab_examples/webapp_demo.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- noplot -*- -""" -This example shows how to use the agg backend directly to create -images, which may be of use to web application developers who want -full control over their code without using the pyplot interface to -manage figures, figure closing etc. - -.. note:: - - It is not necessary to avoid using the pyplot interface in order to - create figures without a graphical front-end - simply setting - the backend to "Agg" would be sufficient. - - -It is also worth noting that, because matplotlib can save figures to file-like -object, matplotlib can also be used inside a cgi-script *without* needing to -write a figure to disk. - -""" - -from matplotlib.backends.backend_agg import FigureCanvasAgg -from matplotlib.figure import Figure -import numpy as np - - -def make_fig(): - """ - Make a figure and save it to "webagg.png". - - """ - fig = Figure() - ax = fig.add_subplot(1, 1, 1) - - ax.plot([1, 2, 3], 'ro--', markersize=12, markerfacecolor='g') - - # make a translucent scatter collection - x = np.random.rand(100) - y = np.random.rand(100) - area = np.pi * (10 * np.random.rand(100)) ** 2 # 0 to 10 point radiuses - c = ax.scatter(x, y, area) - c.set_alpha(0.5) - - # add some text decoration - ax.set_title('My first image') - ax.set_ylabel('Some numbers') - ax.set_xticks((.2, .4, .6, .8)) - labels = ax.set_xticklabels(('Bill', 'Fred', 'Ted', 'Ed')) - - # To set object properties, you can either iterate over the - # objects manually, or define you own set command, as in setapi - # above. - for label in labels: - label.set_rotation(45) - label.set_fontsize(12) - - FigureCanvasAgg(fig).print_png('webapp.png', dpi=150) - -make_fig() diff --git a/examples/pylab_examples/xcorr_demo.py b/examples/pylab_examples/xcorr_demo.py deleted file mode 100644 index 8fae6aa555f9..000000000000 --- a/examples/pylab_examples/xcorr_demo.py +++ /dev/null @@ -1,19 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - - -np.random.seed(0) - -x, y = np.random.randn(2, 100) -fig = plt.figure() -ax1 = fig.add_subplot(211) -ax1.xcorr(x, y, usevlines=True, maxlags=50, normed=True, lw=2) -ax1.grid(True) -ax1.axhline(0, color='black', lw=2) - -ax2 = fig.add_subplot(212, sharex=ax1) -ax2.acorr(x, usevlines=True, normed=True, maxlags=50, lw=2) -ax2.grid(True) -ax2.axhline(0, color='black', lw=2) - -plt.show() diff --git a/examples/pylab_examples/zorder_demo.py b/examples/pylab_examples/zorder_demo.py deleted file mode 100755 index f9c0ff4710c4..000000000000 --- a/examples/pylab_examples/zorder_demo.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -The default drawing order for axes is patches, lines, text. This -order is determined by the zorder attribute. The following defaults -are set - -Artist Z-order -Patch / PatchCollection 1 -Line2D / LineCollection 2 -Text 3 - -You can change the order for individual artists by setting the zorder. Any -individual plot() call can set a value for the zorder of that particular item. - -In the fist subplot below, the lines are drawn above the patch -collection from the scatter, which is the default. - -In the subplot below, the order is reversed. - -The second figure shows how to control the zorder of individual lines. -""" - -import matplotlib.pyplot as plt -import numpy as np - -x = np.random.random(20) -y = np.random.random(20) - -# Lines on top of scatter -plt.figure() -plt.subplot(211) -plt.plot(x, y, 'r', lw=3) -plt.scatter(x, y, s=120) -plt.title('Lines on top of dots') - -# Scatter plot on top of lines -plt.subplot(212) -plt.plot(x, y, 'r', zorder=1, lw=3) -plt.scatter(x, y, s=120, zorder=2) -plt.title('Dots on top of lines') - -# A new figure, with individually ordered items -x = np.linspace(0, 2*np.pi, 100) -plt.figure() -plt.plot(x, np.sin(x), linewidth=10, color='black', label='zorder=10', zorder=10) # on top -plt.plot(x, np.cos(1.3*x), linewidth=10, color='red', label='zorder=1', zorder=1) # bottom -plt.plot(x, np.sin(2.1*x), linewidth=10, color='green', label='zorder=3', zorder=3) -plt.axhline(0, linewidth=10, color='blue', label='zorder=2', zorder=2) -plt.title('Custom order of elements') -l = plt.legend() -l.set_zorder(20) # put the legend on top -plt.show() diff --git a/examples/scales/scales.py b/examples/scales/scales.py deleted file mode 100644 index e00f04236f85..000000000000 --- a/examples/scales/scales.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Illustrate the scale transformations applied to axes, e.g. log, symlog, logit. -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.ticker import NullFormatter - -np.random.seed(1) -# make up some data in the interval ]0, 1[ -y = np.random.normal(loc=0.5, scale=0.4, size=1000) -y = y[(y > 0) & (y < 1)] -y.sort() -x = np.arange(len(y)) - -# plot with various axes scales -fig, axs = plt.subplots(2, 2, sharex=True) -fig.subplots_adjust(left=0.08, right=0.98, wspace=0.3) - -# linear -ax = axs[0, 0] -ax.plot(x, y) -ax.set_yscale('linear') -ax.set_title('linear') -ax.grid(True) - - -# log -ax = axs[0, 1] -ax.plot(x, y) -ax.set_yscale('log') -ax.set_title('log') -ax.grid(True) - - -# symmetric log -ax = axs[1, 1] -ax.plot(x, y - y.mean()) -ax.set_yscale('symlog', linthreshy=0.02) -ax.set_title('symlog') -ax.grid(True) - -# logit -ax = axs[1, 0] -ax.plot(x, y) -ax.set_yscale('logit') -ax.set_title('logit') -ax.grid(True) -ax.yaxis.set_minor_formatter(NullFormatter()) - - -plt.show() diff --git a/examples/shapes_and_collections/artist_reference.py b/examples/shapes_and_collections/artist_reference.py deleted file mode 100644 index ad867771aaa4..000000000000 --- a/examples/shapes_and_collections/artist_reference.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Reference for matplotlib artists - -This example displays several of matplotlib's graphics primitives (artists) -drawn using matplotlib API. A full list of artists and the documentation is -available at http://matplotlib.org/api/artist_api.html. - -Copyright (c) 2010, Bartosz Telenczuk -BSD License -""" -import matplotlib.pyplot as plt -import numpy as np -import matplotlib.path as mpath -import matplotlib.lines as mlines -import matplotlib.patches as mpatches -from matplotlib.collections import PatchCollection - - -plt.rcdefaults() - - -def label(xy, text): - y = xy[1] - 0.15 # shift y-value for label so that it's below the artist - plt.text(xy[0], y, text, ha="center", family='sans-serif', size=14) - - -fig, ax = plt.subplots() -# create 3x3 grid to plot the artists -grid = np.mgrid[0.2:0.8:3j, 0.2:0.8:3j].reshape(2, -1).T - -patches = [] - -# add a circle -circle = mpatches.Circle(grid[0], 0.1, ec="none") -patches.append(circle) -label(grid[0], "Circle") - -# add a rectangle -rect = mpatches.Rectangle(grid[1] - [0.025, 0.05], 0.05, 0.1, ec="none") -patches.append(rect) -label(grid[1], "Rectangle") - -# add a wedge -wedge = mpatches.Wedge(grid[2], 0.1, 30, 270, ec="none") -patches.append(wedge) -label(grid[2], "Wedge") - -# add a Polygon -polygon = mpatches.RegularPolygon(grid[3], 5, 0.1) -patches.append(polygon) -label(grid[3], "Polygon") - -# add an ellipse -ellipse = mpatches.Ellipse(grid[4], 0.2, 0.1) -patches.append(ellipse) -label(grid[4], "Ellipse") - -# add an arrow -arrow = mpatches.Arrow(grid[5, 0] - 0.05, grid[5, 1] - 0.05, 0.1, 0.1, width=0.1) -patches.append(arrow) -label(grid[5], "Arrow") - -# add a path patch -Path = mpath.Path -path_data = [ - (Path.MOVETO, [0.018, -0.11]), - (Path.CURVE4, [-0.031, -0.051]), - (Path.CURVE4, [-0.115, 0.073]), - (Path.CURVE4, [-0.03 , 0.073]), - (Path.LINETO, [-0.011, 0.039]), - (Path.CURVE4, [0.043, 0.121]), - (Path.CURVE4, [0.075, -0.005]), - (Path.CURVE4, [0.035, -0.027]), - (Path.CLOSEPOLY, [0.018, -0.11]) - ] -codes, verts = zip(*path_data) -path = mpath.Path(verts + grid[6], codes) -patch = mpatches.PathPatch(path) -patches.append(patch) -label(grid[6], "PathPatch") - -# add a fancy box -fancybox = mpatches.FancyBboxPatch( - grid[7] - [0.025, 0.05], 0.05, 0.1, - boxstyle=mpatches.BoxStyle("Round", pad=0.02)) -patches.append(fancybox) -label(grid[7], "FancyBboxPatch") - -# add a line -x, y = np.array([[-0.06, 0.0, 0.1], [0.05, -0.05, 0.05]]) -line = mlines.Line2D(x + grid[8, 0], y + grid[8, 1], lw=5., alpha=0.3) -label(grid[8], "Line2D") - -colors = np.linspace(0, 1, len(patches)) -collection = PatchCollection(patches, cmap=plt.cm.hsv, alpha=0.3) -collection.set_array(np.array(colors)) -ax.add_collection(collection) -ax.add_line(line) - -plt.subplots_adjust(left=0, right=1, bottom=0, top=1) -plt.axis('equal') -plt.axis('off') - -plt.show() diff --git a/examples/shapes_and_collections/path_patch_demo.py b/examples/shapes_and_collections/path_patch_demo.py deleted file mode 100644 index fb0c8aa47592..000000000000 --- a/examples/shapes_and_collections/path_patch_demo.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Demo of a PathPatch object. -""" -import matplotlib.path as mpath -import matplotlib.patches as mpatches -import matplotlib.pyplot as plt - - -fig, ax = plt.subplots() - -Path = mpath.Path -path_data = [ - (Path.MOVETO, (1.58, -2.57)), - (Path.CURVE4, (0.35, -1.1)), - (Path.CURVE4, (-1.75, 2.0)), - (Path.CURVE4, (0.375, 2.0)), - (Path.LINETO, (0.85, 1.15)), - (Path.CURVE4, (2.2, 3.2)), - (Path.CURVE4, (3, 0.05)), - (Path.CURVE4, (2.0, -0.5)), - (Path.CLOSEPOLY, (1.58, -2.57)), - ] -codes, verts = zip(*path_data) -path = mpath.Path(verts, codes) -patch = mpatches.PathPatch(path, facecolor='r', alpha=0.5) -ax.add_patch(patch) - -# plot control points and connecting lines -x, y = zip(*path.vertices) -line, = ax.plot(x, y, 'go-') - -ax.grid() -ax.axis('equal') -plt.show() diff --git a/examples/shapes_and_collections/scatter_demo.py b/examples/shapes_and_collections/scatter_demo.py deleted file mode 100644 index 8e66dbfb1560..000000000000 --- a/examples/shapes_and_collections/scatter_demo.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Simple demo of a scatter plot. -""" -import numpy as np -import matplotlib.pyplot as plt - - -N = 50 -x = np.random.rand(N) -y = np.random.rand(N) -colors = np.random.rand(N) -area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radiuses - -plt.scatter(x, y, s=area, c=colors, alpha=0.5) -plt.show() diff --git a/examples/showcase/anatomy.py b/examples/showcase/anatomy.py deleted file mode 100644 index 01d2b56eb147..000000000000 --- a/examples/showcase/anatomy.py +++ /dev/null @@ -1,138 +0,0 @@ -# This figure shows the name of several matplotlib elements composing a figure - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.ticker import MultipleLocator, FuncFormatter - -np.random.seed(123) - -X = np.linspace(0.5, 3.5, 100) -Y1 = 3+np.cos(X) -Y2 = 1+np.cos(1+X/0.75)/2 -Y3 = np.random.uniform(Y1, Y2, len(X)) - -fig = plt.figure(figsize=(8, 8), facecolor="w") -ax = fig.add_subplot(1, 1, 1, aspect=1) - - -def minor_tick(x, pos): - if not x % 1.0: - return "" - return "%.2f" % x - -ax.xaxis.set_major_locator(MultipleLocator(1.000)) -ax.xaxis.set_minor_locator(MultipleLocator(0.250)) -ax.yaxis.set_major_locator(MultipleLocator(1.000)) -ax.yaxis.set_minor_locator(MultipleLocator(0.250)) -ax.xaxis.set_minor_formatter(FuncFormatter(minor_tick)) - -ax.set_xlim(0, 4) -ax.set_ylim(0, 4) - -ax.tick_params(which='major', width=1.0) -ax.tick_params(which='major', length=10) -ax.tick_params(which='minor', width=1.0, labelsize=10) -ax.tick_params(which='minor', length=5, labelsize=10, labelcolor='0.25') - -ax.grid(linestyle="--", linewidth=0.5, color='.25', zorder=-10) - -ax.plot(X, Y1, c=(0.25, 0.25, 1.00), lw=2, label="Blue signal", zorder=10) -ax.plot(X, Y2, c=(1.00, 0.25, 0.25), lw=2, label="Red signal") -ax.scatter(X, Y3, c='w') - -ax.set_title("Anatomy of a figure", fontsize=20) -ax.set_xlabel("X axis label") -ax.set_ylabel("Y axis label") - -ax.legend(frameon=False) - - -def circle(x, y, radius=0.15): - from matplotlib.patches import Circle - from matplotlib.patheffects import withStroke - circle = Circle((x, y), radius, clip_on=False, zorder=10, linewidth=1, - edgecolor='black', facecolor=(0, 0, 0, .0125), - path_effects=[withStroke(linewidth=5, foreground='w')]) - ax.add_artist(circle) - - -def text(x, y, text): - ax.text(x, y, text, backgroundcolor="white", - ha='center', va='top', weight='bold', color='blue') - - -# Minor tick -circle(0.50, -.05) -text(0.50, -0.25, "Minor tick label") - -# Major tick -circle(4.00, 2.00) -text(4.00, 1.80, "Major tick") - -# Minor tick -circle(0.25, 4.00) -text(0.25, 3.80, "Minor tick") - -# Major tick label -circle(-0.05, 3.00) -text(-0.05, 2.80, "Major tick label") - -# X Label -circle(1.80, -0.22) -text(1.80, -0.4, "X axis label") - -# Y Label -circle(-0.20, 1.80) -text(-0.20, 1.6, "Y axis label") - -# Title -circle(1.60, 4.10) -text(1.60, 3.9, "Title") - -# Blue plot -circle(1.75, 2.80) -text(1.75, 2.60, "Line\n(line plot)") - -# Red plot -circle(1.20, 0.60) -text(1.20, 0.40, "Line\n(line plot)") - -# Scatter plot -circle(3.20, 1.75) -text(3.20, 1.55, "Markers\n(scatter plot)") - -# Grid -circle(3.00, 3.00) -text(3.00, 2.80, "Grid") - -# Legend -circle(3.70, 3.75) -text(3.70, 3.55, "Legend") - -# Axes -circle(0.5, 0.5) -text(0.5, 0.3, "Axes") - -# Figure -circle(-0.3, 0.65) -text(-0.3, 0.45, "Figure") - -color = 'blue' -ax.annotate('Spines', xy=(4.0, 0.35), xycoords='data', - xytext=(3.3, 0.5), textcoords='data', - weight='bold', color=color, - arrowprops=dict(arrowstyle='->', - connectionstyle="arc3", - color=color)) - -ax.annotate('', xy=(3.15, 0.0), xycoords='data', - xytext=(3.45, 0.45), textcoords='data', - weight='bold', color=color, - arrowprops=dict(arrowstyle='->', - connectionstyle="arc3", - color=color)) - -ax.text(4.0, -0.4, "Made with http://matplotlib.org", - fontsize=10, ha="right", color='.5') - -plt.show() diff --git a/examples/showcase/bachelors_degrees_by_gender.py b/examples/showcase/bachelors_degrees_by_gender.py deleted file mode 100644 index d891fbe52153..000000000000 --- a/examples/showcase/bachelors_degrees_by_gender.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -A graph of multiple time series which demonstrates extensive custom -styling of plot frame, tick lines and labels, and line graph properties. - -Also demonstrates the custom placement of text labels along the right edge -as an alternative to a conventional legend. -""" - -import matplotlib.pyplot as plt -from matplotlib.mlab import csv2rec -from matplotlib.cbook import get_sample_data - -fname = get_sample_data('percent_bachelors_degrees_women_usa.csv') -gender_degree_data = csv2rec(fname) - -# These are the colors that will be used in the plot -color_sequence = ['#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', - '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5', - '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', - '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'] - -# You typically want your plot to be ~1.33x wider than tall. This plot -# is a rare exception because of the number of lines being plotted on it. -# Common sizes: (10, 7.5) and (12, 9) -fig, ax = plt.subplots(1, 1, figsize=(12, 14)) - -# Remove the plot frame lines. They are unnecessary here. -ax.spines['top'].set_visible(False) -ax.spines['bottom'].set_visible(False) -ax.spines['right'].set_visible(False) -ax.spines['left'].set_visible(False) - -# Ensure that the axis ticks only show up on the bottom and left of the plot. -# Ticks on the right and top of the plot are generally unnecessary. -ax.get_xaxis().tick_bottom() -ax.get_yaxis().tick_left() - -fig.subplots_adjust(left=.06, right=.75, bottom=.02, top=.94) -# Limit the range of the plot to only where the data is. -# Avoid unnecessary whitespace. -ax.set_xlim(1969.5, 2011.1) -ax.set_ylim(-0.25, 90) - -# Make sure your axis ticks are large enough to be easily read. -# You don't want your viewers squinting to read your plot. -plt.xticks(range(1970, 2011, 10), fontsize=14) -plt.yticks(range(0, 91, 10), fontsize=14) -ax.xaxis.set_major_formatter(plt.FuncFormatter('{:.0f}'.format)) -ax.yaxis.set_major_formatter(plt.FuncFormatter('{:.0f}%'.format)) - -# Provide tick lines across the plot to help your viewers trace along -# the axis ticks. Make sure that the lines are light and small so they -# don't obscure the primary data lines. -plt.grid(True, 'major', 'y', ls='--', lw=.5, c='k', alpha=.3) - -# Remove the tick marks; they are unnecessary with the tick lines we just -# plotted. -plt.tick_params(axis='both', which='both', bottom='off', top='off', - labelbottom='on', left='off', right='off', labelleft='on') - -# Now that the plot is prepared, it's time to actually plot the data! -# Note that I plotted the majors in order of the highest % in the final year. -majors = ['Health Professions', 'Public Administration', 'Education', - 'Psychology', 'Foreign Languages', 'English', - 'Communications\nand Journalism', 'Art and Performance', 'Biology', - 'Agriculture', 'Social Sciences and History', 'Business', - 'Math and Statistics', 'Architecture', 'Physical Sciences', - 'Computer Science', 'Engineering'] - -y_offsets = {'Foreign Languages': 0.5, 'English': -0.5, - 'Communications\nand Journalism': 0.75, - 'Art and Performance': -0.25, 'Agriculture': 1.25, - 'Social Sciences and History': 0.25, 'Business': -0.75, - 'Math and Statistics': 0.75, 'Architecture': -0.75, - 'Computer Science': 0.75, 'Engineering': -0.25} - -for rank, column in enumerate(majors): - # Plot each line separately with its own color. - column_rec_name = column.replace('\n', '_').replace(' ', '_').lower() - - line = plt.plot(gender_degree_data.year, - gender_degree_data[column_rec_name], - lw=2.5, - color=color_sequence[rank]) - - # Add a text label to the right end of every line. Most of the code below - # is adding specific offsets y position because some labels overlapped. - y_pos = gender_degree_data[column_rec_name][-1] - 0.5 - - if column in y_offsets: - y_pos += y_offsets[column] - - # Again, make sure that all labels are large enough to be easily read - # by the viewer. - plt.text(2011.5, y_pos, column, fontsize=14, color=color_sequence[rank]) - -# Make the title big enough so it spans the entire plot, but don't make it -# so big that it requires two lines to show. - -# Note that if the title is descriptive enough, it is unnecessary to include -# axis labels; they are self-evident, in this plot's case. -fig.suptitle('Percentage of Bachelor\'s degrees conferred to women in ' - 'the U.S.A. by major (1970-2011)\n', fontsize=18, ha='center') - -# Finally, save the figure as a PNG. -# You can also save it as a PDF, JPEG, etc. -# Just change the file extension in this call. -# plt.savefig('percent-bachelors-degrees-women-usa.png', bbox_inches='tight') -plt.show() diff --git a/examples/showcase/firefox.py b/examples/showcase/firefox.py deleted file mode 100644 index 6977634b6999..000000000000 --- a/examples/showcase/firefox.py +++ /dev/null @@ -1,63 +0,0 @@ -import re -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.path import Path -import matplotlib.patches as patches - -# From: http://raphaeljs.com/icons/#firefox -firefox = "M28.4,22.469c0.479-0.964,0.851-1.991,1.095-3.066c0.953-3.661,0.666-6.854,0.666-6.854l-0.327,2.104c0,0-0.469-3.896-1.044-5.353c-0.881-2.231-1.273-2.214-1.274-2.21c0.542,1.379,0.494,2.169,0.483,2.288c-0.01-0.016-0.019-0.032-0.027-0.047c-0.131-0.324-0.797-1.819-2.225-2.878c-2.502-2.481-5.943-4.014-9.745-4.015c-4.056,0-7.705,1.745-10.238,4.525C5.444,6.5,5.183,5.938,5.159,5.317c0,0-0.002,0.002-0.006,0.005c0-0.011-0.003-0.021-0.003-0.031c0,0-1.61,1.247-1.436,4.612c-0.299,0.574-0.56,1.172-0.777,1.791c-0.375,0.817-0.75,2.004-1.059,3.746c0,0,0.133-0.422,0.399-0.988c-0.064,0.482-0.103,0.971-0.116,1.467c-0.09,0.845-0.118,1.865-0.039,3.088c0,0,0.032-0.406,0.136-1.021c0.834,6.854,6.667,12.165,13.743,12.165l0,0c1.86,0,3.636-0.37,5.256-1.036C24.938,27.771,27.116,25.196,28.4,22.469zM16.002,3.356c2.446,0,4.73,0.68,6.68,1.86c-2.274-0.528-3.433-0.261-3.423-0.248c0.013,0.015,3.384,0.589,3.981,1.411c0,0-1.431,0-2.856,0.41c-0.065,0.019,5.242,0.663,6.327,5.966c0,0-0.582-1.213-1.301-1.42c0.473,1.439,0.351,4.17-0.1,5.528c-0.058,0.174-0.118-0.755-1.004-1.155c0.284,2.037-0.018,5.268-1.432,6.158c-0.109,0.07,0.887-3.189,0.201-1.93c-4.093,6.276-8.959,2.539-10.934,1.208c1.585,0.388,3.267,0.108,4.242-0.559c0.982-0.672,1.564-1.162,2.087-1.047c0.522,0.117,0.87-0.407,0.464-0.872c-0.405-0.466-1.392-1.105-2.725-0.757c-0.94,0.247-2.107,1.287-3.886,0.233c-1.518-0.899-1.507-1.63-1.507-2.095c0-0.366,0.257-0.88,0.734-1.028c0.58,0.062,1.044,0.214,1.537,0.466c0.005-0.135,0.006-0.315-0.001-0.519c0.039-0.077,0.015-0.311-0.047-0.596c-0.036-0.287-0.097-0.582-0.19-0.851c0.01-0.002,0.017-0.007,0.021-0.021c0.076-0.344,2.147-1.544,2.299-1.659c0.153-0.114,0.55-0.378,0.506-1.183c-0.015-0.265-0.058-0.294-2.232-0.286c-0.917,0.003-1.425-0.894-1.589-1.245c0.222-1.231,0.863-2.11,1.919-2.704c0.02-0.011,0.015-0.021-0.008-0.027c0.219-0.127-2.524-0.006-3.76,1.604C9.674,8.045,9.219,7.95,8.71,7.95c-0.638,0-1.139,0.07-1.603,0.187c-0.05,0.013-0.122,0.011-0.208-0.001C6.769,8.04,6.575,7.88,6.365,7.672c0.161-0.18,0.324-0.356,0.495-0.526C9.201,4.804,12.43,3.357,16.002,3.356z" - - -def svg_parse(path): - commands = {'M': (Path.MOVETO,), - 'L': (Path.LINETO,), - 'Q': (Path.CURVE3,)*2, - 'C': (Path.CURVE4,)*3, - 'Z': (Path.CLOSEPOLY,)} - path_re = re.compile(r'([MLHVCSQTAZ])([^MLHVCSQTAZ]+)', re.IGNORECASE) - float_re = re.compile(r'(?:[\s,]*)([+-]?\d+(?:\.\d+)?)') - vertices = [] - codes = [] - last = (0, 0) - for cmd, values in path_re.findall(path): - points = [float(v) for v in float_re.findall(values)] - points = np.array(points).reshape((len(points)//2, 2)) - if cmd.islower(): - points += last - cmd = cmd.capitalize() - last = points[-1] - codes.extend(commands[cmd]) - vertices.extend(points.tolist()) - return codes, vertices - -# SVG to matplotlib -codes, verts = svg_parse(firefox) -verts = np.array(verts) -path = Path(verts, codes) - -# Make upside down -verts[:, 1] *= -1 -xmin, xmax = verts[:, 0].min()-1, verts[:, 0].max()+1 -ymin, ymax = verts[:, 1].min()-1, verts[:, 1].max()+1 - -fig = plt.figure(figsize=(5, 5)) -ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], frameon=False, aspect=1) - -# White outline (width = 6) -patch = patches.PathPatch(path, facecolor='None', edgecolor='w', lw=6) -ax.add_patch(patch) - -# Actual shape with black outline -patch = patches.PathPatch(path, facecolor='orange', edgecolor='k', lw=2) -ax.add_patch(patch) - -# Centering -ax.set_xlim(xmin, xmax) -ax.set_ylim(ymin, ymax) - -# No ticks -ax.set_xticks([]) -ax.set_yticks([]) - -# Display -plt.show() diff --git a/examples/showcase/integral_demo.py b/examples/showcase/integral_demo.py deleted file mode 100644 index 144a5e93f772..000000000000 --- a/examples/showcase/integral_demo.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Plot demonstrating the integral as the area under a curve. - -Although this is a simple example, it demonstrates some important tweaks: - - * A simple line plot with custom color and line width. - * A shaded region created using a Polygon patch. - * A text label with mathtext rendering. - * figtext calls to label the x- and y-axes. - * Use of axis spines to hide the top and right spines. - * Custom tick placement and labels. -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.patches import Polygon - - -def func(x): - return (x - 3) * (x - 5) * (x - 7) + 85 - - -a, b = 2, 9 # integral limits -x = np.linspace(0, 10) -y = func(x) - -fig, ax = plt.subplots() -plt.plot(x, y, 'r', linewidth=2) -plt.ylim(ymin=0) - -# Make the shaded region -ix = np.linspace(a, b) -iy = func(ix) -verts = [(a, 0)] + list(zip(ix, iy)) + [(b, 0)] -poly = Polygon(verts, facecolor='0.9', edgecolor='0.5') -ax.add_patch(poly) - -plt.text(0.5 * (a + b), 30, r"$\int_a^b f(x)\mathrm{d}x$", - horizontalalignment='center', fontsize=20) - -plt.figtext(0.9, 0.05, '$x$') -plt.figtext(0.1, 0.9, '$y$') - -ax.spines['right'].set_visible(False) -ax.spines['top'].set_visible(False) -ax.xaxis.set_ticks_position('bottom') - -ax.set_xticks((a, b)) -ax.set_xticklabels(('$a$', '$b$')) -ax.set_yticks([]) - -plt.show() diff --git a/examples/showcase/xkcd.py b/examples/showcase/xkcd.py deleted file mode 100644 index 517a2e7c1afa..000000000000 --- a/examples/showcase/xkcd.py +++ /dev/null @@ -1,54 +0,0 @@ -import matplotlib.pyplot as plt -import numpy as np - -with plt.xkcd(): - # Based on "Stove Ownership" from XKCD by Randall Monroe - # http://xkcd.com/418/ - - fig = plt.figure() - ax = fig.add_axes((0.1, 0.2, 0.8, 0.7)) - ax.spines['right'].set_color('none') - ax.spines['top'].set_color('none') - plt.xticks([]) - plt.yticks([]) - ax.set_ylim([-30, 10]) - - data = np.ones(100) - data[70:] -= np.arange(30) - - plt.annotate( - 'THE DAY I REALIZED\nI COULD COOK BACON\nWHENEVER I WANTED', - xy=(70, 1), arrowprops=dict(arrowstyle='->'), xytext=(15, -10)) - - plt.plot(data) - - plt.xlabel('time') - plt.ylabel('my overall health') - fig.text( - 0.5, 0.05, - '"Stove Ownership" from xkcd by Randall Monroe', - ha='center') - - # Based on "The Data So Far" from XKCD by Randall Monroe - # http://xkcd.com/373/ - - fig = plt.figure() - ax = fig.add_axes((0.1, 0.2, 0.8, 0.7)) - ax.bar([-0.125, 1.0 - 0.125], [0, 100], 0.25) - ax.spines['right'].set_color('none') - ax.spines['top'].set_color('none') - ax.xaxis.set_ticks_position('bottom') - ax.set_xticks([0, 1]) - ax.set_xlim([-0.5, 1.5]) - ax.set_ylim([0, 110]) - ax.set_xticklabels(['CONFIRMED BY\nEXPERIMENT', 'REFUTED BY\nEXPERIMENT']) - plt.yticks([]) - - plt.title("CLAIMS OF SUPERNATURAL POWERS") - - fig.text( - 0.5, 0.05, - '"The Data So Far" from xkcd by Randall Monroe', - ha='center') - -plt.show() diff --git a/examples/specialty_plots/topographic_hillshading.py b/examples/specialty_plots/topographic_hillshading.py deleted file mode 100644 index 751ea6fe93c7..000000000000 --- a/examples/specialty_plots/topographic_hillshading.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Demonstrates the visual effect of varying blend mode and vertical exaggeration -on "hillshaded" plots. - -Note that the "overlay" and "soft" blend modes work well for complex surfaces -such as this example, while the default "hsv" blend mode works best for smooth -surfaces such as many mathematical functions. - -In most cases, hillshading is used purely for visual purposes, and *dx*/*dy* -can be safely ignored. In that case, you can tweak *vert_exag* (vertical -exaggeration) by trial and error to give the desired visual effect. However, -this example demonstrates how to use the *dx* and *dy* kwargs to ensure that -the *vert_exag* parameter is the true vertical exaggeration. -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.cbook import get_sample_data -from matplotlib.colors import LightSource - -dem = np.load(get_sample_data('jacksboro_fault_dem.npz')) -z = dem['elevation'] - -#-- Optional dx and dy for accurate vertical exaggeration -------------------- -# If you need topographically accurate vertical exaggeration, or you don't want -# to guess at what *vert_exag* should be, you'll need to specify the cellsize -# of the grid (i.e. the *dx* and *dy* parameters). Otherwise, any *vert_exag* -# value you specify will be realitive to the grid spacing of your input data -# (in other words, *dx* and *dy* default to 1.0, and *vert_exag* is calculated -# relative to those parameters). Similarly, *dx* and *dy* are assumed to be in -# the same units as your input z-values. Therefore, we'll need to convert the -# given dx and dy from decimal degrees to meters. -dx, dy = dem['dx'], dem['dy'] -dy = 111200 * dy -dx = 111200 * dx * np.cos(np.radians(dem['ymin'])) -#----------------------------------------------------------------------------- - -# Shade from the northwest, with the sun 45 degrees from horizontal -ls = LightSource(azdeg=315, altdeg=45) -cmap = plt.cm.gist_earth - -fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(8, 9)) -plt.setp(axes.flat, xticks=[], yticks=[]) - -# Vary vertical exaggeration and blend mode and plot all combinations -for col, ve in zip(axes.T, [0.1, 1, 10]): - # Show the hillshade intensity image in the first row - col[0].imshow(ls.hillshade(z, vert_exag=ve, dx=dx, dy=dy), cmap='gray') - - # Place hillshaded plots with different blend modes in the rest of the rows - for ax, mode in zip(col[1:], ['hsv', 'overlay', 'soft']): - rgb = ls.shade(z, cmap=cmap, blend_mode=mode, - vert_exag=ve, dx=dx, dy=dy) - ax.imshow(rgb) - -# Label rows and columns -for ax, ve in zip(axes[0], [0.1, 1, 10]): - ax.set_title('{0}'.format(ve), size=18) -for ax, mode in zip(axes[:, 0], ['Hillshade', 'hsv', 'overlay', 'soft']): - ax.set_ylabel(mode, size=18) - -# Group labels... -axes[0, 1].annotate('Vertical Exaggeration', (0.5, 1), xytext=(0, 30), - textcoords='offset points', xycoords='axes fraction', - ha='center', va='bottom', size=20) -axes[2, 0].annotate('Blend Mode', (0, 0.5), xytext=(-30, 0), - textcoords='offset points', xycoords='axes fraction', - ha='right', va='center', size=20, rotation=90) -fig.subplots_adjust(bottom=0.05, right=0.95) - -plt.show() diff --git a/examples/statistics/boxplot_color_demo.py b/examples/statistics/boxplot_color_demo.py deleted file mode 100644 index 085a5a9e64c5..000000000000 --- a/examples/statistics/boxplot_color_demo.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -================================= -Box plots with custom fill colors -================================= - -This plot illustrates how to create two types of box plots -(rectangular and notched), and how to fill them with custom -colors by accessing the properties of the artists of the -box plots. Additionally, the ``labels`` parameter is used to -provide x-tick labels for each sample. - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf -""" - -import matplotlib.pyplot as plt -import numpy as np - -# Random test data -np.random.seed(123) -all_data = [np.random.normal(0, std, size=100) for std in range(1, 4)] -labels = ['x1', 'x2', 'x3'] - -fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(9, 4)) - -# rectangular box plot -bplot1 = axes[0].boxplot(all_data, - vert=True, # vertical box alignment - patch_artist=True, # fill with color - labels=labels) # will be used to label x-ticks -axes[0].set_title('Rectangular box plot') - -# notch shape box plot -bplot2 = axes[1].boxplot(all_data, - notch=True, # notch shape - vert=True, # vertical box alignment - patch_artist=True, # fill with color - labels=labels) # will be used to label x-ticks -axes[1].set_title('Notched box plot') - -# fill with colors -colors = ['pink', 'lightblue', 'lightgreen'] -for bplot in (bplot1, bplot2): - for patch, color in zip(bplot['boxes'], colors): - patch.set_facecolor(color) - -# adding horizontal grid lines -for ax in axes: - ax.yaxis.grid(True) - ax.set_xlabel('Three separate samples') - ax.set_ylabel('Observed values') - -plt.show() diff --git a/examples/statistics/boxplot_demo.py b/examples/statistics/boxplot_demo.py deleted file mode 100644 index d065fd5997cb..000000000000 --- a/examples/statistics/boxplot_demo.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -========================================= -Demo of artist customization in box plots -========================================= - -This example demonstrates how to use the various kwargs -to fully customize box plots. The first figure demonstrates -how to remove and add individual components (note that the -mean is the only value not shown by default). The second -figure demonstrates how the styles of the artists can -be customized. It also demonstrates how to set the limit -of the whiskers to specific percentiles (lower right axes) - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf - -""" - -import numpy as np -import matplotlib.pyplot as plt - -# fake data -np.random.seed(937) -data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75) -labels = list('ABCD') -fs = 10 # fontsize - -# demonstrate how to toggle the display of different elements: -fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axes[0, 0].boxplot(data, labels=labels) -axes[0, 0].set_title('Default', fontsize=fs) - -axes[0, 1].boxplot(data, labels=labels, showmeans=True) -axes[0, 1].set_title('showmeans=True', fontsize=fs) - -axes[0, 2].boxplot(data, labels=labels, showmeans=True, meanline=True) -axes[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) - -axes[1, 0].boxplot(data, labels=labels, showbox=False, showcaps=False) -tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)' -axes[1, 0].set_title(tufte_title, fontsize=fs) - -axes[1, 1].boxplot(data, labels=labels, notch=True, bootstrap=10000) -axes[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs) - -axes[1, 2].boxplot(data, labels=labels, showfliers=False) -axes[1, 2].set_title('showfliers=False', fontsize=fs) - -for ax in axes.flatten(): - ax.set_yscale('log') - ax.set_yticklabels([]) - -fig.subplots_adjust(hspace=0.4) -plt.show() - - -# demonstrate how to customize the display different elements: -boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod') -flierprops = dict(marker='o', markerfacecolor='green', markersize=12, - linestyle='none') -medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick') -meanpointprops = dict(marker='D', markeredgecolor='black', - markerfacecolor='firebrick') -meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple') - -fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axes[0, 0].boxplot(data, boxprops=boxprops) -axes[0, 0].set_title('Custom boxprops', fontsize=fs) - -axes[0, 1].boxplot(data, flierprops=flierprops, medianprops=medianprops) -axes[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs) - -axes[0, 2].boxplot(data, whis='range') -axes[0, 2].set_title('whis="range"', fontsize=fs) - -axes[1, 0].boxplot(data, meanprops=meanpointprops, meanline=False, - showmeans=True) -axes[1, 0].set_title('Custom mean\nas point', fontsize=fs) - -axes[1, 1].boxplot(data, meanprops=meanlineprops, meanline=True, - showmeans=True) -axes[1, 1].set_title('Custom mean\nas line', fontsize=fs) - -axes[1, 2].boxplot(data, whis=[15, 85]) -axes[1, 2].set_title('whis=[15, 85]\n#percentiles', fontsize=fs) - -for ax in axes.flatten(): - ax.set_yscale('log') - ax.set_yticklabels([]) - -fig.suptitle("I never said they'd be pretty") -fig.subplots_adjust(hspace=0.4) -plt.show() diff --git a/examples/statistics/boxplot_vs_violin_demo.py b/examples/statistics/boxplot_vs_violin_demo.py deleted file mode 100644 index f183a6c7487d..000000000000 --- a/examples/statistics/boxplot_vs_violin_demo.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -=================================== -Box plot vs. violin plot comparison -=================================== - -Note that although violin plots are closely related to Tukey's (1977) -box plots, they add useful information such as the distribution of the -sample data (density trace). - -By default, box plots show data points outside 1.5 * the inter-quartile -range as outliers above or below the whiskers whereas violin plots show -the whole range of the data. - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf - -Violin plots require matplotlib >= 1.4. - -For more information on violin plots, the scikit-learn docs have a great -section: http://scikit-learn.org/stable/modules/density.html -""" - -import matplotlib.pyplot as plt -import numpy as np - -fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(9, 4)) - -# generate some random test data -all_data = [np.random.normal(0, std, 100) for std in range(6, 10)] - -# plot violin plot -axes[0].violinplot(all_data, - showmeans=False, - showmedians=True) -axes[0].set_title('Violin plot') - -# plot box plot -axes[1].boxplot(all_data) -axes[1].set_title('Box plot') - -# adding horizontal grid lines -for ax in axes: - ax.yaxis.grid(True) - ax.set_xticks([y + 1 for y in range(len(all_data))]) - ax.set_xlabel('Four separate samples') - ax.set_ylabel('Observed values') - -# add x-tick labels -plt.setp(axes, xticks=[y + 1 for y in range(len(all_data))], - xticklabels=['x1', 'x2', 'x3', 'x4']) -plt.show() diff --git a/examples/statistics/bxp_demo.py b/examples/statistics/bxp_demo.py deleted file mode 100644 index 90d8b4c4e399..000000000000 --- a/examples/statistics/bxp_demo.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -=================================== -Demo of the boxplot drawer function -=================================== - -This example demonstrates how to pass pre-computed box plot -statistics to the box plot drawer. The first figure demonstrates -how to remove and add individual components (note that the -mean is the only value not shown by default). The second -figure demonstrates how the styles of the artists can -be customized. - -A good general reference on boxplots and their history can be found -here: http://vita.had.co.nz/papers/boxplots.pdf -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.cbook as cbook - -# fake data -np.random.seed(937) -data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75) -labels = list('ABCD') - -# compute the boxplot stats -stats = cbook.boxplot_stats(data, labels=labels, bootstrap=10000) -# After we've computed the stats, we can go through and change anything. -# Just to prove it, I'll set the median of each set to the median of all -# the data, and double the means -for n in range(len(stats)): - stats[n]['med'] = np.median(data) - stats[n]['mean'] *= 2 - -print(stats[0].keys()) - -fs = 10 # fontsize - -# demonstrate how to toggle the display of different elements: -fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True) -axes[0, 0].bxp(stats) -axes[0, 0].set_title('Default', fontsize=fs) - -axes[0, 1].bxp(stats, showmeans=True) -axes[0, 1].set_title('showmeans=True', fontsize=fs) - -axes[0, 2].bxp(stats, showmeans=True, meanline=True) -axes[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs) - -axes[1, 0].bxp(stats, showbox=False, showcaps=False) -tufte_title = 'Tufte Style\n(showbox=False,\nshowcaps=False)' -axes[1, 0].set_title(tufte_title, fontsize=fs) - -axes[1, 1].bxp(stats, shownotches=True) -axes[1, 1].set_title('notch=True', fontsize=fs) - -axes[1, 2].bxp(stats, showfliers=False) -axes[1, 2].set_title('showfliers=False', fontsize=fs) - -for ax in axes.flatten(): - ax.set_yscale('log') - ax.set_yticklabels([]) - -fig.subplots_adjust(hspace=0.4) -plt.show() - -# demonstrate how to customize the display different elements: -boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod') -flierprops = dict(marker='o', markerfacecolor='green', markersize=12, - linestyle='none') -medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick') -meanpointprops = dict(marker='D', markeredgecolor='black', - markerfacecolor='firebrick') -meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple') - -fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(6, 6), sharey=True) -axes[0, 0].bxp(stats, boxprops=boxprops) -axes[0, 0].set_title('Custom boxprops', fontsize=fs) - -axes[0, 1].bxp(stats, flierprops=flierprops, medianprops=medianprops) -axes[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs) - -axes[1, 0].bxp(stats, meanprops=meanpointprops, meanline=False, - showmeans=True) -axes[1, 0].set_title('Custom mean\nas point', fontsize=fs) - -axes[1, 1].bxp(stats, meanprops=meanlineprops, meanline=True, - showmeans=True) -axes[1, 1].set_title('Custom mean\nas line', fontsize=fs) - -for ax in axes.flatten(): - ax.set_yscale('log') - ax.set_yticklabels([]) - -fig.suptitle("I never said they'd be pretty") -fig.subplots_adjust(hspace=0.4) -plt.show() diff --git a/examples/statistics/customized_violin_demo.py b/examples/statistics/customized_violin_demo.py deleted file mode 100644 index e37a034400d5..000000000000 --- a/examples/statistics/customized_violin_demo.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -================================= -Demo of violin plot customization -================================= - -This example demonstrates how to fully customize violin plots. -The first plot shows the default style by providing only -the data. The second plot first limits what matplotlib draws -with additional kwargs. Then a simplified representation of -a box plot is drawn on top. Lastly, the styles of the artists -of the violins are modified. - -For more information on violin plots, the scikit-learn docs have a great -section: http://scikit-learn.org/stable/modules/density.html -""" - -import matplotlib.pyplot as plt -import numpy as np - - -def adjacent_values(vals, q1, q3): - upper_adjacent_value = q3 + (q3 - q1) * 1.5 - upper_adjacent_value = np.clip(upper_adjacent_value, q3, vals[-1]) - - lower_adjacent_value = q1 - (q3 - q1) * 1.5 - lower_adjacent_value = np.clip(lower_adjacent_value, vals[0], q1) - return lower_adjacent_value, upper_adjacent_value - - -def set_axis_style(ax, labels): - ax.get_xaxis().set_tick_params(direction='out') - ax.xaxis.set_ticks_position('bottom') - ax.set_xticks(np.arange(1, len(labels) + 1)) - ax.set_xticklabels(labels) - ax.set_xlim(0.25, len(labels) + 0.75) - ax.set_xlabel('Sample name') - - -# create test data -np.random.seed(123) -data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 5)] - -fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 4), sharey=True) - -ax1.set_title('Default violin plot') -ax1.set_ylabel('Observed values') -ax1.violinplot(data) - -ax2.set_title('Customized violin plot') -parts = ax2.violinplot( - data, showmeans=False, showmedians=False, - showextrema=False) - -for pc in parts['bodies']: - pc.set_facecolor('#D43F3A') - pc.set_edgecolor('black') - pc.set_alpha(1) - -quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1) -whiskers = np.array([ - adjacent_values(sorted_array, q1, q3) - for sorted_array, q1, q3 in zip(data, quartile1, quartile3)]) -whiskersMin, whiskersMax = whiskers[:, 0], whiskers[:, 1] - -inds = np.arange(1, len(medians) + 1) -ax2.scatter(inds, medians, marker='o', color='white', s=30, zorder=3) -ax2.vlines(inds, quartile1, quartile3, color='k', linestyle='-', lw=5) -ax2.vlines(inds, whiskersMin, whiskersMax, color='k', linestyle='-', lw=1) - -# set style for the axes -labels = ['A', 'B', 'C', 'D'] -for ax in [ax1, ax2]: - set_axis_style(ax, labels) - -plt.subplots_adjust(bottom=0.15, wspace=0.05) -plt.show() diff --git a/examples/statistics/errorbar_demo.py b/examples/statistics/errorbar_demo.py deleted file mode 100644 index 540cec3ab9b7..000000000000 --- a/examples/statistics/errorbar_demo.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -============================= -Demo of the errorbar function -============================= - -This exhibits the most basic use of the error bar method. -In this case, constant values are provided for the error -in both the x- and y-directions. -""" - -import numpy as np -import matplotlib.pyplot as plt - -# example data -x = np.arange(0.1, 4, 0.5) -y = np.exp(-x) - -fig, ax = plt.subplots() -ax.errorbar(x, y, xerr=0.2, yerr=0.4) -plt.show() diff --git a/examples/statistics/errorbar_demo_features.py b/examples/statistics/errorbar_demo_features.py deleted file mode 100644 index dfe8f68b5524..000000000000 --- a/examples/statistics/errorbar_demo_features.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -=================================================== -Demo of the different ways of specifying error bars -=================================================== - -Errors can be specified as a constant value (as shown in -`errorbar_demo.py`). However, this example demonstrates -how they vary by specifying arrays of error values. - -If the raw ``x`` and ``y`` data have length N, there are two options: - -Array of shape (N,): - Error varies for each point, but the error values are - symmetric (i.e. the lower and upper values are equal). - -Array of shape (2, N): - Error varies for each point, and the lower and upper limits - (in that order) are different (asymmetric case) - -In addition, this example demonstrates how to use log -scale with error bars. -""" - -import numpy as np -import matplotlib.pyplot as plt - -# example data -x = np.arange(0.1, 4, 0.5) -y = np.exp(-x) - -# example error bar values that vary with x-position -error = 0.1 + 0.2 * x - -fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True) -ax0.errorbar(x, y, yerr=error, fmt='-o') -ax0.set_title('variable, symmetric error') - -# error bar values w/ different -/+ errors that -# also vary with the x-position -lower_error = 0.4 * error -upper_error = error -asymmetric_error = [lower_error, upper_error] - -ax1.errorbar(x, y, xerr=asymmetric_error, fmt='o') -ax1.set_title('variable, asymmetric error') -ax1.set_yscale('log') -plt.show() diff --git a/examples/statistics/errorbar_limits.py b/examples/statistics/errorbar_limits.py deleted file mode 100644 index a765445598fb..000000000000 --- a/examples/statistics/errorbar_limits.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -=========================================================== -Demo of how to include upper and lower limits in error bars -=========================================================== - -In matplotlib, errors bars can have "limits". Applying limits to the -error bars essentially makes the error unidirectional. Because of that, -upper and lower limits can be applied in both the y- and x-directions -via the ``uplims``, ``lolims``, ``xuplims``, and ``xlolims`` parameters, -respectively. These parameters can be scalar or boolean arrays. - -For example, if ``xlolims`` is ``True``, the x-error bars will only -extend from the data towards increasing values. If ``uplims`` is an -array filled with ``False`` except for the 4th and 7th values, all of the -y-error bars will be bidirectional, except the 4th and 7th bars, which -will extend from the data towards decreasing y-values. -""" - -import numpy as np -import matplotlib.pyplot as plt - -# example data -x = np.array([0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]) -y = np.exp(-x) -xerr = 0.1 -yerr = 0.2 - -# lower & upper limits of the error -lolims = np.array([0, 0, 1, 0, 1, 0, 0, 0, 1, 0], dtype=bool) -uplims = np.array([0, 1, 0, 0, 0, 1, 0, 0, 0, 1], dtype=bool) -ls = 'dotted' - -fig, ax = plt.subplots(figsize=(7, 4)) - -# standard error bars -ax.errorbar(x, y, xerr=xerr, yerr=yerr, linestyle=ls) - -# including upper limits -ax.errorbar(x, y + 0.5, xerr=xerr, yerr=yerr, uplims=uplims, - linestyle=ls) - -# including lower limits -ax.errorbar(x, y + 1.0, xerr=xerr, yerr=yerr, lolims=lolims, - linestyle=ls) - -# including upper and lower limits -ax.errorbar(x, y + 1.5, xerr=xerr, yerr=yerr, - lolims=lolims, uplims=uplims, - marker='o', markersize=8, - linestyle=ls) - -# Plot a series with lower and upper limits in both x & y -# constant x-error with varying y-error -xerr = 0.2 -yerr = np.zeros(x.shape) + 0.2 -yerr[[3, 6]] = 0.3 - -# mock up some limits by modifying previous data -xlolims = lolims -xuplims = uplims -lolims = np.zeros(x.shape) -uplims = np.zeros(x.shape) -lolims[[6]] = True # only limited at this index -uplims[[3]] = True # only limited at this index - -# do the plotting -ax.errorbar(x, y + 2.1, xerr=xerr, yerr=yerr, - xlolims=xlolims, xuplims=xuplims, - uplims=uplims, lolims=lolims, - marker='o', markersize=8, - linestyle='none') - -# tidy up the figure -ax.set_xlim((0, 5.5)) -ax.set_title('Errorbar upper and lower limits') -plt.show() diff --git a/examples/statistics/errorbars_and_boxes.py b/examples/statistics/errorbars_and_boxes.py deleted file mode 100644 index 113e299be236..000000000000 --- a/examples/statistics/errorbars_and_boxes.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -============================================================ -Demo on creating boxes from error bars using PatchCollection -============================================================ - -In this example, we snazz up a pretty standard error bar plot by adding -a rectangle patch defined by the limits of the bars in both the x- and -y- directions. To do this, we have to write our own custom function -called ``make_error_boxes``. Close inspection of this function will -reveal the preferred pattern in writing functions for matplotlib: - - 1. an ``Axes`` object is passed directly to the function - 2. the function operates on the `Axes` methods directly, not through - the ``pyplot`` interface - 3. plotting kwargs that could be abbreviated are spelled out for - better code readability in the future (for example we use - ``facecolor`` instead of ``fc``) - 4. the artists returned by the ``Axes`` plotting methods are then - returned by the function so that, if desired, their styles - can be modified later outside of the function (they are not - modified in this example). -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.collections import PatchCollection -from matplotlib.patches import Rectangle - -# Number of data points -n = 5 - -# Dummy data -np.random.seed(10) -x = np.arange(0, n, 1) -y = np.random.rand(n) * 5. - -# Dummy errors (above and below) -xerr = np.random.rand(2, n) + 0.1 -yerr = np.random.rand(2, n) + 0.2 - - -def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r', - edgecolor='None', alpha=0.5): - - # Create list for all the error patches - errorboxes = [] - - # Loop over data points; create box from errors at each point - for x, y, xe, ye in zip(xdata, ydata, xerror.T, yerror.T): - rect = Rectangle((x - xe[0], y - ye[0]), xe.sum(), ye.sum()) - errorboxes.append(rect) - - # Create patch collection with specified colour/alpha - pc = PatchCollection(errorboxes, facecolor=facecolor, alpha=alpha, - edgecolor=edgecolor) - - # Add collection to axes - ax.add_collection(pc) - - # Plot errorbars - artists = ax.errorbar(xdata, ydata, xerr=xerror, yerr=yerror, - fmt='None', ecolor='k') - - return artists - - -# Create figure and axes -fig, ax = plt.subplots(1) - -# Call function to create error boxes -_ = make_error_boxes(ax, x, y, xerr, yerr) - -plt.show() diff --git a/examples/statistics/histogram_demo_cumulative.py b/examples/statistics/histogram_demo_cumulative.py deleted file mode 100644 index e5918684e2f2..000000000000 --- a/examples/statistics/histogram_demo_cumulative.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -========================================================== -Demo of using histograms to plot a cumulative distribution -========================================================== - -This shows how to plot a cumulative, normalized histogram as a -step function in order to visualize the empirical cumulative -distribution function (CDF) of a sample. We also use the ``mlab`` -module to show the theoretical CDF. - -A couple of other options to the ``hist`` function are demonstrated. -Namely, we use the ``normed`` parameter to normalize the histogram and -a couple of different options to the ``cumulative`` parameter. -The ``normed`` parameter takes a boolean value. When ``True``, the bin -heights are scaled such that the total area of the histogram is 1. The -``cumulative`` kwarg is a little more nuanced. Like ``normed``, you -can pass it True or False, but you can also pass it -1 to reverse the -distribution. - -Since we're showing a normalized and cumulative histogram, these curves -are effectively the cumulative distribution functions (CDFs) of the -samples. In engineering, empirical CDFs are sometimes called -"non-exceedance" curves. In other words, you can look at the -y-value for a given-x-value to get the probability of and observation -from the sample not exceeding that x-value. For example, the value of -225 on the x-axis corresponds to about 0.85 on the y-axis, so there's an -85% chance that an observation in the sample does not exceed 225. -Conversely, setting, ``cumulative`` to -1 as is done in the -last series for this example, creates a "exceedance" curve. - -Selecting different bin counts and sizes can significantly affect the -shape of a histogram. The Astropy docs have a great section on how to -select these parameters: -http://docs.astropy.org/en/stable/visualization/histogram.html - -""" - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import mlab - -np.random.seed(0) - -mu = 200 -sigma = 25 -n_bins = 50 -x = np.random.normal(mu, sigma, size=100) - -fig, ax = plt.subplots(figsize=(8, 4)) - -# plot the cumulative histograme -n, bins, patches = ax.hist(x, n_bins, normed=1, histtype='step', - cumulative=True, label='Empirical') - -# Add a line showing the expected distribution. -y = mlab.normpdf(bins, mu, sigma).cumsum() -y /= y[-1] - -ax.plot(bins, y, 'k--', linewidth=1.5, label='Theoretical') - -# Overlay a reversed cumulative histogram. -ax.hist(x, bins=bins, normed=1, histtype='step', cumulative=-1, - label='Reversed emp.') - -# tidy up the figure -ax.grid(True) -ax.legend(loc='right') -ax.set_title('Cumulative step histograms') -ax.set_xlabel('Annual rainfall (mm)') -ax.set_ylabel('Likelihood of occurrence') - -plt.show() diff --git a/examples/statistics/histogram_demo_features.py b/examples/statistics/histogram_demo_features.py deleted file mode 100644 index c662f36ac5e1..000000000000 --- a/examples/statistics/histogram_demo_features.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -========================================================= -Demo of the histogram (hist) function with a few features -========================================================= - -In addition to the basic histogram, this demo shows a few optional -features: - - * Setting the number of data bins - * The ``normed`` flag, which normalizes bin heights so that the - integral of the histogram is 1. The resulting histogram is an - approximation of the probability density function. - * Setting the face color of the bars - * Setting the opacity (alpha value). - -Selecting different bin counts and sizes can significantly affect the -shape of a histogram. The Astropy docs have a great section on how to -select these parameters: -http://docs.astropy.org/en/stable/visualization/histogram.html -""" - -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.pyplot as plt - -np.random.seed(0) - -# example data -mu = 100 # mean of distribution -sigma = 15 # standard deviation of distribution -x = mu + sigma * np.random.randn(437) - -num_bins = 50 - -fig, ax = plt.subplots() - -# the histogram of the data -n, bins, patches = ax.hist(x, num_bins, normed=1) - -# add a 'best fit' line -y = mlab.normpdf(bins, mu, sigma) -ax.plot(bins, y, '--') -ax.set_xlabel('Smarts') -ax.set_ylabel('Probability density') -ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$') - -# Tweak spacing to prevent clipping of ylabel -fig.tight_layout() -plt.show() diff --git a/examples/statistics/histogram_demo_histtypes.py b/examples/statistics/histogram_demo_histtypes.py deleted file mode 100644 index 5a6f9fe08a8e..000000000000 --- a/examples/statistics/histogram_demo_histtypes.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -================================================================ -Demo of the histogram function's different ``histtype`` settings -================================================================ - -* Histogram with step curve that has a color fill. -* Histogram with custom and unequal bin widths. - -Selecting different bin counts and sizes can significantly affect the -shape of a histogram. The Astropy docs have a great section on how to -select these parameters: -http://docs.astropy.org/en/stable/visualization/histogram.html -""" - -import numpy as np -import matplotlib.pyplot as plt - -np.random.seed(0) - -mu = 200 -sigma = 25 -x = np.random.normal(mu, sigma, size=100) - -fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(8, 4)) - -ax0.hist(x, 20, normed=1, histtype='stepfilled', facecolor='g', alpha=0.75) -ax0.set_title('stepfilled') - -# Create a histogram by providing the bin edges (unequally spaced). -bins = [100, 150, 180, 195, 205, 220, 250, 300] -ax1.hist(x, bins, normed=1, histtype='bar', rwidth=0.8) -ax1.set_title('unequal bins') - -fig.tight_layout() -plt.show() diff --git a/examples/statistics/histogram_demo_multihist.py b/examples/statistics/histogram_demo_multihist.py deleted file mode 100644 index 04619cd670e2..000000000000 --- a/examples/statistics/histogram_demo_multihist.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -============================================================= -Demo of the histogram (hist) function with multiple data sets -============================================================= - -Plot histogram with multiple sample sets and demonstrate: - - * Use of legend with multiple sample sets - * Stacked bars - * Step curve with no fill - * Data sets of different sample sizes - -Selecting different bin counts and sizes can significantly affect the -shape of a histogram. The Astropy docs have a great section on how to -select these parameters: -http://docs.astropy.org/en/stable/visualization/histogram.html -""" - -import numpy as np -import matplotlib.pyplot as plt - -np.random.seed(0) - -n_bins = 10 -x = np.random.randn(1000, 3) - -fig, axes = plt.subplots(nrows=2, ncols=2) -ax0, ax1, ax2, ax3 = axes.flatten() - -colors = ['red', 'tan', 'lime'] -ax0.hist(x, n_bins, normed=1, histtype='bar', color=colors, label=colors) -ax0.legend(prop={'size': 10}) -ax0.set_title('bars with legend') - -ax1.hist(x, n_bins, normed=1, histtype='bar', stacked=True) -ax1.set_title('stacked bar') - -ax2.hist(x, n_bins, histtype='step', stacked=True, fill=False) -ax2.set_title('stack step (unfilled)') - -# Make a multiple-histogram of data-sets with different length. -x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]] -ax3.hist(x_multi, n_bins, histtype='bar') -ax3.set_title('different sample sizes') - -fig.tight_layout() -plt.show() diff --git a/examples/statistics/violinplot_demo.py b/examples/statistics/violinplot_demo.py deleted file mode 100644 index 5aaf8ea7699a..000000000000 --- a/examples/statistics/violinplot_demo.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -================================== -Demo of the basics of violin plots -================================== - -Violin plots are similar to histograms and box plots in that they show -an abstract representation of the probability distribution of the -sample. Rather than showing counts of data points that fall into bins -or order statistics, violin plots use kernel density estimation (KDE) to -compute an empirical distribution of the sample. That computation -is controlled by several parameters. This example demostrates how to -modify the number of points at which the KDE is evaluated (``points``) -and how to modify the band-width of the KDE (``bw_method``). - -For more information on violin plots and KDE, the scikit-learn docs -have a great section: http://scikit-learn.org/stable/modules/density.html -""" - -import random -import numpy as np -import matplotlib.pyplot as plt - -# fake data -fs = 10 # fontsize -pos = [1, 2, 4, 5, 7, 8] -data = [np.random.normal(0, std, size=100) for std in pos] - -fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(6, 6)) - -axes[0, 0].violinplot(data, pos, points=20, widths=0.3, - showmeans=True, showextrema=True, showmedians=True) -axes[0, 0].set_title('Custom violinplot 1', fontsize=fs) - -axes[0, 1].violinplot(data, pos, points=40, widths=0.5, - showmeans=True, showextrema=True, showmedians=True, - bw_method='silverman') -axes[0, 1].set_title('Custom violinplot 2', fontsize=fs) - -axes[0, 2].violinplot(data, pos, points=60, widths=0.7, showmeans=True, - showextrema=True, showmedians=True, bw_method=0.5) -axes[0, 2].set_title('Custom violinplot 3', fontsize=fs) - -axes[1, 0].violinplot(data, pos, points=80, vert=False, widths=0.7, - showmeans=True, showextrema=True, showmedians=True) -axes[1, 0].set_title('Custom violinplot 4', fontsize=fs) - -axes[1, 1].violinplot(data, pos, points=100, vert=False, widths=0.9, - showmeans=True, showextrema=True, showmedians=True, - bw_method='silverman') -axes[1, 1].set_title('Custom violinplot 5', fontsize=fs) - -axes[1, 2].violinplot(data, pos, points=200, vert=False, widths=1.1, - showmeans=True, showextrema=True, showmedians=True, - bw_method=0.5) -axes[1, 2].set_title('Custom violinplot 6', fontsize=fs) - -for ax in axes.flatten(): - ax.set_yticklabels([]) - -fig.suptitle("Violin Plotting Examples") -fig.subplots_adjust(hspace=0.4) -plt.show() diff --git a/examples/style_sheets/plot_bmh.py b/examples/style_sheets/plot_bmh.py deleted file mode 100644 index f44c319a675e..000000000000 --- a/examples/style_sheets/plot_bmh.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -This example demonstrates the "bmh" style, which is the design used in the -Bayesian Methods for Hackers online book. -""" -from numpy.random import beta -import matplotlib.pyplot as plt - -plt.style.use('bmh') - - -def plot_beta_hist(a, b): - plt.hist(beta(a, b, size=10000), histtype="stepfilled", - bins=25, alpha=0.8, normed=True) - return - -plot_beta_hist(10, 10) -plot_beta_hist(4, 12) -plot_beta_hist(50, 12) -plot_beta_hist(6, 55) - -plt.show() diff --git a/examples/style_sheets/plot_dark_background.py b/examples/style_sheets/plot_dark_background.py deleted file mode 100644 index dd1f0251b324..000000000000 --- a/examples/style_sheets/plot_dark_background.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This example demonstrates the "dark_background" style, which uses white for -elements that are typically black (text, borders, etc). Note, however, that not -all plot elements default to colors defined by an rc parameter. - -""" -import numpy as np -import matplotlib.pyplot as plt - - -plt.style.use('dark_background') - -L = 6 -x = np.linspace(0, L) -ncolors = len(plt.rcParams['axes.prop_cycle']) -shift = np.linspace(0, L, ncolors, endpoint=False) -for s in shift: - plt.plot(x, np.sin(x + s), 'o-') -plt.xlabel('x-axis') -plt.ylabel('y-axis') -plt.title('title') - -plt.show() diff --git a/examples/style_sheets/plot_fivethirtyeight.py b/examples/style_sheets/plot_fivethirtyeight.py deleted file mode 100644 index e3798e921d96..000000000000 --- a/examples/style_sheets/plot_fivethirtyeight.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -This shows an example of the "fivethirtyeight" styling, which -tries to replicate the styles from FiveThirtyEight.com. -""" - - -from matplotlib import pyplot as plt -import numpy as np - -x = np.linspace(0, 10) - -with plt.style.context('fivethirtyeight'): - plt.plot(x, np.sin(x) + x + np.random.randn(50)) - plt.plot(x, np.sin(x) + 0.5 * x + np.random.randn(50)) - plt.plot(x, np.sin(x) + 2 * x + np.random.randn(50)) - plt.plot(x, np.sin(x) - 0.5 * x + np.random.randn(50)) - plt.plot(x, np.sin(x) - 2 * x + np.random.randn(50)) - plt.plot(x, np.sin(x) + np.random.randn(50)) - - -plt.show() diff --git a/examples/style_sheets/plot_ggplot.py b/examples/style_sheets/plot_ggplot.py deleted file mode 100644 index b5777040f2a8..000000000000 --- a/examples/style_sheets/plot_ggplot.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -This example demonstrates the "ggplot" style, which adjusts the style to -emulate ggplot_ (a popular plotting package for R_). - -These settings were shamelessly stolen from [1]_ (with permission). - -.. [1] http://www.huyng.com/posts/sane-color-scheme-for-matplotlib/ - -.. _ggplot: http://ggplot2.org/ -.. _R: https://www.r-project.org/ - -""" -import numpy as np -import matplotlib.pyplot as plt - -plt.style.use('ggplot') - -fig, axes = plt.subplots(ncols=2, nrows=2) -ax1, ax2, ax3, ax4 = axes.ravel() - -# scatter plot (Note: `plt.scatter` doesn't use default colors) -x, y = np.random.normal(size=(2, 200)) -ax1.plot(x, y, 'o') - -# sinusoidal lines with colors from default color cycle -L = 2*np.pi -x = np.linspace(0, L) -ncolors = len(plt.rcParams['axes.prop_cycle']) -shift = np.linspace(0, L, ncolors, endpoint=False) -for s in shift: - ax2.plot(x, np.sin(x + s), '-') -ax2.margins(0) - -# bar graphs -x = np.arange(5) -y1, y2 = np.random.randint(1, 25, size=(2, 5)) -width = 0.25 -ax3.bar(x, y1, width) -ax3.bar(x + width, y2, width, - color=list(plt.rcParams['axes.prop_cycle'])[2]['color']) -ax3.set_xticks(x + width) -ax3.set_xticklabels(['a', 'b', 'c', 'd', 'e']) - -# circles with colors from default color cycle -for i, color in enumerate(plt.rcParams['axes.prop_cycle']): - xy = np.random.normal(size=2) - ax4.add_patch(plt.Circle(xy, radius=0.3, color=color['color'])) -ax4.axis('equal') -ax4.margins(0) - -plt.show() diff --git a/examples/style_sheets/plot_grayscale.py b/examples/style_sheets/plot_grayscale.py deleted file mode 100644 index e86dccd9932c..000000000000 --- a/examples/style_sheets/plot_grayscale.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -This example demonstrates the "grayscale" style sheet, which changes all colors -that are defined as rc parameters to grayscale. Note, however, that not all -plot elements default to colors defined by an rc parameter. - -""" -import numpy as np -import matplotlib.pyplot as plt - - -def color_cycle_example(ax): - L = 6 - x = np.linspace(0, L) - ncolors = len(plt.rcParams['axes.prop_cycle']) - shift = np.linspace(0, L, ncolors, endpoint=False) - for s in shift: - ax.plot(x, np.sin(x + s), 'o-') - - -def image_and_patch_example(ax): - ax.imshow(np.random.random(size=(20, 20)), interpolation='none') - c = plt.Circle((5, 5), radius=5, label='patch') - ax.add_patch(c) - - -plt.style.use('grayscale') - -fig, (ax1, ax2) = plt.subplots(ncols=2) - -color_cycle_example(ax1) -image_and_patch_example(ax2) - -plt.show() diff --git a/examples/style_sheets/style_sheets_reference.py b/examples/style_sheets/style_sheets_reference.py deleted file mode 100644 index ef12c1ac3a09..000000000000 --- a/examples/style_sheets/style_sheets_reference.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -====================== -Style sheets reference -====================== - -This script demonstrates the different available style sheets on a -common set of example plots: scatter plot, image, bar graph, patches, -line plot and histogram, - -""" - -import numpy as np -import matplotlib.pyplot as plt - - -def plot_scatter(ax, prng, nb_samples=100): - """Scatter plot. - """ - for mu, sigma, marker in [(-.5, 0.75, 'o'), (0.75, 1., 's')]: - x, y = prng.normal(loc=mu, scale=sigma, size=(2, nb_samples)) - ax.plot(x, y, ls='none', marker=marker) - ax.set_xlabel('X-label') - return ax - - -def plot_colored_sinusoidal_lines(ax): - """Plot sinusoidal lines with colors following the style color cycle. - """ - L = 2 * np.pi - x = np.linspace(0, L) - nb_colors = len(plt.rcParams['axes.prop_cycle']) - shift = np.linspace(0, L, nb_colors, endpoint=False) - for s in shift: - ax.plot(x, np.sin(x + s), '-') - ax.set_xlim([x[0], x[-1]]) - return ax - - -def plot_bar_graphs(ax, prng, min_value=5, max_value=25, nb_samples=5): - """Plot two bar graphs side by side, with letters as x-tick labels. - """ - x = np.arange(nb_samples) - ya, yb = prng.randint(min_value, max_value, size=(2, nb_samples)) - width = 0.25 - ax.bar(x, ya, width) - ax.bar(x + width, yb, width, color='C2') - ax.set_xticks(x + width) - ax.set_xticklabels(['a', 'b', 'c', 'd', 'e']) - return ax - - -def plot_colored_circles(ax, prng, nb_samples=15): - """Plot circle patches. - - NB: draws a fixed amount of samples, rather than using the length of - the color cycle, because different styles may have different numbers - of colors. - """ - for sty_dict, j in zip(plt.rcParams['axes.prop_cycle'], range(nb_samples)): - ax.add_patch(plt.Circle(prng.normal(scale=3, size=2), - radius=1.0, color=sty_dict['color'])) - # Force the limits to be the same across the styles (because different - # styles may have different numbers of available colors). - ax.set_xlim([-4, 8]) - ax.set_ylim([-5, 6]) - ax.set_aspect('equal', adjustable='box') # to plot circles as circles - return ax - - -def plot_image_and_patch(ax, prng, size=(20, 20)): - """Plot an image with random values and superimpose a circular patch. - """ - values = prng.random_sample(size=size) - ax.imshow(values, interpolation='none') - c = plt.Circle((5, 5), radius=5, label='patch') - ax.add_patch(c) - # Remove ticks - ax.set_xticks([]) - ax.set_yticks([]) - - -def plot_histograms(ax, prng, nb_samples=10000): - """Plot 4 histograms and a text annotation. - """ - params = ((10, 10), (4, 12), (50, 12), (6, 55)) - for a, b in params: - values = prng.beta(a, b, size=nb_samples) - ax.hist(values, histtype="stepfilled", bins=30, alpha=0.8, normed=True) - # Add a small annotation. - ax.annotate('Annotation', xy=(0.25, 4.25), xycoords='data', - xytext=(0.9, 0.9), textcoords='axes fraction', - va="top", ha="right", - bbox=dict(boxstyle="round", alpha=0.2), - arrowprops=dict( - arrowstyle="->", - connectionstyle="angle,angleA=-95,angleB=35,rad=10"), - ) - return ax - - -def plot_figure(style_label=""): - """Setup and plot the demonstration figure with a given style. - """ - # Use a dedicated RandomState instance to draw the same "random" values - # across the different figures. - prng = np.random.RandomState(96917002) - - # Tweak the figure size to be better suited for a row of numerous plots: - # double the width and halve the height. NB: use relative changes because - # some styles may have a figure size different from the default one. - (fig_width, fig_height) = plt.rcParams['figure.figsize'] - fig_size = [fig_width * 2, fig_height / 2] - - fig, axes = plt.subplots(ncols=6, nrows=1, num=style_label, - figsize=fig_size, squeeze=True) - axes[0].set_ylabel(style_label) - - plot_scatter(axes[0], prng) - plot_image_and_patch(axes[1], prng) - plot_bar_graphs(axes[2], prng) - plot_colored_circles(axes[3], prng) - plot_colored_sinusoidal_lines(axes[4]) - plot_histograms(axes[5], prng) - - fig.tight_layout() - - return fig - - -if __name__ == "__main__": - - # Setup a list of all available styles, in alphabetical order but - # the `default` and `classic` ones, which will be forced resp. in - # first and second position. - style_list = list(plt.style.available) # *new* list: avoids side effects. - style_list.remove('classic') # `classic` is in the list: first remove it. - style_list.sort() - style_list.insert(0, u'default') - style_list.insert(1, u'classic') - - # Plot a demonstration figure for every available style sheet. - for style_label in style_list: - with plt.style.context(style_label): - fig = plot_figure(style_label=style_label) - - plt.show() diff --git a/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py b/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py deleted file mode 100644 index 2318f1678671..000000000000 --- a/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Demo of how to display two scales on the left and right y axis. - -This example uses the Fahrenheit and Celsius scales. -""" -import matplotlib.pyplot as plt -import numpy as np - - -def fahrenheit2celsius(temp): - """ - Returns temperature in Celsius. - """ - return (5. / 9.) * (temp - 32) - - -def convert_ax_c_to_celsius(ax_f): - """ - Update second axis according with first axis. - """ - y1, y2 = ax_f.get_ylim() - ax_c.set_ylim(fahrenheit2celsius(y1), fahrenheit2celsius(y2)) - ax_c.figure.canvas.draw() - -fig, ax_f = plt.subplots() -ax_c = ax_f.twinx() - -# automatically update ylim of ax2 when ylim of ax1 changes. -ax_f.callbacks.connect("ylim_changed", convert_ax_c_to_celsius) -ax_f.plot(np.linspace(-40, 120, 100)) -ax_f.set_xlim(0, 100) - -ax_f.set_title('Two scales: Fahrenheit and Celsius') -ax_f.set_ylabel('Fahrenheit') -ax_c.set_ylabel('Celsius') - -plt.show() diff --git a/examples/subplots_axes_and_figures/subplot_demo.py b/examples/subplots_axes_and_figures/subplot_demo.py deleted file mode 100644 index e00d7cb6f24b..000000000000 --- a/examples/subplots_axes_and_figures/subplot_demo.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Simple demo with multiple subplots. -""" -import numpy as np -import matplotlib.pyplot as plt - - -x1 = np.linspace(0.0, 5.0) -x2 = np.linspace(0.0, 2.0) - -y1 = np.cos(2 * np.pi * x1) * np.exp(-x1) -y2 = np.cos(2 * np.pi * x2) - -plt.subplot(2, 1, 1) -plt.plot(x1, y1, 'o-') -plt.title('A tale of 2 subplots') -plt.ylabel('Damped oscillation') - -plt.subplot(2, 1, 2) -plt.plot(x2, y2, '.-') -plt.xlabel('time (s)') -plt.ylabel('Undamped') - -plt.show() diff --git a/examples/tests/backend_driver.py b/examples/tests/backend_driver.py deleted file mode 100755 index 84fe6b1809fd..000000000000 --- a/examples/tests/backend_driver.py +++ /dev/null @@ -1,529 +0,0 @@ -from __future__ import print_function, division -""" -This is used to drive many of the examples across the backends, for -regression testing, and comparing backend efficiency. - -You can specify the backends to be tested either via the --backends -switch, which takes a comma-separated list, or as separate arguments, -e.g. - - python backend_driver.py agg ps - -would test the agg and ps backends. If no arguments are given, a -default list of backends will be tested. - -Interspersed with the backend arguments can be switches for the Python -interpreter executing the tests. If entering such arguments causes an -option parsing error with the driver script, separate them from driver -switches with a --. -""" - -import os -import time -import sys -import glob -from optparse import OptionParser - -import matplotlib.rcsetup as rcsetup -from matplotlib.cbook import Bunch, dedent - - -all_backends = list(rcsetup.all_backends) # to leave the original list alone - -# actual physical directory for each dir -dirs = dict(files=os.path.join('..', 'lines_bars_and_markers'), - shapes=os.path.join('..', 'shapes_and_collections'), - images=os.path.join('..', 'images_contours_and_fields'), - pie=os.path.join('..', 'pie_and_polar_charts'), - text=os.path.join('..', 'text_labels_and_annotations'), - ticks=os.path.join('..', 'ticks_and_spines'), - subplots=os.path.join('..', 'subplots_axes_and_figures'), - specialty=os.path.join('..', 'specialty_plots'), - showcase=os.path.join('..', 'showcase'), - pylab=os.path.join('..', 'pylab_examples'), - api=os.path.join('..', 'api'), - units=os.path.join('..', 'units'), - mplot3d=os.path.join('..', 'mplot3d'), - colors=os.path.join('..', 'color')) - - -# files in each dir -files = dict() - -files['lines'] = [ - 'barh_demo.py', - 'fill_demo.py', - 'fill_demo_features.py', - 'line_demo_dash_control.py', - 'line_styles_reference.py', - 'scatter_with_legend.py' - ] - -files['shapes'] = [ - 'path_patch_demo.py', - 'scatter_demo.py', - ] - -files['colors'] = [ - 'color_cycle_default.py', - 'color_cycle_demo.py', - ] - -files['images'] = [ - 'image_demo.py', - 'contourf_log.py', - ] - -files['statistics'] = [ - 'errorbar_demo.py', - 'errorbar_demo_features.py', - 'histogram_demo_cumulative.py', - 'histogram_demo_features.py', - 'histogram_demo_histtypes.py', - 'histogram_demo_multihist.py', - ] - -files['pie'] = [ - 'pie_demo.py', - 'polar_bar_demo.py', - 'polar_scatter_demo.py', - ] - -files['text_labels_and_annotations'] = [ - 'text_demo_fontdict.py', - 'unicode_demo.py', - ] - -files['ticks_and_spines'] = [ - 'spines_demo_bounds.py', - 'ticklabels_demo_rotation.py', - ] - -files['subplots_axes_and_figures'] = [ - 'subplot_demo.py', - ] - -files['showcase'] = [ - 'integral_demo.py', - ] - -files['pylab'] = [ - 'accented_text.py', - 'alignment_test.py', - 'annotation_demo.py', - 'annotation_demo.py', - 'annotation_demo2.py', - 'annotation_demo2.py', - 'anscombe.py', - 'arctest.py', - 'arrow_demo.py', - 'axes_demo.py', - 'axes_props.py', - 'axhspan_demo.py', - 'axis_equal_demo.py', - 'bar_stacked.py', - 'barb_demo.py', - 'barchart_demo.py', - 'barcode_demo.py', - 'boxplot_demo.py', - 'broken_barh.py', - 'cohere_demo.py', - 'color_by_yvalue.py', - 'color_demo.py', - 'colorbar_tick_labelling_demo.py', - 'contour_demo.py', - 'contour_image.py', - 'contour_label_demo.py', - 'contourf_demo.py', - 'coords_demo.py', - 'coords_report.py', - 'csd_demo.py', - 'cursor_demo.py', - 'custom_cmap.py', - 'custom_figure_class.py', - 'custom_ticker1.py', - 'customize_rc.py', - 'dashpointlabel.py', - 'date_demo1.py', - 'date_demo2.py', - 'date_demo_convert.py', - 'date_demo_rrule.py', - 'date_index_formatter.py', - 'dolphin.py', - 'ellipse_collection.py', - 'ellipse_demo.py', - 'ellipse_rotated.py', - 'equal_aspect_ratio.py', - 'errorbar_limits.py', - 'fancyarrow_demo.py', - 'fancybox_demo.py', - 'fancybox_demo2.py', - 'fancytextbox_demo.py', - 'figimage_demo.py', - 'figlegend_demo.py', - 'figure_title.py', - 'fill_between_demo.py', - 'fill_spiral.py', - 'finance_demo.py', - 'findobj_demo.py', - 'fonts_demo.py', - 'fonts_demo_kw.py', - 'ganged_plots.py', - 'geo_demo.py', - 'gradient_bar.py', - 'griddata_demo.py', - 'hatch_demo.py', - 'hexbin_demo.py', - 'hexbin_demo2.py', - 'hist_colormapped.py', - 'vline_hline_demo.py', - - 'image_clip_path.py', - 'image_demo.py', - 'image_demo2.py', - 'image_interp.py', - 'image_masked.py', - 'image_nonuniform.py', - 'image_origin.py', - 'image_slices_viewer.py', - 'interp_demo.py', - 'invert_axes.py', - 'layer_images.py', - 'legend_demo2.py', - 'legend_demo3.py', - 'line_collection.py', - 'line_collection2.py', - 'log_bar.py', - 'log_demo.py', - 'log_test.py', - 'major_minor_demo1.py', - 'major_minor_demo2.py', - 'manual_axis.py', - 'masked_demo.py', - 'mathtext_demo.py', - 'mathtext_examples.py', - 'matshow.py', - 'mri_demo.py', - 'mri_with_eeg.py', - 'multi_image.py', - 'multiline.py', - 'multiple_figs_demo.py', - 'nan_test.py', - 'newscalarformatter_demo.py', - 'pcolor_demo.py', - 'pcolor_log.py', - 'pcolor_small.py', - 'pie_demo2.py', - 'plotfile_demo.py', - 'polar_demo.py', - 'polar_legend.py', - 'psd_demo.py', - 'psd_demo2.py', - 'psd_demo3.py', - 'quadmesh_demo.py', - 'quiver_demo.py', - 'scatter_custom_symbol.py', - 'scatter_demo2.py', - 'scatter_masked.py', - 'scatter_profile.py', - 'scatter_star_poly.py', - #'set_and_get.py', - 'shared_axis_across_figures.py', - 'shared_axis_demo.py', - 'simple_plot.py', - 'specgram_demo.py', - 'spine_placement_demo.py', - 'spy_demos.py', - 'stem_plot.py', - 'step_demo.py', - 'stix_fonts_demo.py', - 'stock_demo.py', - 'subplots_adjust.py', - 'symlog_demo.py', - 'table_demo.py', - 'text_handles.py', - 'text_rotation.py', - 'text_rotation_relative_to_line.py', - 'transoffset.py', - 'xcorr_demo.py', - 'zorder_demo.py', - ] - - -files['api'] = [ - 'agg_oo.py', - 'barchart_demo.py', - 'bbox_intersect.py', - 'collections_demo.py', - 'colorbar_only.py', - 'custom_projection_example.py', - 'custom_scale_example.py', - 'date_demo.py', - 'date_index_formatter.py', - 'donut_demo.py', - 'font_family_rc.py', - 'image_zcoord.py', - 'joinstyle.py', - 'legend_demo.py', - 'line_with_text.py', - 'logo2.py', - 'mathtext_asarray.py', - 'patch_collection.py', - 'quad_bezier.py', - 'scatter_piecharts.py', - 'span_regions.py', - 'two_scales.py', - 'unicode_minus.py', - 'watermark_image.py', - 'watermark_text.py', -] - -files['units'] = [ - 'annotate_with_units.py', - #'artist_tests.py', # broken, fixme - 'bar_demo2.py', - #'bar_unit_demo.py', # broken, fixme - #'ellipse_with_units.py', # broken, fixme - 'radian_demo.py', - 'units_sample.py', - #'units_scatter.py', # broken, fixme - - ] - -files['mplot3d'] = [ - '2dcollections3d_demo.py', - 'bars3d_demo.py', - 'contour3d_demo.py', - 'contour3d_demo2.py', - 'contourf3d_demo.py', - 'lines3d_demo.py', - 'polys3d_demo.py', - 'scatter3d_demo.py', - 'surface3d_demo.py', - 'surface3d_demo2.py', - 'text3d_demo.py', - 'wire3d_demo.py', - ] - -# dict from dir to files we know we don't want to test (e.g., examples -# not using pyplot, examples requiring user input, animation examples, -# examples that may only work in certain environs (usetex examples?), -# examples that generate multiple figures - -excluded = { - 'pylab': ['__init__.py', 'toggle_images.py', ], - 'units': ['__init__.py', 'date_support.py', ], -} - - -def report_missing(dir, flist): - 'report the py files in dir that are not in flist' - globstr = os.path.join(dir, '*.py') - fnames = glob.glob(globstr) - - pyfiles = set([os.path.split(fullpath)[-1] for fullpath in set(fnames)]) - - exclude = set(excluded.get(dir, [])) - flist = set(flist) - missing = list(pyfiles - flist - exclude) - missing.sort() - if missing: - print('%s files not tested: %s' % (dir, ', '.join(missing))) - - -def report_all_missing(directories): - for f in directories: - report_missing(dirs[f], files[f]) - - -# tests known to fail on a given backend - -failbackend = dict( - svg=('tex_demo.py', ), - agg=('hyperlinks.py', ), - pdf=('hyperlinks.py', ), - ps=('hyperlinks.py', ), - ) - - -try: - import subprocess - - def run(arglist): - try: - ret = subprocess.call(arglist) - except KeyboardInterrupt: - sys.exit() - else: - return ret -except ImportError: - def run(arglist): - os.system(' '.join(arglist)) - - -def drive(backend, directories, python=['python'], switches=[]): - exclude = failbackend.get(backend, []) - - # Clear the destination directory for the examples - path = backend - if os.path.exists(path): - import glob - for fname in os.listdir(path): - os.unlink(os.path.join(path, fname)) - else: - os.mkdir(backend) - failures = [] - - testcases = [os.path.join(dirs[d], fname) - for d in directories - for fname in files[d]] - - for fullpath in testcases: - print('\tdriving %-40s' % (fullpath)) - sys.stdout.flush() - fpath, fname = os.path.split(fullpath) - - if fname in exclude: - print('\tSkipping %s, known to fail on backend: %s' % backend) - continue - - basename, ext = os.path.splitext(fname) - outfile = os.path.join(path, basename) - tmpfile_name = '_tmp_%s.py' % basename - tmpfile = open(tmpfile_name, 'w') - - future_imports = 'from __future__ import division, print_function' - for line in open(fullpath): - line_lstrip = line.lstrip() - if line_lstrip.startswith("#"): - tmpfile.write(line) - elif 'unicode_literals' in line: - future_imports = future_imports + ', unicode_literals' - - tmpfile.writelines(( - future_imports + '\n', - 'import sys\n', - 'sys.path.append("%s")\n' % fpath.replace('\\', '\\\\'), - 'import matplotlib\n', - 'matplotlib.use("%s")\n' % backend, - 'from pylab import savefig\n', - 'import numpy\n', - 'numpy.seterr(invalid="ignore")\n', - )) - for line in open(fullpath): - line_lstrip = line.lstrip() - if (line_lstrip.startswith('from __future__ import') or - line_lstrip.startswith('matplotlib.use') or - line_lstrip.startswith('savefig') or - line_lstrip.startswith('show')): - continue - tmpfile.write(line) - if backend in rcsetup.interactive_bk: - tmpfile.write('show()') - else: - tmpfile.write('\nsavefig(r"%s", dpi=150)' % outfile) - - tmpfile.close() - start_time = time.time() - program = [x % {'name': basename} for x in python] - ret = run(program + [tmpfile_name] + switches) - end_time = time.time() - print("%s %s" % ((end_time - start_time), ret)) - #os.system('%s %s %s' % (python, tmpfile_name, ' '.join(switches))) - os.remove(tmpfile_name) - if ret: - failures.append(fullpath) - return failures - - -def parse_options(): - doc = (__doc__ and __doc__.split('\n\n')) or " " - op = OptionParser(description=doc[0].strip(), - usage='%prog [options] [--] [backends and switches]', - #epilog='\n'.join(doc[1:]) # epilog not supported on my python2.4 machine: JDH - ) - op.disable_interspersed_args() - op.set_defaults(dirs='pylab,api,units,mplot3d', - clean=False, coverage=False, valgrind=False) - op.add_option('-d', '--dirs', '--directories', type='string', - dest='dirs', help=dedent(''' - Run only the tests in these directories; comma-separated list of - one or more of: pylab (or pylab_examples), api, units, mplot3d''')) - op.add_option('-b', '--backends', type='string', dest='backends', - help=dedent(''' - Run tests only for these backends; comma-separated list of - one or more of: agg, ps, svg, pdf, template, cairo, - Default is everything except cairo.''')) - op.add_option('--clean', action='store_true', dest='clean', - help='Remove result directories, run no tests') - op.add_option('-c', '--coverage', action='store_true', dest='coverage', - help='Run in coverage.py') - op.add_option('-v', '--valgrind', action='store_true', dest='valgrind', - help='Run in valgrind') - - options, args = op.parse_args() - switches = [x for x in args if x.startswith('--')] - backends = [x.lower() for x in args if not x.startswith('--')] - if options.backends: - backends += [be.lower() for be in options.backends.split(',')] - - result = Bunch( - dirs=options.dirs.split(','), - backends=backends or ['agg', 'ps', 'svg', 'pdf', 'template'], - clean=options.clean, - coverage=options.coverage, - valgrind=options.valgrind, - switches=switches) - if 'pylab_examples' in result.dirs: - result.dirs[result.dirs.index('pylab_examples')] = 'pylab' - #print(result) - return (result) - -if __name__ == '__main__': - times = {} - failures = {} - options = parse_options() - - if options.clean: - localdirs = [d for d in glob.glob('*') if os.path.isdir(d)] - all_backends_set = set(all_backends) - for d in localdirs: - if d.lower() not in all_backends_set: - continue - print('removing %s' % d) - for fname in glob.glob(os.path.join(d, '*')): - os.remove(fname) - os.rmdir(d) - for fname in glob.glob('_tmp*.py'): - os.remove(fname) - - print('all clean...') - raise SystemExit - if options.coverage: - python = ['coverage.py', '-x'] - elif options.valgrind: - python = ['valgrind', '--tool=memcheck', '--leak-check=yes', - '--log-file=%(name)s', sys.executable] - elif sys.platform == 'win32': - python = [sys.executable] - else: - python = [sys.executable] - - report_all_missing(options.dirs) - for backend in options.backends: - print('testing %s %s' % (backend, ' '.join(options.switches))) - t0 = time.time() - failures[backend] = \ - drive(backend, options.dirs, python, options.switches) - t1 = time.time() - times[backend] = (t1 - t0)/60.0 - - #print(times) - for backend, elapsed in times.items(): - print('Backend %s took %1.2f minutes to complete' % (backend, elapsed)) - failed = failures[backend] - if failed: - print(' Failures: %s' % failed) - if 'template' in times: - print('\ttemplate ratio %1.3f, template residual %1.3f' % ( - elapsed/times['template'], elapsed - times['template'])) diff --git a/examples/text_labels_and_annotations/autowrap_demo.py b/examples/text_labels_and_annotations/autowrap_demo.py deleted file mode 100644 index d56e18f3f86f..000000000000 --- a/examples/text_labels_and_annotations/autowrap_demo.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Auto-wrapping text demo. -""" -import matplotlib.pyplot as plt - -fig = plt.figure() -plt.axis([0, 10, 0, 10]) -t = "This is a really long string that I'd rather have wrapped so that it"\ - " doesn't go outside of the figure, but if it's long enough it will go"\ - " off the top or bottom!" -plt.text(4, 1, t, ha='left', rotation=15, wrap=True) -plt.text(6, 5, t, ha='left', rotation=15, wrap=True) -plt.text(5, 5, t, ha='right', rotation=-15, wrap=True) -plt.text(5, 10, t, fontsize=18, style='oblique', ha='center', - va='top', wrap=True) -plt.text(3, 4, t, family='serif', style='italic', ha='right', wrap=True) -plt.text(-1, 0, t, ha='left', rotation=-15, wrap=True) - -plt.show() diff --git a/examples/text_labels_and_annotations/rainbow_text.py b/examples/text_labels_and_annotations/rainbow_text.py deleted file mode 100644 index 95142175ce3d..000000000000 --- a/examples/text_labels_and_annotations/rainbow_text.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -""" -The example shows how to string together several text objects. - -HISTORY -------- -On the matplotlib-users list back in February 2012, Gökhan Sever asked the -following question: - - Is there a way in matplotlib to partially specify the color of a string? - - Example: - - plt.ylabel("Today is cloudy.") - How can I show "today" as red, "is" as green and "cloudy." as blue? - - Thanks. - -Paul Ivanov responded with this answer: -""" -import matplotlib.pyplot as plt -from matplotlib import transforms - - -def rainbow_text(x, y, strings, colors, ax=None, **kw): - """ - Take a list of ``strings`` and ``colors`` and place them next to each - other, with text strings[i] being shown in colors[i]. - - This example shows how to do both vertical and horizontal text, and will - pass all keyword arguments to plt.text, so you can set the font size, - family, etc. - - The text will get added to the ``ax`` axes, if provided, otherwise the - currently active axes will be used. - """ - if ax is None: - ax = plt.gca() - t = ax.transData - canvas = ax.figure.canvas - - # horizontal version - for s, c in zip(strings, colors): - text = ax.text(x, y, s + " ", color=c, transform=t, **kw) - text.draw(canvas.get_renderer()) - ex = text.get_window_extent() - t = transforms.offset_copy(text._transform, x=ex.width, units='dots') - - # vertical version - for s, c in zip(strings, colors): - text = ax.text(x, y, s + " ", color=c, transform=t, - rotation=90, va='bottom', ha='center', **kw) - text.draw(canvas.get_renderer()) - ex = text.get_window_extent() - t = transforms.offset_copy(text._transform, y=ex.height, units='dots') - - -rainbow_text(0, 0, "all unicorns poop rainbows ! ! !".split(), - ['red', 'cyan', 'brown', 'green', 'blue', 'purple', 'black'], - size=16) - -plt.show() diff --git a/examples/text_labels_and_annotations/text_demo_fontdict.py b/examples/text_labels_and_annotations/text_demo_fontdict.py deleted file mode 100644 index ae35d222b8d3..000000000000 --- a/examples/text_labels_and_annotations/text_demo_fontdict.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Demo using fontdict to control style of text and labels. -""" -import numpy as np -import matplotlib.pyplot as plt - - -font = {'family': 'serif', - 'color': 'darkred', - 'weight': 'normal', - 'size': 16, - } - -x = np.linspace(0.0, 5.0, 100) -y = np.cos(2*np.pi*x) * np.exp(-x) - -plt.plot(x, y, 'k') -plt.title('Damped exponential decay', fontdict=font) -plt.text(2, 0.65, r'$\cos(2 \pi t) \exp(-t)$', fontdict=font) -plt.xlabel('time (s)', fontdict=font) -plt.ylabel('voltage (mV)', fontdict=font) - -# Tweak spacing to prevent clipping of ylabel -plt.subplots_adjust(left=0.15) -plt.show() diff --git a/examples/text_labels_and_annotations/unicode_demo.py b/examples/text_labels_and_annotations/unicode_demo.py deleted file mode 100644 index 5fc39bea0f4c..000000000000 --- a/examples/text_labels_and_annotations/unicode_demo.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Demo of unicode support in text and labels. -""" -from __future__ import unicode_literals - -import matplotlib.pyplot as plt - - -plt.title('Développés et fabriqués') -plt.xlabel("réactivité nous permettent d'être sélectionnés et adoptés") -plt.ylabel('André was here!') -plt.text(0.2, 0.8, 'Institut für Festkörperphysik', rotation=45) -plt.text(0.4, 0.2, 'AVA (check kerning)') - -plt.show() diff --git a/examples/ticks_and_spines/spines_demo.py b/examples/ticks_and_spines/spines_demo.py deleted file mode 100644 index 0b5e450ed3b7..000000000000 --- a/examples/ticks_and_spines/spines_demo.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Basic demo of axis spines. - -This demo compares a normal axes, with spines on all four sides, and an axes -with spines only on the left and bottom. -""" -import numpy as np -import matplotlib.pyplot as plt - - -x = np.linspace(0, 2 * np.pi, 100) -y = 2 * np.sin(x) - -fig, (ax0, ax1) = plt.subplots(nrows=2) - -ax0.plot(x, y) -ax0.set_title('normal spines') - -ax1.plot(x, y) -ax1.set_title('bottom-left spines') - -# Hide the right and top spines -ax1.spines['right'].set_visible(False) -ax1.spines['top'].set_visible(False) -# Only show ticks on the left and bottom spines -ax1.yaxis.set_ticks_position('left') -ax1.xaxis.set_ticks_position('bottom') - -# Tweak spacing between subplots to prevent labels from overlapping -plt.subplots_adjust(hspace=0.5) - -plt.show() diff --git a/examples/ticks_and_spines/spines_demo_bounds.py b/examples/ticks_and_spines/spines_demo_bounds.py deleted file mode 100644 index 4828e72a0af6..000000000000 --- a/examples/ticks_and_spines/spines_demo_bounds.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Demo of spines using custom bounds to limit the extent of the spine. -""" -import numpy as np -import matplotlib.pyplot as plt - - -x = np.linspace(0, 2*np.pi, 50) -y = np.sin(x) -y2 = y + 0.1 * np.random.normal(size=x.shape) - -fig, ax = plt.subplots() -ax.plot(x, y, 'k--') -ax.plot(x, y2, 'ro') - -# set ticks and tick labels -ax.set_xlim((0, 2*np.pi)) -ax.set_xticks([0, np.pi, 2*np.pi]) -ax.set_xticklabels(['0', '$\pi$', '2$\pi$']) -ax.set_ylim((-1.5, 1.5)) -ax.set_yticks([-1, 0, 1]) - -# Only draw spine between the y-ticks -ax.spines['left'].set_bounds(-1, 1) -# Hide the right and top spines -ax.spines['right'].set_visible(False) -ax.spines['top'].set_visible(False) -# Only show ticks on the left and bottom spines -ax.yaxis.set_ticks_position('left') -ax.xaxis.set_ticks_position('bottom') - -plt.show() diff --git a/examples/ticks_and_spines/spines_demo_dropped.py b/examples/ticks_and_spines/spines_demo_dropped.py deleted file mode 100644 index 1a11e8f58f59..000000000000 --- a/examples/ticks_and_spines/spines_demo_dropped.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Demo of spines offset from the axes (a.k.a. "dropped spines"). -""" -import numpy as np -import matplotlib.pyplot as plt - - -fig, ax = plt.subplots() - -image = np.random.uniform(size=(10, 10)) -ax.imshow(image, cmap=plt.cm.gray, interpolation='nearest') -ax.set_title('dropped spines') - -# Move left and bottom spines outward by 10 points -ax.spines['left'].set_position(('outward', 10)) -ax.spines['bottom'].set_position(('outward', 10)) -# Hide the right and top spines -ax.spines['right'].set_visible(False) -ax.spines['top'].set_visible(False) -# Only show ticks on the left and bottom spines -ax.yaxis.set_ticks_position('left') -ax.xaxis.set_ticks_position('bottom') - -plt.show() diff --git a/examples/ticks_and_spines/tick-formatters.py b/examples/ticks_and_spines/tick-formatters.py deleted file mode 100644 index e4278d41c631..000000000000 --- a/examples/ticks_and_spines/tick-formatters.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Show the different tick formatters -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.ticker as ticker - - -# Setup a plot such that only the bottom spine is shown -def setup(ax): - ax.spines['right'].set_color('none') - ax.spines['left'].set_color('none') - ax.yaxis.set_major_locator(ticker.NullLocator()) - ax.spines['top'].set_color('none') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(which='major', width=1.00, length=5) - ax.tick_params(which='minor', width=0.75, length=2.5, labelsize=10) - ax.set_xlim(0, 5) - ax.set_ylim(0, 1) - ax.patch.set_alpha(0.0) - - -plt.figure(figsize=(8, 6)) -n = 6 - -# Null formatter -ax = plt.subplot(n, 1, 1) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) -ax.xaxis.set_major_formatter(ticker.NullFormatter()) -ax.xaxis.set_minor_formatter(ticker.NullFormatter()) -ax.text(0.0, 0.5, "NullFormatter()", fontsize=16, transform=ax.transAxes) - -# Fixed formatter -ax = plt.subplot(n, 1, 2) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(1.0)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) -majors = ["", "0", "1", "2", "3", "4", "5"] -ax.xaxis.set_major_formatter(ticker.FixedFormatter(majors)) -minors = [""] + ["%.2f" % (x-int(x)) if (x-int(x)) - else "" for x in np.arange(0, 5, 0.25)] -ax.xaxis.set_minor_formatter(ticker.FixedFormatter(minors)) -ax.text(0.0, 0.5, "FixedFormatter(['', '0', '1', ...])", - fontsize=15, transform=ax.transAxes) - - -# Func formatter -def major_formatter(x, pos): - return "[%.2f]" % x - - -ax = plt.subplot(n, 1, 3) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) -ax.xaxis.set_major_formatter(ticker.FuncFormatter(major_formatter)) -ax.text(0.0, 0.5, 'FuncFormatter(lambda x, pos: "[%.2f]" % x)', - fontsize=15, transform=ax.transAxes) - - -# FormatStr formatter -ax = plt.subplot(n, 1, 4) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) -ax.xaxis.set_major_formatter(ticker.FormatStrFormatter(">%d<")) -ax.text(0.0, 0.5, "FormatStrFormatter('>%d<')", - fontsize=15, transform=ax.transAxes) - -# Scalar formatter -ax = plt.subplot(n, 1, 5) -setup(ax) -ax.xaxis.set_major_locator(ticker.AutoLocator()) -ax.xaxis.set_minor_locator(ticker.AutoMinorLocator()) -ax.xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True)) -ax.text(0.0, 0.5, "ScalarFormatter()", fontsize=15, transform=ax.transAxes) - -# StrMethod formatter -ax = plt.subplot(n, 1, 6) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25)) -ax.xaxis.set_major_formatter(ticker.StrMethodFormatter("{x}")) -ax.text(0.0, 0.5, "StrMethodFormatter('{x}')", - fontsize=15, transform=ax.transAxes) - -plt.show() diff --git a/examples/ticks_and_spines/tick-locators.py b/examples/ticks_and_spines/tick-locators.py deleted file mode 100644 index 2add8a29a543..000000000000 --- a/examples/ticks_and_spines/tick-locators.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Show the different tick locators -""" - -import numpy as np -import matplotlib.pyplot as plt -import matplotlib.ticker as ticker - - -# Setup a plot such that only the bottom spine is shown -def setup(ax): - ax.spines['right'].set_color('none') - ax.spines['left'].set_color('none') - ax.yaxis.set_major_locator(ticker.NullLocator()) - ax.spines['top'].set_color('none') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(which='major', width=1.00) - ax.tick_params(which='major', length=5) - ax.tick_params(which='minor', width=0.75) - ax.tick_params(which='minor', length=2.5) - ax.set_xlim(0, 5) - ax.set_ylim(0, 1) - ax.patch.set_alpha(0.0) - - -plt.figure(figsize=(8, 8)) -n = 8 - -# Null Locator -ax = plt.subplot(n, 1, 1) -setup(ax) -ax.xaxis.set_major_locator(ticker.NullLocator()) -ax.xaxis.set_minor_locator(ticker.NullLocator()) -ax.text(0.0, 0.5, "NullLocator()", fontsize=14, transform=ax.transAxes) - -# Multiple Locator -ax = plt.subplot(n, 1, 2) -setup(ax) -ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5)) -ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.1)) -ax.text(0.0, 0.5, "MultipleLocator(0.5)", fontsize=14, - transform=ax.transAxes) - -# Fixed Locator -ax = plt.subplot(n, 1, 3) -setup(ax) -majors = [0, 1, 5] -ax.xaxis.set_major_locator(ticker.FixedLocator(majors)) -minors = np.linspace(0, 1, 11)[1:-1] -ax.xaxis.set_minor_locator(ticker.FixedLocator(minors)) -ax.text(0.0, 0.5, "FixedLocator([0, 1, 5])", fontsize=14, - transform=ax.transAxes) - -# Linear Locator -ax = plt.subplot(n, 1, 4) -setup(ax) -ax.xaxis.set_major_locator(ticker.LinearLocator(3)) -ax.xaxis.set_minor_locator(ticker.LinearLocator(31)) -ax.text(0.0, 0.5, "LinearLocator(numticks=3)", - fontsize=14, transform=ax.transAxes) - -# Index Locator -ax = plt.subplot(n, 1, 5) -setup(ax) -ax.plot(range(0, 5), [0]*5, color='White') -ax.xaxis.set_major_locator(ticker.IndexLocator(base=.5, offset=.25)) -ax.text(0.0, 0.5, "IndexLocator(base=0.5, offset=0.25)", - fontsize=14, transform=ax.transAxes) - -# Auto Locator -ax = plt.subplot(n, 1, 6) -setup(ax) -ax.xaxis.set_major_locator(ticker.AutoLocator()) -ax.xaxis.set_minor_locator(ticker.AutoMinorLocator()) -ax.text(0.0, 0.5, "AutoLocator()", fontsize=14, transform=ax.transAxes) - -# MaxN Locator -ax = plt.subplot(n, 1, 7) -setup(ax) -ax.xaxis.set_major_locator(ticker.MaxNLocator(4)) -ax.xaxis.set_minor_locator(ticker.MaxNLocator(40)) -ax.text(0.0, 0.5, "MaxNLocator(n=4)", fontsize=14, transform=ax.transAxes) - -# Log Locator -ax = plt.subplot(n, 1, 8) -setup(ax) -ax.set_xlim(10**3, 10**10) -ax.set_xscale('log') -ax.xaxis.set_major_locator(ticker.LogLocator(base=10.0, numticks=15)) -ax.text(0.0, 0.5, "LogLocator(base=10, numticks=15)", - fontsize=15, transform=ax.transAxes) - -plt.show() diff --git a/examples/ticks_and_spines/tick_labels_from_values.py b/examples/ticks_and_spines/tick_labels_from_values.py deleted file mode 100644 index 2073442cc6b5..000000000000 --- a/examples/ticks_and_spines/tick_labels_from_values.py +++ /dev/null @@ -1,34 +0,0 @@ -""" - -Basic demo showing how to set tick labels to values of a series. - -Using ax.set_xticks causes the tick labels to be set on the currently -chosen ticks. However, you may want to allow matplotlib to dynamically -choose the number of ticks and their spacing. - -In this case it may be better to determine the tick label from the -value at the tick. The following example shows how to do this. - -NB: The MaxNLocator is used here to ensure that the tick values -take integer values. - -""" - -import matplotlib.pyplot as plt -from matplotlib.ticker import FuncFormatter, MaxNLocator -fig = plt.figure() -ax = fig.add_subplot(111) -xs = range(26) -ys = range(26) -labels = list('abcdefghijklmnopqrstuvwxyz') - - -def format_fn(tick_val, tick_pos): - if int(tick_val) in xs: - return labels[int(tick_val)] - else: - return '' -ax.xaxis.set_major_formatter(FuncFormatter(format_fn)) -ax.xaxis.set_major_locator(MaxNLocator(integer=True)) -ax.plot(xs, ys) -plt.show() diff --git a/examples/ticks_and_spines/ticklabels_demo_rotation.py b/examples/ticks_and_spines/ticklabels_demo_rotation.py deleted file mode 100644 index 9c1c49b2e4d6..000000000000 --- a/examples/ticks_and_spines/ticklabels_demo_rotation.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Demo of custom tick-labels with user-defined rotation. -""" -import matplotlib.pyplot as plt - - -x = [1, 2, 3, 4] -y = [1, 4, 9, 6] -labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs'] - -plt.plot(x, y, 'ro') -# You can specify a rotation for the tick labels in degrees or with keywords. -plt.xticks(x, labels, rotation='vertical') -# Pad margins so that markers don't get clipped by the axes -plt.margins(0.2) -# Tweak spacing to prevent clipping of tick-labels -plt.subplots_adjust(bottom=0.15) -plt.show() diff --git a/examples/units/annotate_with_units.py b/examples/units/annotate_with_units.py deleted file mode 100644 index b322e2dd2c89..000000000000 --- a/examples/units/annotate_with_units.py +++ /dev/null @@ -1,23 +0,0 @@ -import matplotlib.pyplot as plt -from basic_units import cm - -fig, ax = plt.subplots() - -ax.annotate("Note 01", [0.5*cm, 0.5*cm]) - -# xy and text both unitized -ax.annotate('local max', xy=(3*cm, 1*cm), xycoords='data', - xytext=(0.8*cm, 0.95*cm), textcoords='data', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='right', verticalalignment='top') - -# mixing units w/ nonunits -ax.annotate('local max', xy=(3*cm, 1*cm), xycoords='data', - xytext=(0.8, 0.95), textcoords='axes fraction', - arrowprops=dict(facecolor='black', shrink=0.05), - horizontalalignment='right', verticalalignment='top') - - -ax.set_xlim(0*cm, 4*cm) -ax.set_ylim(0*cm, 4*cm) -plt.show() diff --git a/examples/units/artist_tests.py b/examples/units/artist_tests.py deleted file mode 100644 index 09cc8caa1d2f..000000000000 --- a/examples/units/artist_tests.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Test unit support with each of the matplotlib primitive artist types - -The axes handles unit conversions and the artists keep a pointer to -their axes parent, so you must init the artists with the axes instance -if you want to initialize them with unit data, or else they will not -know how to convert the units to scalars -""" -import random -import matplotlib.lines as lines -import matplotlib.patches as patches -import matplotlib.text as text -import matplotlib.collections as collections - -from basic_units import cm, inch -import numpy as np -import matplotlib.pyplot as plt - -fig, ax = plt.subplots() -ax.xaxis.set_units(cm) -ax.yaxis.set_units(cm) - -if 0: - # test a line collection - # Not supported at present. - verts = [] - for i in range(10): - # a random line segment in inches - verts.append(zip(*inch*10*np.random.rand(2, random.randint(2, 15)))) - lc = collections.LineCollection(verts, axes=ax) - ax.add_collection(lc) - -# test a plain-ol-line -line = lines.Line2D([0*cm, 1.5*cm], [0*cm, 2.5*cm], lw=2, color='black', axes=ax) -ax.add_line(line) - -if 0: - # test a patch - # Not supported at present. - rect = patches.Rectangle((1*cm, 1*cm), width=5*cm, height=2*cm, alpha=0.2, axes=ax) - ax.add_patch(rect) - - -t = text.Text(3*cm, 2.5*cm, 'text label', ha='left', va='bottom', axes=ax) -ax.add_artist(t) - -ax.set_xlim(-1*cm, 10*cm) -ax.set_ylim(-1*cm, 10*cm) -#ax.xaxis.set_units(inch) -ax.grid(True) -ax.set_title("Artists with units") -plt.show() diff --git a/examples/units/bar_demo2.py b/examples/units/bar_demo2.py deleted file mode 100644 index e7fb8bdde07c..000000000000 --- a/examples/units/bar_demo2.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -plot using a variety of cm vs inches conversions. The example shows -how default unit instrospection works (ax1), how various keywords can -be used to set the x and y units to override the defaults (ax2, ax3, -ax4) and how one can set the xlimits using scalars (ax3, current units -assumed) or units (conversions applied to get the numbers to current -units) - -""" -import numpy as np -from basic_units import cm, inch -import matplotlib.pyplot as plt - - -cms = cm * np.arange(0, 10, 2) -bottom = 0*cm -width = 0.8*cm - -fig = plt.figure() - -ax1 = fig.add_subplot(2, 2, 1) -ax1.bar(cms, cms, bottom=bottom) - -ax2 = fig.add_subplot(2, 2, 2) -ax2.bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch) - -ax3 = fig.add_subplot(2, 2, 3) -ax3.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm) -ax3.set_xlim(2, 6) # scalars are interpreted in current units - -ax4 = fig.add_subplot(2, 2, 4) -ax4.bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch) -#fig.savefig('simple_conversion_plot.png') -ax4.set_xlim(2*cm, 6*cm) # cm are converted to inches - -plt.show() diff --git a/examples/units/bar_unit_demo.py b/examples/units/bar_unit_demo.py deleted file mode 100644 index 86b2add9e28a..000000000000 --- a/examples/units/bar_unit_demo.py +++ /dev/null @@ -1,30 +0,0 @@ -import numpy as np -from basic_units import cm, inch -import matplotlib.pyplot as plt - - -N = 5 -menMeans = (150*cm, 160*cm, 146*cm, 172*cm, 155*cm) -menStd = (20*cm, 30*cm, 32*cm, 10*cm, 20*cm) - -fig, ax = plt.subplots() - -ind = np.arange(N) # the x locations for the groups -width = 0.35 # the width of the bars -p1 = ax.bar(ind, menMeans, width, color='r', bottom=0*cm, yerr=menStd) - - -womenMeans = (145*cm, 149*cm, 172*cm, 165*cm, 200*cm) -womenStd = (30*cm, 25*cm, 20*cm, 31*cm, 22*cm) -p2 = ax.bar(ind + width, womenMeans, width, color='y', bottom=0*cm, yerr=womenStd) - -ax.set_title('Scores by group and gender') -ax.set_xticks(ind + width) -ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5')) - -ax.legend((p1[0], p2[0]), ('Men', 'Women')) -ax.yaxis.set_units(inch) -ax.autoscale_view() - -#plt.savefig('barchart_demo') -plt.show() diff --git a/examples/units/basic_units.py b/examples/units/basic_units.py deleted file mode 100644 index f8425f1ec45c..000000000000 --- a/examples/units/basic_units.py +++ /dev/null @@ -1,370 +0,0 @@ -import math - -import numpy as np - -import matplotlib.units as units -import matplotlib.ticker as ticker -from matplotlib.axes import Axes -from matplotlib.cbook import iterable - - -class ProxyDelegate(object): - def __init__(self, fn_name, proxy_type): - self.proxy_type = proxy_type - self.fn_name = fn_name - - def __get__(self, obj, objtype=None): - return self.proxy_type(self.fn_name, obj) - - -class TaggedValueMeta (type): - def __init__(cls, name, bases, dict): - for fn_name in cls._proxies.keys(): - try: - dummy = getattr(cls, fn_name) - except AttributeError: - setattr(cls, fn_name, - ProxyDelegate(fn_name, cls._proxies[fn_name])) - - -class PassThroughProxy(object): - def __init__(self, fn_name, obj): - self.fn_name = fn_name - self.target = obj.proxy_target - - def __call__(self, *args): - fn = getattr(self.target, self.fn_name) - ret = fn(*args) - return ret - - -class ConvertArgsProxy(PassThroughProxy): - def __init__(self, fn_name, obj): - PassThroughProxy.__init__(self, fn_name, obj) - self.unit = obj.unit - - def __call__(self, *args): - converted_args = [] - for a in args: - try: - converted_args.append(a.convert_to(self.unit)) - except AttributeError: - converted_args.append(TaggedValue(a, self.unit)) - converted_args = tuple([c.get_value() for c in converted_args]) - return PassThroughProxy.__call__(self, *converted_args) - - -class ConvertReturnProxy(PassThroughProxy): - def __init__(self, fn_name, obj): - PassThroughProxy.__init__(self, fn_name, obj) - self.unit = obj.unit - - def __call__(self, *args): - ret = PassThroughProxy.__call__(self, *args) - if (type(ret) == type(NotImplemented)): - return NotImplemented - return TaggedValue(ret, self.unit) - - -class ConvertAllProxy(PassThroughProxy): - def __init__(self, fn_name, obj): - PassThroughProxy.__init__(self, fn_name, obj) - self.unit = obj.unit - - def __call__(self, *args): - converted_args = [] - arg_units = [self.unit] - for a in args: - if hasattr(a, 'get_unit') and not hasattr(a, 'convert_to'): - # if this arg has a unit type but no conversion ability, - # this operation is prohibited - return NotImplemented - - if hasattr(a, 'convert_to'): - try: - a = a.convert_to(self.unit) - except: - pass - arg_units.append(a.get_unit()) - converted_args.append(a.get_value()) - else: - converted_args.append(a) - if hasattr(a, 'get_unit'): - arg_units.append(a.get_unit()) - else: - arg_units.append(None) - converted_args = tuple(converted_args) - ret = PassThroughProxy.__call__(self, *converted_args) - if (type(ret) == type(NotImplemented)): - return NotImplemented - ret_unit = unit_resolver(self.fn_name, arg_units) - if (ret_unit == NotImplemented): - return NotImplemented - return TaggedValue(ret, ret_unit) - - -class _TaggedValue(object): - - _proxies = {'__add__': ConvertAllProxy, - '__sub__': ConvertAllProxy, - '__mul__': ConvertAllProxy, - '__rmul__': ConvertAllProxy, - '__cmp__': ConvertAllProxy, - '__lt__': ConvertAllProxy, - '__gt__': ConvertAllProxy, - '__len__': PassThroughProxy} - - def __new__(cls, value, unit): - # generate a new subclass for value - value_class = type(value) - try: - subcls = type('TaggedValue_of_%s' % (value_class.__name__), - tuple([cls, value_class]), - {}) - if subcls not in units.registry: - units.registry[subcls] = basicConverter - return object.__new__(subcls) - except TypeError: - if cls not in units.registry: - units.registry[cls] = basicConverter - return object.__new__(cls) - - def __init__(self, value, unit): - self.value = value - self.unit = unit - self.proxy_target = self.value - - def __getattribute__(self, name): - if (name.startswith('__')): - return object.__getattribute__(self, name) - variable = object.__getattribute__(self, 'value') - if (hasattr(variable, name) and name not in self.__class__.__dict__): - return getattr(variable, name) - return object.__getattribute__(self, name) - - def __array__(self, t=None, context=None): - if t is not None: - return np.asarray(self.value).astype(t) - else: - return np.asarray(self.value, 'O') - - def __array_wrap__(self, array, context): - return TaggedValue(array, self.unit) - - def __repr__(self): - return 'TaggedValue(' + repr(self.value) + ', ' + repr(self.unit) + ')' - - def __str__(self): - return str(self.value) + ' in ' + str(self.unit) - - def __len__(self): - return len(self.value) - - def __iter__(self): - class IteratorProxy(object): - def __init__(self, iter, unit): - self.iter = iter - self.unit = unit - - def __next__(self): - value = next(self.iter) - return TaggedValue(value, self.unit) - next = __next__ # for Python 2 - return IteratorProxy(iter(self.value), self.unit) - - def get_compressed_copy(self, mask): - new_value = np.ma.masked_array(self.value, mask=mask).compressed() - return TaggedValue(new_value, self.unit) - - def convert_to(self, unit): - if (unit == self.unit or not unit): - return self - new_value = self.unit.convert_value_to(self.value, unit) - return TaggedValue(new_value, unit) - - def get_value(self): - return self.value - - def get_unit(self): - return self.unit - - -TaggedValue = TaggedValueMeta('TaggedValue', (_TaggedValue, ), {}) - - -class BasicUnit(object): - def __init__(self, name, fullname=None): - self.name = name - if fullname is None: - fullname = name - self.fullname = fullname - self.conversions = dict() - - def __repr__(self): - return 'BasicUnit(%s)' % self.name - - def __str__(self): - return self.fullname - - def __call__(self, value): - return TaggedValue(value, self) - - def __mul__(self, rhs): - value = rhs - unit = self - if hasattr(rhs, 'get_unit'): - value = rhs.get_value() - unit = rhs.get_unit() - unit = unit_resolver('__mul__', (self, unit)) - if (unit == NotImplemented): - return NotImplemented - return TaggedValue(value, unit) - - def __rmul__(self, lhs): - return self*lhs - - def __array_wrap__(self, array, context): - return TaggedValue(array, self) - - def __array__(self, t=None, context=None): - ret = np.array([1]) - if t is not None: - return ret.astype(t) - else: - return ret - - def add_conversion_factor(self, unit, factor): - def convert(x): - return x*factor - self.conversions[unit] = convert - - def add_conversion_fn(self, unit, fn): - self.conversions[unit] = fn - - def get_conversion_fn(self, unit): - return self.conversions[unit] - - def convert_value_to(self, value, unit): - conversion_fn = self.conversions[unit] - ret = conversion_fn(value) - return ret - - def get_unit(self): - return self - - -class UnitResolver(object): - def addition_rule(self, units): - for unit_1, unit_2 in zip(units[:-1], units[1:]): - if (unit_1 != unit_2): - return NotImplemented - return units[0] - - def multiplication_rule(self, units): - non_null = [u for u in units if u] - if (len(non_null) > 1): - return NotImplemented - return non_null[0] - - op_dict = { - '__mul__': multiplication_rule, - '__rmul__': multiplication_rule, - '__add__': addition_rule, - '__radd__': addition_rule, - '__sub__': addition_rule, - '__rsub__': addition_rule} - - def __call__(self, operation, units): - if (operation not in self.op_dict): - return NotImplemented - - return self.op_dict[operation](self, units) - - -unit_resolver = UnitResolver() - -cm = BasicUnit('cm', 'centimeters') -inch = BasicUnit('inch', 'inches') -inch.add_conversion_factor(cm, 2.54) -cm.add_conversion_factor(inch, 1/2.54) - -radians = BasicUnit('rad', 'radians') -degrees = BasicUnit('deg', 'degrees') -radians.add_conversion_factor(degrees, 180.0/np.pi) -degrees.add_conversion_factor(radians, np.pi/180.0) - -secs = BasicUnit('s', 'seconds') -hertz = BasicUnit('Hz', 'Hertz') -minutes = BasicUnit('min', 'minutes') - -secs.add_conversion_fn(hertz, lambda x: 1./x) -secs.add_conversion_factor(minutes, 1/60.0) - - -# radians formatting -def rad_fn(x, pos=None): - n = int((x / np.pi) * 2.0 + 0.25) - if n == 0: - return '0' - elif n == 1: - return r'$\pi/2$' - elif n == 2: - return r'$\pi$' - elif n % 2 == 0: - return r'$%s\pi$' % (n//2,) - else: - return r'$%s\pi/2$' % (n,) - - -class BasicUnitConverter(units.ConversionInterface): - @staticmethod - def axisinfo(unit, axis): - 'return AxisInfo instance for x and unit' - - if unit == radians: - return units.AxisInfo( - majloc=ticker.MultipleLocator(base=np.pi/2), - majfmt=ticker.FuncFormatter(rad_fn), - label=unit.fullname, - ) - elif unit == degrees: - return units.AxisInfo( - majloc=ticker.AutoLocator(), - majfmt=ticker.FormatStrFormatter(r'$%i^\circ$'), - label=unit.fullname, - ) - elif unit is not None: - if hasattr(unit, 'fullname'): - return units.AxisInfo(label=unit.fullname) - elif hasattr(unit, 'unit'): - return units.AxisInfo(label=unit.unit.fullname) - return None - - @staticmethod - def convert(val, unit, axis): - if units.ConversionInterface.is_numlike(val): - return val - if iterable(val): - return [thisval.convert_to(unit).get_value() for thisval in val] - else: - return val.convert_to(unit).get_value() - - @staticmethod - def default_units(x, axis): - 'return the default unit for x or None' - if iterable(x): - for thisx in x: - return thisx.unit - return x.unit - - -def cos(x): - if iterable(x): - return [math.cos(val.convert_to(radians).get_value()) for val in x] - else: - return math.cos(x.convert_to(radians).get_value()) - - -basicConverter = BasicUnitConverter() -units.registry[BasicUnit] = basicConverter -units.registry[TaggedValue] = basicConverter diff --git a/examples/units/evans_test.py b/examples/units/evans_test.py deleted file mode 100644 index 8f2686c33e4a..000000000000 --- a/examples/units/evans_test.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -A mockup "Foo" units class which supports -conversion and different tick formatting depending on the "unit". -Here the "unit" is just a scalar conversion factor, but this example shows mpl is -entirely agnostic to what kind of units client packages use -""" -from matplotlib.cbook import iterable -import matplotlib.units as units -import matplotlib.ticker as ticker -import matplotlib.pyplot as plt - - -class Foo(object): - def __init__(self, val, unit=1.0): - self.unit = unit - self._val = val * unit - - def value(self, unit): - if unit is None: - unit = self.unit - return self._val / unit - - -class FooConverter(object): - @staticmethod - def axisinfo(unit, axis): - 'return the Foo AxisInfo' - if unit == 1.0 or unit == 2.0: - return units.AxisInfo( - majloc=ticker.IndexLocator(8, 0), - majfmt=ticker.FormatStrFormatter("VAL: %s"), - label='foo', - ) - - else: - return None - - @staticmethod - def convert(obj, unit, axis): - """ - convert obj using unit. If obj is a sequence, return the - converted sequence - """ - if units.ConversionInterface.is_numlike(obj): - return obj - - if iterable(obj): - return [o.value(unit) for o in obj] - else: - return obj.value(unit) - - @staticmethod - def default_units(x, axis): - 'return the default unit for x or None' - if iterable(x): - for thisx in x: - return thisx.unit - else: - return x.unit - -units.registry[Foo] = FooConverter() - -# create some Foos -x = [] -for val in range(0, 50, 2): - x.append(Foo(val, 1.0)) - -# and some arbitrary y data -y = [i for i in range(len(x))] - - -# plot specifying units -fig = plt.figure() -fig.suptitle("Custom units") -fig.subplots_adjust(bottom=0.2) -ax = fig.add_subplot(1, 2, 2) -ax.plot(x, y, 'o', xunits=2.0) -for label in ax.get_xticklabels(): - label.set_rotation(30) - label.set_ha('right') -ax.set_title("xunits = 2.0") - - -# plot without specifying units; will use the None branch for axisinfo -ax = fig.add_subplot(1, 2, 1) -ax.plot(x, y) # uses default units -ax.set_title('default units') -for label in ax.get_xticklabels(): - label.set_rotation(30) - label.set_ha('right') - -plt.show() diff --git a/examples/units/radian_demo.py b/examples/units/radian_demo.py deleted file mode 100644 index 3eeca931e75d..000000000000 --- a/examples/units/radian_demo.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Plot with radians from the basic_units mockup example package -This example shows how the unit class can determine the tick locating, -formatting and axis labeling. -""" -import numpy as np -from basic_units import radians, degrees, cos -from matplotlib.pyplot import figure, show - -x = [val*radians for val in np.arange(0, 15, 0.01)] - -fig = figure() -fig.subplots_adjust(hspace=0.3) - -ax = fig.add_subplot(211) -line1, = ax.plot(x, cos(x), xunits=radians) - -ax = fig.add_subplot(212) -line2, = ax.plot(x, cos(x), xunits=degrees) - -show() diff --git a/examples/units/units_sample.py b/examples/units/units_sample.py deleted file mode 100644 index 84f26490a87f..000000000000 --- a/examples/units/units_sample.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -plot using a variety of cm vs inches conversions. The example shows -how default unit instrospection works (ax1), how various keywords can -be used to set the x and y units to override the defaults (ax2, ax3, -ax4) and how one can set the xlimits using scalars (ax3, current units -assumed) or units (conversions applied to get the numbers to current -units) - -""" -from basic_units import cm, inch -import matplotlib.pyplot as plt -import numpy - -cms = cm * numpy.arange(0, 10, 2) - -fig = plt.figure() - -ax1 = fig.add_subplot(2, 2, 1) -ax1.plot(cms, cms) - -ax2 = fig.add_subplot(2, 2, 2) -ax2.plot(cms, cms, xunits=cm, yunits=inch) - -ax3 = fig.add_subplot(2, 2, 3) -ax3.plot(cms, cms, xunits=inch, yunits=cm) -ax3.set_xlim(3, 6) # scalars are interpreted in current units - -ax4 = fig.add_subplot(2, 2, 4) -ax4.plot(cms, cms, xunits=inch, yunits=inch) -#fig.savefig('simple_conversion_plot.png') -ax4.set_xlim(3*cm, 6*cm) # cm are converted to inches - -plt.show() diff --git a/examples/units/units_scatter.py b/examples/units/units_scatter.py deleted file mode 100644 index 8a8e43e6ca8e..000000000000 --- a/examples/units/units_scatter.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Demonstrate unit handling - -basic_units is a mockup of a true units package used for testing -purposed, which illustrates the basic interface that a units package -must provide to matplotlib. - -The example below shows support for unit conversions over masked -arrays. -""" -import numpy as np -import matplotlib.pyplot as plt -from basic_units import secs, hertz, minutes - -# create masked array -data = (1, 2, 3, 4, 5, 6, 7, 8) -mask = (1, 0, 1, 0, 0, 0, 1, 0) -xsecs = secs * np.ma.MaskedArray(data, mask, float) - -fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, sharex=True) -ax1.scatter(xsecs, xsecs) -ax1.yaxis.set_units(secs) -ax1.axis([0, 10, 0, 10]) - -ax2.scatter(xsecs, xsecs, yunits=hertz) -ax2.axis([0, 10, 0, 1]) - -ax3.scatter(xsecs, xsecs, yunits=hertz) -ax3.yaxis.set_units(minutes) -ax3.axis([0, 10, 0, 1]) - -fig.tight_layout() -plt.show() diff --git a/examples/user_interfaces/README.txt b/examples/user_interfaces/README.txt deleted file mode 100644 index 36bff79d0834..000000000000 --- a/examples/user_interfaces/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -Embedding matplotlib in graphical user interfaces -================================================= - -You can embed matplotlib directly into a user interface application by -following the embedding_in_SOMEGUI.py examples here. Currently -matplotlib supports wxpython, pygtk, tkinter and pyqt4/5. - -When embedding matplotlib in a GUI, you must use the matplotlib API -directly rather than the pylab/pyplot proceedural interface, so take a -look at the examples/api directory for some example code working with -the API. diff --git a/examples/user_interfaces/README.wx b/examples/user_interfaces/README.wx deleted file mode 100644 index c44eeff2ced1..000000000000 --- a/examples/user_interfaces/README.wx +++ /dev/null @@ -1,21 +0,0 @@ -You have a few different options available to you for embedding -matplotlib in a wxPython application - -1. Embed one of the wxPython backend widgets (which subclass wx.Panel) - directly and draw plots on it using matplotlib's object-oriented - API. This approach is demonstrated by some of the examples - embedding_in_wx*.py - -2. Embed the PlotPanel from Matt Newville's `MPlot' package and draw - plots on it using its plot() and oplot() methods. - - http://cars9.uchicago.edu/~newville/Python/MPlot/ - -3. Embed the PlotPanel from Ken McIvor wxmpl module and draw plots on - it using the matplotlib's object-oriented API. - - http://agni.phys.iit.edu/~kmcivor/wxmpl/ - -Each of these approachs has different benefits and drawbacks, so I -encourage you to evaluate each of them and select the one that best -meets your needs. diff --git a/examples/user_interfaces/embedding_in_gtk.py b/examples/user_interfaces/embedding_in_gtk.py deleted file mode 100644 index 7cb9467f5543..000000000000 --- a/examples/user_interfaces/embedding_in_gtk.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -show how to add a matplotlib FigureCanvasGTK or FigureCanvasGTKAgg widget to a -gtk.Window -""" - -import gtk - -from matplotlib.figure import Figure -from numpy import arange, sin, pi - -# uncomment to select /GTK/GTKAgg/GTKCairo -#from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas -from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas -#from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas - - -win = gtk.Window() -win.connect("destroy", lambda x: gtk.main_quit()) -win.set_default_size(400, 300) -win.set_title("Embedding in GTK") - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) -a.plot(t, s) - -canvas = FigureCanvas(f) # a gtk.DrawingArea -win.add(canvas) - -win.show_all() -gtk.main() diff --git a/examples/user_interfaces/embedding_in_gtk2.py b/examples/user_interfaces/embedding_in_gtk2.py deleted file mode 100644 index 2665e3123b26..000000000000 --- a/examples/user_interfaces/embedding_in_gtk2.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -show how to add a matplotlib FigureCanvasGTK or FigureCanvasGTKAgg widget and -a toolbar to a gtk.Window -""" -import gtk - -from matplotlib.figure import Figure -from numpy import arange, sin, pi - -# uncomment to select /GTK/GTKAgg/GTKCairo -#from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas -from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas -#from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas - -# or NavigationToolbar for classic -#from matplotlib.backends.backend_gtk import NavigationToolbar2GTK as NavigationToolbar -from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar - -# implement the default mpl key bindings -from matplotlib.backend_bases import key_press_handler - -win = gtk.Window() -win.connect("destroy", lambda x: gtk.main_quit()) -win.set_default_size(400, 300) -win.set_title("Embedding in GTK") - -vbox = gtk.VBox() -win.add(vbox) - -fig = Figure(figsize=(5, 4), dpi=100) -ax = fig.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) - -ax.plot(t, s) - - -canvas = FigureCanvas(fig) # a gtk.DrawingArea -vbox.pack_start(canvas) -toolbar = NavigationToolbar(canvas, win) -vbox.pack_start(toolbar, False, False) - - -def on_key_event(event): - print('you pressed %s' % event.key) - key_press_handler(event, canvas, toolbar) - -canvas.mpl_connect('key_press_event', on_key_event) - -win.show_all() -gtk.main() diff --git a/examples/user_interfaces/embedding_in_gtk3.py b/examples/user_interfaces/embedding_in_gtk3.py deleted file mode 100755 index a8d2f59822a2..000000000000 --- a/examples/user_interfaces/embedding_in_gtk3.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -demonstrate adding a FigureCanvasGTK3Agg widget to a Gtk.ScrolledWindow -using GTK3 accessed via pygobject -""" - -from gi.repository import Gtk - -from matplotlib.figure import Figure -from numpy import arange, sin, pi -from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas - -win = Gtk.Window() -win.connect("delete-event", Gtk.main_quit) -win.set_default_size(400, 300) -win.set_title("Embedding in GTK") - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) -a.plot(t, s) - -sw = Gtk.ScrolledWindow() -win.add(sw) -# A scrolled window border goes outside the scrollbars and viewport -sw.set_border_width(10) - -canvas = FigureCanvas(f) # a Gtk.DrawingArea -canvas.set_size_request(800, 600) -sw.add_with_viewport(canvas) - -win.show_all() -Gtk.main() diff --git a/examples/user_interfaces/embedding_in_gtk3_panzoom.py b/examples/user_interfaces/embedding_in_gtk3_panzoom.py deleted file mode 100644 index 34331a3e2e2c..000000000000 --- a/examples/user_interfaces/embedding_in_gtk3_panzoom.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -demonstrate NavigationToolbar with GTK3 accessed via pygobject -""" - -from gi.repository import Gtk - -from matplotlib.figure import Figure -from numpy import arange, sin, pi -from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas -from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar - -win = Gtk.Window() -win.connect("delete-event", Gtk.main_quit) -win.set_default_size(400, 300) -win.set_title("Embedding in GTK") - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(1, 1, 1) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) -a.plot(t, s) - -vbox = Gtk.VBox() -win.add(vbox) - -# Add canvas to vbox -canvas = FigureCanvas(f) # a Gtk.DrawingArea -vbox.pack_start(canvas, True, True, 0) - -# Create toolbar -toolbar = NavigationToolbar(canvas, win) -vbox.pack_start(toolbar, False, False, 0) - -win.show_all() -Gtk.main() diff --git a/examples/user_interfaces/embedding_in_qt4.py b/examples/user_interfaces/embedding_in_qt4.py deleted file mode 100755 index 31108c9bd300..000000000000 --- a/examples/user_interfaces/embedding_in_qt4.py +++ /dev/null @@ -1,138 +0,0 @@ - -# embedding_in_qt4.py --- Simple Qt4 application embedding matplotlib canvases -# -# Copyright (C) 2005 Florent Rougon -# 2006 Darren Dale -# -# This file is an example program for matplotlib. It may be used and -# modified with no restriction; raw copies as well as modified versions -# may be distributed without limitation. - -from __future__ import unicode_literals -import sys -import os -import random -from matplotlib.backends import qt_compat -use_pyside = qt_compat.QT_API == qt_compat.QT_API_PYSIDE -if use_pyside: - from PySide import QtGui, QtCore -else: - from PyQt4 import QtGui, QtCore - -from numpy import arange, sin, pi -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.figure import Figure - -progname = os.path.basename(sys.argv[0]) -progversion = "0.1" - - -class MyMplCanvas(FigureCanvas): - """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" - - def __init__(self, parent=None, width=5, height=4, dpi=100): - fig = Figure(figsize=(width, height), dpi=dpi) - self.axes = fig.add_subplot(111) - # We want the axes cleared every time plot() is called - self.axes.hold(False) - - self.compute_initial_figure() - - # - FigureCanvas.__init__(self, fig) - self.setParent(parent) - - FigureCanvas.setSizePolicy(self, - QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Expanding) - FigureCanvas.updateGeometry(self) - - def compute_initial_figure(self): - pass - - -class MyStaticMplCanvas(MyMplCanvas): - """Simple canvas with a sine plot.""" - - def compute_initial_figure(self): - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - self.axes.plot(t, s) - - -class MyDynamicMplCanvas(MyMplCanvas): - """A canvas that updates itself every second with a new plot.""" - - def __init__(self, *args, **kwargs): - MyMplCanvas.__init__(self, *args, **kwargs) - timer = QtCore.QTimer(self) - timer.timeout.connect(self.update_figure) - timer.start(1000) - - def compute_initial_figure(self): - self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r') - - def update_figure(self): - # Build a list of 4 random integers between 0 and 10 (both inclusive) - l = [random.randint(0, 10) for i in range(4)] - - self.axes.plot([0, 1, 2, 3], l, 'r') - self.draw() - - -class ApplicationWindow(QtGui.QMainWindow): - def __init__(self): - QtGui.QMainWindow.__init__(self) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - self.setWindowTitle("application main window") - - self.file_menu = QtGui.QMenu('&File', self) - self.file_menu.addAction('&Quit', self.fileQuit, - QtCore.Qt.CTRL + QtCore.Qt.Key_Q) - self.menuBar().addMenu(self.file_menu) - - self.help_menu = QtGui.QMenu('&Help', self) - self.menuBar().addSeparator() - self.menuBar().addMenu(self.help_menu) - - self.help_menu.addAction('&About', self.about) - - self.main_widget = QtGui.QWidget(self) - - l = QtGui.QVBoxLayout(self.main_widget) - sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100) - dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100) - l.addWidget(sc) - l.addWidget(dc) - - self.main_widget.setFocus() - self.setCentralWidget(self.main_widget) - - self.statusBar().showMessage("All hail matplotlib!", 2000) - - def fileQuit(self): - self.close() - - def closeEvent(self, ce): - self.fileQuit() - - def about(self): - QtGui.QMessageBox.about(self, "About", - """embedding_in_qt4.py example -Copyright 2005 Florent Rougon, 2006 Darren Dale - -This program is a simple example of a Qt4 application embedding matplotlib -canvases. - -It may be used and modified with no restriction; raw copies as well as -modified versions may be distributed without limitation.""" - ) - - -qApp = QtGui.QApplication(sys.argv) - -aw = ApplicationWindow() -aw.setWindowTitle("%s" % progname) -aw.show() -sys.exit(qApp.exec_()) -#qApp.exec_() diff --git a/examples/user_interfaces/embedding_in_qt4_wtoolbar.py b/examples/user_interfaces/embedding_in_qt4_wtoolbar.py deleted file mode 100644 index da642fb75efb..000000000000 --- a/examples/user_interfaces/embedding_in_qt4_wtoolbar.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import print_function - -import sys - -import numpy as np -import matplotlib -matplotlib.use("Qt4Agg") -from matplotlib.figure import Figure -from matplotlib.backend_bases import key_press_handler -from matplotlib.backends.backend_qt4agg import ( - FigureCanvasQTAgg as FigureCanvas, - NavigationToolbar2QT as NavigationToolbar) -from matplotlib.backends import qt_compat -use_pyside = qt_compat.QT_API == qt_compat.QT_API_PYSIDE - -if use_pyside: - from PySide.QtCore import * - from PySide.QtGui import * -else: - from PyQt4.QtCore import * - from PyQt4.QtGui import * - - -class AppForm(QMainWindow): - def __init__(self, parent=None): - QMainWindow.__init__(self, parent) - #self.x, self.y = self.get_data() - self.data = self.get_data2() - self.create_main_frame() - self.on_draw() - - def create_main_frame(self): - self.main_frame = QWidget() - - self.fig = Figure((5.0, 4.0), dpi=100) - self.canvas = FigureCanvas(self.fig) - self.canvas.setParent(self.main_frame) - self.canvas.setFocusPolicy(Qt.StrongFocus) - self.canvas.setFocus() - - self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) - - self.canvas.mpl_connect('key_press_event', self.on_key_press) - - vbox = QVBoxLayout() - vbox.addWidget(self.canvas) # the matplotlib canvas - vbox.addWidget(self.mpl_toolbar) - self.main_frame.setLayout(vbox) - self.setCentralWidget(self.main_frame) - - def get_data2(self): - return np.arange(20).reshape([4, 5]).copy() - - def on_draw(self): - self.fig.clear() - self.axes = self.fig.add_subplot(111) - #self.axes.plot(self.x, self.y, 'ro') - self.axes.imshow(self.data, interpolation='nearest') - #self.axes.plot([1,2,3]) - self.canvas.draw() - - def on_key_press(self, event): - print('you pressed', event.key) - # implement the default mpl key press events described at - # http://matplotlib.org/users/navigation_toolbar.html#navigation-keyboard-shortcuts - key_press_handler(event, self.canvas, self.mpl_toolbar) - - -def main(): - app = QApplication(sys.argv) - form = AppForm() - form.show() - app.exec_() - - -if __name__ == "__main__": - main() diff --git a/examples/user_interfaces/embedding_in_qt5.py b/examples/user_interfaces/embedding_in_qt5.py deleted file mode 100755 index 95059c62b378..000000000000 --- a/examples/user_interfaces/embedding_in_qt5.py +++ /dev/null @@ -1,140 +0,0 @@ - -# embedding_in_qt5.py --- Simple Qt5 application embedding matplotlib canvases -# -# Copyright (C) 2005 Florent Rougon -# 2006 Darren Dale -# 2015 Jens H Nielsen -# -# This file is an example program for matplotlib. It may be used and -# modified with no restriction; raw copies as well as modified versions -# may be distributed without limitation. - -from __future__ import unicode_literals -import sys -import os -import random -import matplotlib -# Make sure that we are using QT5 -matplotlib.use('Qt5Agg') -from PyQt5 import QtCore, QtWidgets - -from numpy import arange, sin, pi -from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.figure import Figure - -progname = os.path.basename(sys.argv[0]) -progversion = "0.1" - - -class MyMplCanvas(FigureCanvas): - """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" - - def __init__(self, parent=None, width=5, height=4, dpi=100): - fig = Figure(figsize=(width, height), dpi=dpi) - self.axes = fig.add_subplot(111) - # We want the axes cleared every time plot() is called - self.axes.hold(False) - - self.compute_initial_figure() - - # - FigureCanvas.__init__(self, fig) - self.setParent(parent) - - FigureCanvas.setSizePolicy(self, - QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Expanding) - FigureCanvas.updateGeometry(self) - - def compute_initial_figure(self): - pass - - -class MyStaticMplCanvas(MyMplCanvas): - """Simple canvas with a sine plot.""" - - def compute_initial_figure(self): - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - self.axes.plot(t, s) - - -class MyDynamicMplCanvas(MyMplCanvas): - """A canvas that updates itself every second with a new plot.""" - - def __init__(self, *args, **kwargs): - MyMplCanvas.__init__(self, *args, **kwargs) - timer = QtCore.QTimer(self) - timer.timeout.connect(self.update_figure) - timer.start(1000) - - def compute_initial_figure(self): - self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r') - - def update_figure(self): - # Build a list of 4 random integers between 0 and 10 (both inclusive) - l = [random.randint(0, 10) for i in range(4)] - - self.axes.plot([0, 1, 2, 3], l, 'r') - self.draw() - - -class ApplicationWindow(QtWidgets.QMainWindow): - def __init__(self): - QtWidgets.QMainWindow.__init__(self) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - self.setWindowTitle("application main window") - - self.file_menu = QtWidgets.QMenu('&File', self) - self.file_menu.addAction('&Quit', self.fileQuit, - QtCore.Qt.CTRL + QtCore.Qt.Key_Q) - self.menuBar().addMenu(self.file_menu) - - self.help_menu = QtWidgets.QMenu('&Help', self) - self.menuBar().addSeparator() - self.menuBar().addMenu(self.help_menu) - - self.help_menu.addAction('&About', self.about) - - self.main_widget = QtWidgets.QWidget(self) - - l = QtWidgets.QVBoxLayout(self.main_widget) - sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100) - dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100) - l.addWidget(sc) - l.addWidget(dc) - - self.main_widget.setFocus() - self.setCentralWidget(self.main_widget) - - self.statusBar().showMessage("All hail matplotlib!", 2000) - - def fileQuit(self): - self.close() - - def closeEvent(self, ce): - self.fileQuit() - - def about(self): - QtWidgets.QMessageBox.about(self, "About", - """embedding_in_qt5.py example -Copyright 2005 Florent Rougon, 2006 Darren Dale, 2015 Jens H Nielsen - -This program is a simple example of a Qt5 application embedding matplotlib -canvases. - -It may be used and modified with no restriction; raw copies as well as -modified versions may be distributed without limitation. - -This is modified from the embedding in qt4 example to show the difference -between qt4 and qt5""" - ) - - -qApp = QtWidgets.QApplication(sys.argv) - -aw = ApplicationWindow() -aw.setWindowTitle("%s" % progname) -aw.show() -sys.exit(qApp.exec_()) -#qApp.exec_() diff --git a/examples/user_interfaces/embedding_in_tk.py b/examples/user_interfaces/embedding_in_tk.py deleted file mode 100755 index b4aacd416b7d..000000000000 --- a/examples/user_interfaces/embedding_in_tk.py +++ /dev/null @@ -1,57 +0,0 @@ -import matplotlib -matplotlib.use('TkAgg') - -from numpy import arange, sin, pi -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg -# implement the default mpl key bindings -from matplotlib.backend_bases import key_press_handler - - -from matplotlib.figure import Figure - -import sys -if sys.version_info[0] < 3: - import Tkinter as Tk -else: - import tkinter as Tk - -root = Tk.Tk() -root.wm_title("Embedding in TK") - - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) - -a.plot(t, s) - - -# a tk.DrawingArea -canvas = FigureCanvasTkAgg(f, master=root) -canvas.show() -canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -toolbar = NavigationToolbar2TkAgg(canvas, root) -toolbar.update() -canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - - -def on_key_event(event): - print('you pressed %s' % event.key) - key_press_handler(event, canvas, toolbar) - -canvas.mpl_connect('key_press_event', on_key_event) - - -def _quit(): - root.quit() # stops mainloop - root.destroy() # this is necessary on Windows to prevent - # Fatal Python Error: PyEval_RestoreThread: NULL tstate - -button = Tk.Button(master=root, text='Quit', command=_quit) -button.pack(side=Tk.BOTTOM) - -Tk.mainloop() -# If you put root.destroy() here, it will cause an error if -# the window is closed with the window manager. diff --git a/examples/user_interfaces/embedding_in_tk2.py b/examples/user_interfaces/embedding_in_tk2.py deleted file mode 100644 index ab6c98b66dee..000000000000 --- a/examples/user_interfaces/embedding_in_tk2.py +++ /dev/null @@ -1,43 +0,0 @@ -import matplotlib -matplotlib.use('TkAgg') - -from numpy import arange, sin, pi -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.figure import Figure - -import sys -if sys.version_info[0] < 3: - import Tkinter as Tk -else: - import tkinter as Tk - - -def destroy(e): - sys.exit() - -root = Tk.Tk() -root.wm_title("Embedding in TK") - - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) - -a.plot(t, s) -a.set_title('Tk embedding') -a.set_xlabel('X axis label') -a.set_ylabel('Y label') - - -# a tk.DrawingArea -canvas = FigureCanvasTkAgg(f, master=root) -canvas.show() -canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -button = Tk.Button(master=root, text='Quit', command=sys.exit) -button.pack(side=Tk.BOTTOM) - -Tk.mainloop() diff --git a/examples/user_interfaces/embedding_in_tk_canvas.py b/examples/user_interfaces/embedding_in_tk_canvas.py deleted file mode 100755 index 8e069bc6d930..000000000000 --- a/examples/user_interfaces/embedding_in_tk_canvas.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- noplot -*- - -import matplotlib as mpl -import numpy as np -import sys -if sys.version_info[0] < 3: - import Tkinter as tk -else: - import tkinter as tk -import matplotlib.backends.tkagg as tkagg -from matplotlib.backends.backend_agg import FigureCanvasAgg - - -def draw_figure(canvas, figure, loc=(0, 0)): - """ Draw a matplotlib figure onto a Tk canvas - - loc: location of top-left corner of figure on canvas in pixels. - - Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py - """ - figure_canvas_agg = FigureCanvasAgg(figure) - figure_canvas_agg.draw() - figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds - figure_w, figure_h = int(figure_w), int(figure_h) - photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h) - - # Position: convert from top-left anchor to center anchor - canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo) - - # Unfortunatly, there's no accessor for the pointer to the native renderer - tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) - - # Return a handle which contains a reference to the photo object - # which must be kept live or else the picture disappears - return photo - -# Create a canvas -w, h = 300, 200 -window = tk.Tk() -window.title("A figure in a canvas") -canvas = tk.Canvas(window, width=w, height=h) -canvas.pack() - -# Generate some example data -X = np.linspace(0, 2.0*3.14, 50) -Y = np.sin(X) - -# Create the figure we desire to add to an existing canvas -fig = mpl.figure.Figure(figsize=(2, 1)) -ax = fig.add_axes([0, 0, 1, 1]) -ax.plot(X, Y) - -# Keep this handle alive, or else figure will disappear -fig_x, fig_y = 100, 100 -fig_photo = draw_figure(canvas, fig, loc=(fig_x, fig_y)) -fig_w, fig_h = fig_photo.width(), fig_photo.height() - -# Add more elements to the canvas, potentially on top of the figure -canvas.create_line(200, 50, fig_x + fig_w / 2, fig_y + fig_h / 2) -canvas.create_text(200, 50, text="Zero-crossing", anchor="s") - -# Let Tk take over -tk.mainloop() diff --git a/examples/user_interfaces/embedding_in_wx2.py b/examples/user_interfaces/embedding_in_wx2.py deleted file mode 100644 index 55c1580babbd..000000000000 --- a/examples/user_interfaces/embedding_in_wx2.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -An example of how to use wx or wxagg in an application with the new -toolbar - comment out the setA_toolbar line for no toolbar -""" - -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -from numpy import arange, sin, pi - -import matplotlib - -# uncomment the following to use wx rather than wxagg -#matplotlib.use('WX') -#from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas - -# comment out the following to use wx rather than wxagg -matplotlib.use('WXAgg') -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas - -from matplotlib.backends.backend_wx import NavigationToolbar2Wx - -from matplotlib.figure import Figure - -import wx -import wx.lib.mixins.inspection as WIT - - -class CanvasFrame(wx.Frame): - def __init__(self): - wx.Frame.__init__(self, None, -1, - 'CanvasFrame', size=(550, 350)) - - self.figure = Figure() - self.axes = self.figure.add_subplot(111) - t = arange(0.0, 3.0, 0.01) - s = sin(2 * pi * t) - - self.axes.plot(t, s) - self.canvas = FigureCanvas(self, -1, self.figure) - - self.sizer = wx.BoxSizer(wx.VERTICAL) - self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND) - self.SetSizer(self.sizer) - self.Fit() - - self.add_toolbar() # comment this out for no toolbar - - def add_toolbar(self): - self.toolbar = NavigationToolbar2Wx(self.canvas) - self.toolbar.Realize() - # By adding toolbar in sizer, we are able to put it at the bottom - # of the frame - so appearance is closer to GTK version. - self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - # update the axes menu on the toolbar - self.toolbar.update() - - -# alternatively you could use -#class App(wx.App): -class App(WIT.InspectableApp): - def OnInit(self): - 'Create the main window and insert the custom frame' - self.Init() - frame = CanvasFrame() - frame.Show(True) - - return True - -app = App(0) -app.MainLoop() diff --git a/examples/user_interfaces/embedding_in_wx3.py b/examples/user_interfaces/embedding_in_wx3.py deleted file mode 100755 index 76cd4196c513..000000000000 --- a/examples/user_interfaces/embedding_in_wx3.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -Copyright (C) 2003-2004 Andrew Straw, Jeremy O'Donoghue and others - -License: This work is licensed under the PSF. A copy should be included -with this source code, and is also available at -http://www.python.org/psf/license.html - -This is yet another example of using matplotlib with wx. Hopefully -this is pretty full-featured: - - - both matplotlib toolbar and WX buttons manipulate plot - - full wxApp framework, including widget interaction - - XRC (XML wxWidgets resource) file to create GUI (made with XRCed) - -This was derived from embedding_in_wx and dynamic_image_wxagg. - -Thanks to matplotlib and wx teams for creating such great software! - -""" -from __future__ import print_function - -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -import sys -import time -import os -import gc -import matplotlib -matplotlib.use('WXAgg') -import matplotlib.cm as cm -import matplotlib.cbook as cbook -from matplotlib.backends.backend_wxagg import Toolbar, FigureCanvasWxAgg -from matplotlib.figure import Figure -import numpy as np - -import wx -import wx.xrc as xrc - -ERR_TOL = 1e-5 # floating point slop for peak-detection - - -matplotlib.rc('image', origin='lower') - - -class PlotPanel(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent, -1) - - self.fig = Figure((5, 4), 75) - self.canvas = FigureCanvasWxAgg(self, -1, self.fig) - self.toolbar = Toolbar(self.canvas) # matplotlib toolbar - self.toolbar.Realize() - # self.toolbar.set_active([0,1]) - - # Now put all into a sizer - sizer = wx.BoxSizer(wx.VERTICAL) - # This way of adding to sizer allows resizing - sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) - # Best to allow the toolbar to resize! - sizer.Add(self.toolbar, 0, wx.GROW) - self.SetSizer(sizer) - self.Fit() - - def init_plot_data(self): - a = self.fig.add_subplot(111) - - x = np.arange(120.0) * 2 * np.pi / 60.0 - y = np.arange(100.0) * 2 * np.pi / 50.0 - self.x, self.y = np.meshgrid(x, y) - z = np.sin(self.x) + np.cos(self.y) - self.im = a.imshow(z, cmap=cm.RdBu) # , interpolation='nearest') - - zmax = np.amax(z) - ERR_TOL - ymax_i, xmax_i = np.nonzero(z >= zmax) - if self.im.origin == 'upper': - ymax_i = z.shape[0] - ymax_i - self.lines = a.plot(xmax_i, ymax_i, 'ko') - - self.toolbar.update() # Not sure why this is needed - ADS - - def GetToolBar(self): - # You will need to override GetToolBar if you are using an - # unmanaged toolbar in your frame - return self.toolbar - - def OnWhiz(self, evt): - self.x += np.pi / 15 - self.y += np.pi / 20 - z = np.sin(self.x) + np.cos(self.y) - self.im.set_array(z) - - zmax = np.amax(z) - ERR_TOL - ymax_i, xmax_i = np.nonzero(z >= zmax) - if self.im.origin == 'upper': - ymax_i = z.shape[0] - ymax_i - self.lines[0].set_data(xmax_i, ymax_i) - - self.canvas.draw() - - def onEraseBackground(self, evt): - # this is supposed to prevent redraw flicker on some X servers... - pass - - -class MyApp(wx.App): - def OnInit(self): - xrcfile = cbook.get_sample_data('embedding_in_wx3.xrc', - asfileobj=False) - print('loading', xrcfile) - - self.res = xrc.XmlResource(xrcfile) - - # main frame and panel --------- - - self.frame = self.res.LoadFrame(None, "MainFrame") - self.panel = xrc.XRCCTRL(self.frame, "MainPanel") - - # matplotlib panel ------------- - - # container for matplotlib panel (I like to make a container - # panel for our panel so I know where it'll go when in XRCed.) - plot_container = xrc.XRCCTRL(self.frame, "plot_container_panel") - sizer = wx.BoxSizer(wx.VERTICAL) - - # matplotlib panel itself - self.plotpanel = PlotPanel(plot_container) - self.plotpanel.init_plot_data() - - # wx boilerplate - sizer.Add(self.plotpanel, 1, wx.EXPAND) - plot_container.SetSizer(sizer) - - # whiz button ------------------ - whiz_button = xrc.XRCCTRL(self.frame, "whiz_button") - whiz_button.Bind(wx.EVT_BUTTON, self.plotpanel.OnWhiz) - - # bang button ------------------ - bang_button = xrc.XRCCTRL(self.frame, "bang_button") - bang_button.Bind(wx.EVT_BUTTON, self.OnBang) - - # final setup ------------------ - sizer = self.panel.GetSizer() - self.frame.Show(1) - - self.SetTopWindow(self.frame) - - return True - - def OnBang(self, event): - bang_count = xrc.XRCCTRL(self.frame, "bang_count") - bangs = bang_count.GetValue() - bangs = int(bangs) + 1 - bang_count.SetValue(str(bangs)) - -if __name__ == '__main__': - app = MyApp(0) - app.MainLoop() diff --git a/examples/user_interfaces/embedding_in_wx4.py b/examples/user_interfaces/embedding_in_wx4.py deleted file mode 100644 index 5270fc0c1d25..000000000000 --- a/examples/user_interfaces/embedding_in_wx4.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -An example of how to use wx or wxagg in an application with a custom -toolbar -""" - -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -from numpy import arange, sin, pi - -import matplotlib - -matplotlib.use('WXAgg') -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas -from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg - -from matplotlib.backends.backend_wx import _load_bitmap -from matplotlib.figure import Figure -from numpy.random import rand - -import wx - - -class MyNavigationToolbar(NavigationToolbar2WxAgg): - """ - Extend the default wx toolbar with your own event handlers - """ - ON_CUSTOM = wx.NewId() - - def __init__(self, canvas, cankill): - NavigationToolbar2WxAgg.__init__(self, canvas) - - # for simplicity I'm going to reuse a bitmap from wx, you'll - # probably want to add your own. - if 'phoenix' in wx.PlatformInfo: - self.AddTool(self.ON_CUSTOM, 'Click me', - _load_bitmap('stock_left.xpm'), - 'Activate custom contol') - self.Bind(wx.EVT_TOOL, self._on_custom, id=self.ON_CUSTOM) - else: - self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap('stock_left.xpm'), - 'Click me', 'Activate custom contol') - self.Bind(wx.EVT_TOOL, self._on_custom, id=self.ON_CUSTOM) - - def _on_custom(self, evt): - # add some text to the axes in a random location in axes (0,1) - # coords) with a random color - - # get the axes - ax = self.canvas.figure.axes[0] - - # generate a random location can color - x, y = tuple(rand(2)) - rgb = tuple(rand(3)) - - # add the text and draw - ax.text(x, y, 'You clicked me', - transform=ax.transAxes, - color=rgb) - self.canvas.draw() - evt.Skip() - - -class CanvasFrame(wx.Frame): - def __init__(self): - wx.Frame.__init__(self, None, -1, - 'CanvasFrame', size=(550, 350)) - - self.figure = Figure(figsize=(5, 4), dpi=100) - self.axes = self.figure.add_subplot(111) - t = arange(0.0, 3.0, 0.01) - s = sin(2 * pi * t) - - self.axes.plot(t, s) - - self.canvas = FigureCanvas(self, -1, self.figure) - - self.sizer = wx.BoxSizer(wx.VERTICAL) - self.sizer.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND) - # Capture the paint message - self.Bind(wx.EVT_PAINT, self.OnPaint) - - self.toolbar = MyNavigationToolbar(self.canvas, True) - self.toolbar.Realize() - # By adding toolbar in sizer, we are able to put it at the bottom - # of the frame - so appearance is closer to GTK version. - self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - - # update the axes menu on the toolbar - self.toolbar.update() - self.SetSizer(self.sizer) - self.Fit() - - def OnPaint(self, event): - self.canvas.draw() - event.Skip() - - -class App(wx.App): - def OnInit(self): - 'Create the main window and insert the custom frame' - frame = CanvasFrame() - frame.Show(True) - - return True - -app = App(0) -app.MainLoop() diff --git a/examples/user_interfaces/embedding_in_wx5.py b/examples/user_interfaces/embedding_in_wx5.py deleted file mode 100644 index 83db43c545d2..000000000000 --- a/examples/user_interfaces/embedding_in_wx5.py +++ /dev/null @@ -1,64 +0,0 @@ -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -import wx -import wx.lib.mixins.inspection as wit - -if 'phoenix' in wx.PlatformInfo: - import wx.lib.agw.aui as aui -else: - import wx.aui as aui - -import matplotlib as mpl -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas -from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar - - -class Plot(wx.Panel): - def __init__(self, parent, id=-1, dpi=None, **kwargs): - wx.Panel.__init__(self, parent, id=id, **kwargs) - self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2, 2)) - self.canvas = Canvas(self, -1, self.figure) - self.toolbar = Toolbar(self.canvas) - self.toolbar.Realize() - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.canvas, 1, wx.EXPAND) - sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - self.SetSizer(sizer) - - -class PlotNotebook(wx.Panel): - def __init__(self, parent, id=-1): - wx.Panel.__init__(self, parent, id=id) - self.nb = aui.AuiNotebook(self) - sizer = wx.BoxSizer() - sizer.Add(self.nb, 1, wx.EXPAND) - self.SetSizer(sizer) - - def add(self, name="plot"): - page = Plot(self.nb) - self.nb.AddPage(page, name) - return page.figure - - -def demo(): - # alternatively you could use - #app = wx.App() - # InspectableApp is a great debug tool, see: - # http://wiki.wxpython.org/Widget%20Inspection%20Tool - app = wit.InspectableApp() - frame = wx.Frame(None, -1, 'Plotter') - plotter = PlotNotebook(frame) - axes1 = plotter.add('figure 1').gca() - axes1.plot([1, 2, 3], [2, 1, 4]) - axes2 = plotter.add('figure 2').gca() - axes2.plot([1, 2, 3, 4, 5], [2, 1, 4, 2, 3]) - frame.Show() - app.MainLoop() - -if __name__ == "__main__": - demo() diff --git a/examples/user_interfaces/embedding_webagg.py b/examples/user_interfaces/embedding_webagg.py deleted file mode 100644 index ba208fd0c077..000000000000 --- a/examples/user_interfaces/embedding_webagg.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -This example demonstrates how to embed matplotlib WebAgg interactive -plotting in your own web application and framework. It is not -necessary to do all this if you merely want to display a plot in a -browser or use matplotlib's built-in Tornado-based server "on the -side". - -The framework being used must support web sockets. -""" - -import io - -try: - import tornado -except ImportError: - raise RuntimeError("This example requires tornado.") -import tornado.web -import tornado.httpserver -import tornado.ioloop -import tornado.websocket - - -from matplotlib.backends.backend_webagg_core import ( - FigureManagerWebAgg, new_figure_manager_given_figure) -from matplotlib.figure import Figure - -import numpy as np - -import json - - -def create_figure(): - """ - Creates a simple example figure. - """ - fig = Figure() - a = fig.add_subplot(111) - t = np.arange(0.0, 3.0, 0.01) - s = np.sin(2 * np.pi * t) - a.plot(t, s) - return fig - - -# The following is the content of the web page. You would normally -# generate this using some sort of template facility in your web -# framework, but here we just use Python string formatting. -html_content = """ - - - - - - - - - - - - - - matplotlib - - - -
    -
    - - -""" - - -class MyApplication(tornado.web.Application): - class MainPage(tornado.web.RequestHandler): - """ - Serves the main HTML page. - """ - - def get(self): - manager = self.application.manager - ws_uri = "ws://{req.host}/".format(req=self.request) - content = html_content % { - "ws_uri": ws_uri, "fig_id": manager.num} - self.write(content) - - class MplJs(tornado.web.RequestHandler): - """ - Serves the generated matplotlib javascript file. The content - is dynamically generated based on which toolbar functions the - user has defined. Call `FigureManagerWebAgg` to get its - content. - """ - - def get(self): - self.set_header('Content-Type', 'application/javascript') - js_content = FigureManagerWebAgg.get_javascript() - - self.write(js_content) - - class Download(tornado.web.RequestHandler): - """ - Handles downloading of the figure in various file formats. - """ - - def get(self, fmt): - manager = self.application.manager - - mimetypes = { - 'ps': 'application/postscript', - 'eps': 'application/postscript', - 'pdf': 'application/pdf', - 'svg': 'image/svg+xml', - 'png': 'image/png', - 'jpeg': 'image/jpeg', - 'tif': 'image/tiff', - 'emf': 'application/emf' - } - - self.set_header('Content-Type', mimetypes.get(fmt, 'binary')) - - buff = io.BytesIO() - manager.canvas.print_figure(buff, format=fmt) - self.write(buff.getvalue()) - - class WebSocket(tornado.websocket.WebSocketHandler): - """ - A websocket for interactive communication between the plot in - the browser and the server. - - In addition to the methods required by tornado, it is required to - have two callback methods: - - - ``send_json(json_content)`` is called by matplotlib when - it needs to send json to the browser. `json_content` is - a JSON tree (Python dictionary), and it is the responsibility - of this implementation to encode it as a string to send over - the socket. - - - ``send_binary(blob)`` is called to send binary image data - to the browser. - """ - supports_binary = True - - def open(self): - # Register the websocket with the FigureManager. - manager = self.application.manager - manager.add_web_socket(self) - if hasattr(self, 'set_nodelay'): - self.set_nodelay(True) - - def on_close(self): - # When the socket is closed, deregister the websocket with - # the FigureManager. - manager = self.application.manager - manager.remove_web_socket(self) - - def on_message(self, message): - # The 'supports_binary' message is relevant to the - # websocket itself. The other messages get passed along - # to matplotlib as-is. - - # Every message has a "type" and a "figure_id". - message = json.loads(message) - if message['type'] == 'supports_binary': - self.supports_binary = message['value'] - else: - manager = self.application.manager - manager.handle_json(message) - - def send_json(self, content): - self.write_message(json.dumps(content)) - - def send_binary(self, blob): - if self.supports_binary: - self.write_message(blob, binary=True) - else: - data_uri = "data:image/png;base64,{0}".format( - blob.encode('base64').replace('\n', '')) - self.write_message(data_uri) - - def __init__(self, figure): - self.figure = figure - self.manager = new_figure_manager_given_figure( - id(figure), figure) - - super(MyApplication, self).__init__([ - # Static files for the CSS and JS - (r'/_static/(.*)', - tornado.web.StaticFileHandler, - {'path': FigureManagerWebAgg.get_static_file_path()}), - - # The page that contains all of the pieces - ('/', self.MainPage), - - ('/mpl.js', self.MplJs), - - # Sends images and events to the browser, and receives - # events from the browser - ('/ws', self.WebSocket), - - # Handles the downloading (i.e., saving) of static images - (r'/download.([a-z0-9.]+)', self.Download), - ]) - - -if __name__ == "__main__": - figure = create_figure() - application = MyApplication(figure) - - http_server = tornado.httpserver.HTTPServer(application) - http_server.listen(8080) - - print("http://127.0.0.1:8080/") - print("Press Ctrl+C to quit") - - tornado.ioloop.IOLoop.instance().start() diff --git a/examples/user_interfaces/fourier_demo_wx.py b/examples/user_interfaces/fourier_demo_wx.py deleted file mode 100644 index d07b07bb3882..000000000000 --- a/examples/user_interfaces/fourier_demo_wx.py +++ /dev/null @@ -1,245 +0,0 @@ -import numpy as np - -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -import wx -import matplotlib -matplotlib.interactive(False) -matplotlib.use('WXAgg') -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg -from matplotlib.figure import Figure -from matplotlib.pyplot import gcf, setp - - -class Knob(object): - """ - Knob - simple class with a "setKnob" method. - A Knob instance is attached to a Param instance, e.g., param.attach(knob) - Base class is for documentation purposes. - """ - - def setKnob(self, value): - pass - - -class Param(object): - """ - The idea of the "Param" class is that some parameter in the GUI may have - several knobs that both control it and reflect the parameter's state, e.g. - a slider, text, and dragging can all change the value of the frequency in - the waveform of this example. - The class allows a cleaner way to update/"feedback" to the other knobs when - one is being changed. Also, this class handles min/max constraints for all - the knobs. - Idea - knob list - in "set" method, knob object is passed as well - - the other knobs in the knob list have a "set" method which gets - called for the others. - """ - - def __init__(self, initialValue=None, minimum=0., maximum=1.): - self.minimum = minimum - self.maximum = maximum - if initialValue != self.constrain(initialValue): - raise ValueError('illegal initial value') - self.value = initialValue - self.knobs = [] - - def attach(self, knob): - self.knobs += [knob] - - def set(self, value, knob=None): - self.value = value - self.value = self.constrain(value) - for feedbackKnob in self.knobs: - if feedbackKnob != knob: - feedbackKnob.setKnob(self.value) - return self.value - - def constrain(self, value): - if value <= self.minimum: - value = self.minimum - if value >= self.maximum: - value = self.maximum - return value - - -class SliderGroup(Knob): - def __init__(self, parent, label, param): - self.sliderLabel = wx.StaticText(parent, label=label) - self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER) - self.slider = wx.Slider(parent, -1) - # self.slider.SetMax(param.maximum*1000) - self.slider.SetRange(0, param.maximum * 1000) - self.setKnob(param.value) - - sizer = wx.BoxSizer(wx.HORIZONTAL) - sizer.Add(self.sliderLabel, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, - border=2) - sizer.Add(self.sliderText, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, - border=2) - sizer.Add(self.slider, 1, wx.EXPAND) - self.sizer = sizer - - self.slider.Bind(wx.EVT_SLIDER, self.sliderHandler) - self.sliderText.Bind(wx.EVT_TEXT_ENTER, self.sliderTextHandler) - - self.param = param - self.param.attach(self) - - def sliderHandler(self, evt): - value = evt.GetInt() / 1000. - self.param.set(value) - - def sliderTextHandler(self, evt): - value = float(self.sliderText.GetValue()) - self.param.set(value) - - def setKnob(self, value): - self.sliderText.SetValue('%g' % value) - self.slider.SetValue(value * 1000) - - -class FourierDemoFrame(wx.Frame): - def __init__(self, *args, **kwargs): - wx.Frame.__init__(self, *args, **kwargs) - - self.fourierDemoWindow = FourierDemoWindow(self) - self.frequencySliderGroup = SliderGroup( - self, - label='Frequency f0:', - param=self.fourierDemoWindow.f0) - self.amplitudeSliderGroup = SliderGroup(self, label=' Amplitude a:', - param=self.fourierDemoWindow.A) - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.fourierDemoWindow, 1, wx.EXPAND) - sizer.Add(self.frequencySliderGroup.sizer, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) - sizer.Add(self.amplitudeSliderGroup.sizer, 0, - wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) - self.SetSizer(sizer) - - -class FourierDemoWindow(wx.Window, Knob): - def __init__(self, *args, **kwargs): - wx.Window.__init__(self, *args, **kwargs) - self.lines = [] - self.figure = Figure() - self.canvas = FigureCanvasWxAgg(self, -1, self.figure) - self.canvas.callbacks.connect('button_press_event', self.mouseDown) - self.canvas.callbacks.connect('motion_notify_event', self.mouseMotion) - self.canvas.callbacks.connect('button_release_event', self.mouseUp) - self.state = '' - self.mouseInfo = (None, None, None, None) - self.f0 = Param(2., minimum=0., maximum=6.) - self.A = Param(1., minimum=0.01, maximum=2.) - self.draw() - - # Not sure I like having two params attached to the same Knob, - # but that is what we have here... it works but feels kludgy - - # although maybe it's not too bad since the knob changes both params - # at the same time (both f0 and A are affected during a drag) - self.f0.attach(self) - self.A.attach(self) - self.Bind(wx.EVT_SIZE, self.sizeHandler) - - self.Bind(wx.EVT_PAINT, self.OnPaint) - - def OnPaint(self, event): - self.canvas.draw() - event.Skip() - - def sizeHandler(self, *args, **kwargs): - self.canvas.SetSize(self.GetSize()) - - def mouseDown(self, evt): - if self.lines[0] in self.figure.hitlist(evt): - self.state = 'frequency' - elif self.lines[1] in self.figure.hitlist(evt): - self.state = 'time' - else: - self.state = '' - self.mouseInfo = (evt.xdata, evt.ydata, - max(self.f0.value, .1), - self.A.value) - - def mouseMotion(self, evt): - if self.state == '': - return - x, y = evt.xdata, evt.ydata - if x is None: # outside the axes - return - x0, y0, f0Init, AInit = self.mouseInfo - self.A.set(AInit + (AInit * (y - y0) / y0), self) - if self.state == 'frequency': - self.f0.set(f0Init + (f0Init * (x - x0) / x0)) - elif self.state == 'time': - if (x - x0) / x0 != -1.: - self.f0.set(1. / (1. / f0Init + (1. / f0Init * (x - x0) / x0))) - - def mouseUp(self, evt): - self.state = '' - - def draw(self): - if not hasattr(self, 'subplot1'): - self.subplot1 = self.figure.add_subplot(211) - self.subplot2 = self.figure.add_subplot(212) - x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value) - color = (1., 0., 0.) - self.lines += self.subplot1.plot(x1, y1, color=color, linewidth=2) - self.lines += self.subplot2.plot(x2, y2, color=color, linewidth=2) - # Set some plot attributes - self.subplot1.set_title( - "Click and drag waveforms to change frequency and amplitude", - fontsize=12) - self.subplot1.set_ylabel("Frequency Domain Waveform X(f)", fontsize=8) - self.subplot1.set_xlabel("frequency f", fontsize=8) - self.subplot2.set_ylabel("Time Domain Waveform x(t)", fontsize=8) - self.subplot2.set_xlabel("time t", fontsize=8) - self.subplot1.set_xlim([-6, 6]) - self.subplot1.set_ylim([0, 1]) - self.subplot2.set_xlim([-2, 2]) - self.subplot2.set_ylim([-2, 2]) - self.subplot1.text(0.05, .95, - r'$X(f) = \mathcal{F}\{x(t)\}$', - verticalalignment='top', - transform=self.subplot1.transAxes) - self.subplot2.text(0.05, .95, - r'$x(t) = a \cdot \cos(2\pi f_0 t) e^{-\pi t^2}$', - verticalalignment='top', - transform=self.subplot2.transAxes) - - def compute(self, f0, A): - f = np.arange(-6., 6., 0.02) - t = np.arange(-2., 2., 0.01) - x = A * np.cos(2 * np.pi * f0 * t) * np.exp(-np.pi * t ** 2) - X = A / 2 * \ - (np.exp(-np.pi * (f - f0) ** 2) + np.exp(-np.pi * (f + f0) ** 2)) - return f, X, t, x - - def repaint(self): - self.canvas.draw() - - def setKnob(self, value): - # Note, we ignore value arg here and just go by state of the params - x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value) - setp(self.lines[0], xdata=x1, ydata=y1) - setp(self.lines[1], xdata=x2, ydata=y2) - self.repaint() - - -class App(wx.App): - def OnInit(self): - self.frame1 = FourierDemoFrame(parent=None, title="Fourier Demo", - size=(640, 480)) - self.frame1.Show() - return True - -app = App() -app.MainLoop() diff --git a/examples/user_interfaces/gtk_spreadsheet.py b/examples/user_interfaces/gtk_spreadsheet.py deleted file mode 100644 index 85798b0ca691..000000000000 --- a/examples/user_interfaces/gtk_spreadsheet.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Example of embedding matplotlib in an application and interacting with -a treeview to store data. Double click on an entry to update plot -data - -""" -import pygtk -pygtk.require('2.0') -import gtk -from gtk import gdk - -import matplotlib -matplotlib.use('GTKAgg') # or 'GTK' -from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas - -from numpy.random import random -from matplotlib.figure import Figure - - -class DataManager(gtk.Window): - numRows, numCols = 20, 10 - - data = random((numRows, numCols)) - - def __init__(self): - gtk.Window.__init__(self) - self.set_default_size(600, 600) - self.connect('destroy', lambda win: gtk.main_quit()) - - self.set_title('GtkListStore demo') - self.set_border_width(8) - - vbox = gtk.VBox(False, 8) - self.add(vbox) - - label = gtk.Label('Double click a row to plot the data') - - vbox.pack_start(label, False, False) - - sw = gtk.ScrolledWindow() - sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) - sw.set_policy(gtk.POLICY_NEVER, - gtk.POLICY_AUTOMATIC) - vbox.pack_start(sw, True, True) - - model = self.create_model() - - self.treeview = gtk.TreeView(model) - self.treeview.set_rules_hint(True) - - # matplotlib stuff - fig = Figure(figsize=(6, 4)) - - self.canvas = FigureCanvas(fig) # a gtk.DrawingArea - vbox.pack_start(self.canvas, True, True) - ax = fig.add_subplot(111) - self.line, = ax.plot(self.data[0, :], 'go') # plot the first row - - self.treeview.connect('row-activated', self.plot_row) - sw.add(self.treeview) - - self.add_columns() - - self.add_events(gdk.BUTTON_PRESS_MASK | - gdk.KEY_PRESS_MASK | - gdk.KEY_RELEASE_MASK) - - def plot_row(self, treeview, path, view_column): - ind, = path # get the index into data - points = self.data[ind, :] - self.line.set_ydata(points) - self.canvas.draw() - - def add_columns(self): - for i in range(self.numCols): - column = gtk.TreeViewColumn('%d' % i, gtk.CellRendererText(), text=i) - self.treeview.append_column(column) - - def create_model(self): - types = [float]*self.numCols - store = gtk.ListStore(*types) - - for row in self.data: - store.append(row) - return store - - -manager = DataManager() -manager.show_all() -gtk.main() diff --git a/examples/user_interfaces/histogram_demo_canvasagg.py b/examples/user_interfaces/histogram_demo_canvasagg.py deleted file mode 100644 index 11a13ba12185..000000000000 --- a/examples/user_interfaces/histogram_demo_canvasagg.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -This is an example that shows you how to work directly with the agg -figure canvas to create a figure using the pythonic API. - -In this example, the contents of the agg canvas are extracted to a -string, which can in turn be passed off to PIL or put in a numeric -array - - -""" -from matplotlib.backends.backend_agg import FigureCanvasAgg -from matplotlib.figure import Figure -from matplotlib.mlab import normpdf -from numpy.random import randn -import numpy - -fig = Figure(figsize=(5, 4), dpi=100) -ax = fig.add_subplot(111) - -canvas = FigureCanvasAgg(fig) - -mu, sigma = 100, 15 -x = mu + sigma*randn(10000) - -# the histogram of the data -n, bins, patches = ax.hist(x, 50, normed=1) - -# add a 'best fit' line -y = normpdf(bins, mu, sigma) -line, = ax.plot(bins, y, 'r--') -line.set_linewidth(1) - -ax.set_xlabel('Smarts') -ax.set_ylabel('Probability') -ax.set_title(r'$\mathrm{Histogram of IQ: }\mu=100, \sigma=15$') - -ax.set_xlim((40, 160)) -ax.set_ylim((0, 0.03)) - -canvas.draw() - -s = canvas.tostring_rgb() # save this and convert to bitmap as needed - -# get the figure dimensions for creating bitmaps or numpy arrays, -# etc. -l, b, w, h = fig.bbox.bounds -w, h = int(w), int(h) - -if 0: - # convert to a numpy array - X = numpy.fromstring(s, numpy.uint8) - X.shape = h, w, 3 - -if 0: - # pass off to PIL - from PIL import Image - im = Image.fromstring("RGB", (w, h), s) - im.show() diff --git a/examples/user_interfaces/interactive.py b/examples/user_interfaces/interactive.py deleted file mode 100755 index adc54264d76c..000000000000 --- a/examples/user_interfaces/interactive.py +++ /dev/null @@ -1,243 +0,0 @@ -"""Multithreaded interactive interpreter with GTK and Matplotlib support. - -WARNING: -As of 2010/06/25, this is not working, at least on Linux. -I have disabled it as a runnable script. - EF - - -Usage: - - pyint-gtk.py -> starts shell with gtk thread running separately - - pyint-gtk.py -pylab [filename] -> initializes matplotlib, optionally running - the named file. The shell starts after the file is executed. - -Threading code taken from: -http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian -McErlean and John Finlay. - -Matplotlib support taken from interactive.py in the matplotlib distribution. - -Also borrows liberally from code.py in the Python standard library.""" - -from __future__ import print_function - -__author__ = "Fernando Perez " - -import sys -import code -import threading - -import gobject -import gtk - -try: - import readline -except ImportError: - has_readline = False -else: - has_readline = True - - -class MTConsole(code.InteractiveConsole): - """Simple multi-threaded shell""" - - def __init__(self, on_kill=None, *args, **kw): - code.InteractiveConsole.__init__(self, *args, **kw) - self.code_to_run = None - self.ready = threading.Condition() - self._kill = False - if on_kill is None: - on_kill = [] - # Check that all things to kill are callable: - for _ in on_kill: - if not callable(_): - raise TypeError('on_kill must be a list of callables') - self.on_kill = on_kill - # Set up tab-completer - if has_readline: - import rlcompleter - try: # this form only works with python 2.3 - self.completer = rlcompleter.Completer(self.locals) - except: # simpler for py2.2 - self.completer = rlcompleter.Completer() - - readline.set_completer(self.completer.complete) - # Use tab for completions - readline.parse_and_bind('tab: complete') - # This forces readline to automatically print the above list when tab - # completion is set to 'complete'. - readline.parse_and_bind('set show-all-if-ambiguous on') - # Bindings for incremental searches in the history. These searches - # use the string typed so far on the command line and search - # anything in the previous input history containing them. - readline.parse_and_bind('"\C-r": reverse-search-history') - readline.parse_and_bind('"\C-s": forward-search-history') - - def runsource(self, source, filename="", symbol="single"): - """Compile and run some source in the interpreter. - - Arguments are as for compile_command(). - - One several things can happen: - - 1) The input is incorrect; compile_command() raised an - exception (SyntaxError or OverflowError). A syntax traceback - will be printed by calling the showsyntaxerror() method. - - 2) The input is incomplete, and more input is required; - compile_command() returned None. Nothing happens. - - 3) The input is complete; compile_command() returned a code - object. The code is executed by calling self.runcode() (which - also handles run-time exceptions, except for SystemExit). - - The return value is True in case 2, False in the other cases (unless - an exception is raised). The return value can be used to - decide whether to use sys.ps1 or sys.ps2 to prompt the next - line. - """ - try: - code = self.compile(source, filename, symbol) - except (OverflowError, SyntaxError, ValueError): - # Case 1 - self.showsyntaxerror(filename) - return False - - if code is None: - # Case 2 - return True - - # Case 3 - # Store code in self, so the execution thread can handle it - self.ready.acquire() - self.code_to_run = code - self.ready.wait() # Wait until processed in timeout interval - self.ready.release() - - return False - - def runcode(self): - """Execute a code object. - - When an exception occurs, self.showtraceback() is called to display a - traceback.""" - - self.ready.acquire() - if self._kill: - print('Closing threads...') - sys.stdout.flush() - for tokill in self.on_kill: - tokill() - print('Done.') - - if self.code_to_run is not None: - self.ready.notify() - code.InteractiveConsole.runcode(self, self.code_to_run) - - self.code_to_run = None - self.ready.release() - return True - - def kill(self): - """Kill the thread, returning when it has been shut down.""" - self.ready.acquire() - self._kill = True - self.ready.release() - - -class GTKInterpreter(threading.Thread): - """Run gtk.main in the main thread and a python interpreter in a - separate thread. - Python commands can be passed to the thread where they will be executed. - This is implemented by periodically checking for passed code using a - GTK timeout callback. - """ - TIMEOUT = 100 # Millisecond interval between timeouts. - - def __init__(self, banner=None): - threading.Thread.__init__(self) - self.banner = banner - self.shell = MTConsole(on_kill=[gtk.main_quit]) - - def run(self): - self.pre_interact() - self.shell.interact(self.banner) - self.shell.kill() - - def mainloop(self): - self.start() - gobject.timeout_add(self.TIMEOUT, self.shell.runcode) - try: - if gtk.gtk_version[0] >= 2: - gtk.gdk.threads_init() - except AttributeError: - pass - gtk.main() - self.join() - - def pre_interact(self): - """This method should be overridden by subclasses. - - It gets called right before interact(), but after the thread starts. - Typically used to push initialization code into the interpreter""" - - pass - - -class MatplotLibInterpreter(GTKInterpreter): - """Threaded interpreter with matplotlib support. - - Note that this explicitly sets GTKAgg as the backend, since it has - specific GTK hooks in it.""" - - def __init__(self, banner=None): - banner = """\nWelcome to matplotlib, a MATLAB-like python environment. - help(matlab) -> help on matlab compatible commands from matplotlib. - help(plotting) -> help on plotting commands. - """ - GTKInterpreter.__init__(self, banner) - - def pre_interact(self): - """Initialize matplotlib before user interaction begins""" - - push = self.shell.push - # Code to execute in user's namespace - lines = ["import matplotlib", - "matplotlib.use('GTKAgg')", - "matplotlib.interactive(1)", - "import matplotlib.pylab as pylab", - "from matplotlib.pylab import *\n"] - - map(push, lines) - - # Execute file if given. - if len(sys.argv) > 1: - import matplotlib - matplotlib.interactive(0) # turn off interaction - fname = sys.argv[1] - try: - inFile = file(fname, 'r') - except IOError: - print('*** ERROR *** Could not read file <%s>' % fname) - else: - print('*** Executing file <%s>:' % fname) - for line in inFile: - if line.lstrip().find('show()') == 0: - continue - print('>>', line) - push(line) - inFile.close() - matplotlib.interactive(1) # turn on interaction - -if __name__ == '__main__': - print("This demo is not presently functional, so running") - print("it as a script has been disabled.") - sys.exit() - # Quick sys.argv hack to extract the option and leave filenames in sys.argv. - # For real option handling, use optparse or getopt. - if len(sys.argv) > 1 and sys.argv[1] == '-pylab': - sys.argv = [sys.argv[0]] + sys.argv[2:] - MatplotLibInterpreter().mainloop() - else: - GTKInterpreter().mainloop() diff --git a/examples/user_interfaces/interactive2.py b/examples/user_interfaces/interactive2.py deleted file mode 100755 index a6e7b700a113..000000000000 --- a/examples/user_interfaces/interactive2.py +++ /dev/null @@ -1,381 +0,0 @@ -from __future__ import print_function - -# GTK Interactive Console -# (C) 2003, Jon Anderson -# See www.python.org/2.2/license.html for -# license details. -# -import gtk -import gtk.gdk - -import code -import os -import sys -import pango - -import __builtin__ -import __main__ - - -banner = """GTK Interactive Python Console -Thanks to Jon Anderson -%s -""" % sys.version - -banner += """ - -Welcome to matplotlib. - - help(matplotlib) -- some general information about matplotlib - help(plotting) -- shows a list of plot specific commands - -""" - - -class Completer(object): - """ - Taken from rlcompleter, with readline references stripped, and a local dictionary to use. - """ - - def __init__(self, locals): - self.locals = locals - - def complete(self, text, state): - """Return the next possible completion for 'text'. - This is called successively with state == 0, 1, 2, ... until it - returns None. The completion should begin with 'text'. - - """ - if state == 0: - if "." in text: - self.matches = self.attr_matches(text) - else: - self.matches = self.global_matches(text) - try: - return self.matches[state] - except IndexError: - return None - - def global_matches(self, text): - """Compute matches when text is a simple name. - - Return a list of all keywords, built-in functions and names - currently defines in __main__ that match. - - """ - import keyword - matches = [] - n = len(text) - for list in [keyword.kwlist, __builtin__.__dict__.keys(), __main__.__dict__.keys(), self.locals.keys()]: - for word in list: - if word[:n] == text and word != "__builtins__": - matches.append(word) - return matches - - def attr_matches(self, text): - """Compute matches when text contains a dot. - - Assuming the text is of the form NAME.NAME....[NAME], and is - evaluatable in the globals of __main__, it will be evaluated - and its attributes (as revealed by dir()) are used as possible - completions. (For class instances, class members are are also - considered.) - - WARNING: this can still invoke arbitrary C code, if an object - with a __getattr__ hook is evaluated. - - """ - import re - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) - if not m: - return - expr, attr = m.group(1, 3) - object = eval(expr, __main__.__dict__, self.locals) - words = dir(object) - if hasattr(object, '__class__'): - words.append('__class__') - words = words + get_class_members(object.__class__) - matches = [] - n = len(attr) - for word in words: - if word[:n] == attr and word != "__builtins__": - matches.append("%s.%s" % (expr, word)) - return matches - - -def get_class_members(klass): - ret = dir(klass) - if hasattr(klass, '__bases__'): - for base in klass.__bases__: - ret = ret + get_class_members(base) - return ret - - -class OutputStream(object): - """ - A Multiplexing output stream. - It can replace another stream, and tee output to the original stream and too - a GTK textview. - """ - - def __init__(self, view, old_out, style): - self.view = view - self.buffer = view.get_buffer() - self.mark = self.buffer.create_mark("End", self.buffer.get_end_iter(), False) - self.out = old_out - self.style = style - self.tee = 1 - - def write(self, text): - if self.tee: - self.out.write(text) - - end = self.buffer.get_end_iter() - - if self.view is not None: - self.view.scroll_to_mark(self.mark, 0, True, 1, 1) - - self.buffer.insert_with_tags(end, text, self.style) - - -class GTKInterpreterConsole(gtk.ScrolledWindow): - """ - An InteractiveConsole for GTK. It's an actual widget, - so it can be dropped in just about anywhere. - """ - - def __init__(self): - gtk.ScrolledWindow.__init__(self) - self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - - self.text = gtk.TextView() - self.text.set_wrap_mode(True) - - self.interpreter = code.InteractiveInterpreter() - - self.completer = Completer(self.interpreter.locals) - self.buffer = [] - self.history = [] - self.banner = banner - self.ps1 = ">>> " - self.ps2 = "... " - - self.text.add_events(gtk.gdk.KEY_PRESS_MASK) - self.text.connect("key_press_event", self.key_pressed) - - self.current_history = -1 - - self.mark = self.text.get_buffer().create_mark("End", self.text.get_buffer().get_end_iter(), False) - - # setup colors - self.style_banner = gtk.TextTag("banner") - self.style_banner.set_property("foreground", "saddle brown") - - self.style_ps1 = gtk.TextTag("ps1") - self.style_ps1.set_property("foreground", "DarkOrchid4") - self.style_ps1.set_property("editable", False) - self.style_ps1.set_property("font", "courier") - - self.style_ps2 = gtk.TextTag("ps2") - self.style_ps2.set_property("foreground", "DarkOliveGreen") - self.style_ps2.set_property("editable", False) - self.style_ps2.set_property("font", "courier") - - self.style_out = gtk.TextTag("stdout") - self.style_out.set_property("foreground", "midnight blue") - self.style_err = gtk.TextTag("stderr") - self.style_err.set_property("style", pango.STYLE_ITALIC) - self.style_err.set_property("foreground", "red") - - self.text.get_buffer().get_tag_table().add(self.style_banner) - self.text.get_buffer().get_tag_table().add(self.style_ps1) - self.text.get_buffer().get_tag_table().add(self.style_ps2) - self.text.get_buffer().get_tag_table().add(self.style_out) - self.text.get_buffer().get_tag_table().add(self.style_err) - - self.stdout = OutputStream(self.text, sys.stdout, self.style_out) - self.stderr = OutputStream(self.text, sys.stderr, self.style_err) - - sys.stderr = self.stderr - sys.stdout = self.stdout - - self.current_prompt = None - - self.write_line(self.banner, self.style_banner) - self.prompt_ps1() - - self.add(self.text) - self.text.show() - - def reset_history(self): - self.history = [] - - def reset_buffer(self): - self.buffer = [] - - def prompt_ps1(self): - self.current_prompt = self.prompt_ps1 - self.write_line(self.ps1, self.style_ps1) - - def prompt_ps2(self): - self.current_prompt = self.prompt_ps2 - self.write_line(self.ps2, self.style_ps2) - - def write_line(self, text, style=None): - start, end = self.text.get_buffer().get_bounds() - if style is None: - self.text.get_buffer().insert(end, text) - else: - self.text.get_buffer().insert_with_tags(end, text, style) - - self.text.scroll_to_mark(self.mark, 0, True, 1, 1) - - def push(self, line): - - self.buffer.append(line) - if len(line) > 0: - self.history.append(line) - - source = "\n".join(self.buffer) - - more = self.interpreter.runsource(source, "<>") - - if not more: - self.reset_buffer() - - return more - - def key_pressed(self, widget, event): - if event.keyval == gtk.gdk.keyval_from_name('Return'): - return self.execute_line() - - if event.keyval == gtk.gdk.keyval_from_name('Up'): - self.current_history = self.current_history - 1 - if self.current_history < - len(self.history): - self.current_history = - len(self.history) - return self.show_history() - elif event.keyval == gtk.gdk.keyval_from_name('Down'): - self.current_history = self.current_history + 1 - if self.current_history > 0: - self.current_history = 0 - return self.show_history() - elif event.keyval == gtk.gdk.keyval_from_name('Home'): - l = self.text.get_buffer().get_line_count() - 1 - start = self.text.get_buffer().get_iter_at_line_offset(l, 4) - self.text.get_buffer().place_cursor(start) - return True - elif event.keyval == gtk.gdk.keyval_from_name('space') and event.state & gtk.gdk.CONTROL_MASK: - return self.complete_line() - return False - - def show_history(self): - if self.current_history == 0: - return True - else: - self.replace_line(self.history[self.current_history]) - return True - - def current_line(self): - start, end = self.current_line_bounds() - return self.text.get_buffer().get_text(start, end, True) - - def current_line_bounds(self): - txt_buffer = self.text.get_buffer() - l = txt_buffer.get_line_count() - 1 - - start = txt_buffer.get_iter_at_line(l) - if start.get_chars_in_line() >= 4: - start.forward_chars(4) - end = txt_buffer.get_end_iter() - return start, end - - def replace_line(self, txt): - start, end = self.current_line_bounds() - self.text.get_buffer().delete(start, end) - self.write_line(txt) - - def execute_line(self, line=None): - if line is None: - line = self.current_line() - self.write_line("\n") - else: - self.write_line(line + "\n") - - more = self.push(line) - - self.text.get_buffer().place_cursor(self.text.get_buffer().get_end_iter()) - - if more: - self.prompt_ps2() - else: - self.prompt_ps1() - - self.current_history = 0 - - self.window.raise_() - - return True - - def complete_line(self): - line = self.current_line() - tokens = line.split() - token = tokens[-1] - - completions = [] - p = self.completer.complete(token, len(completions)) - while p is not None: - completions.append(p) - p = self.completer.complete(token, len(completions)) - - if len(completions) != 1: - self.write_line("\n") - self.write_line("\n".join(completions), self.style_ps1) - self.write_line("\n") - self.current_prompt() - self.write_line(line) - else: - i = line.rfind(token) - line = line[0:i] + completions[0] - self.replace_line(line) - - return True - - -def main(): - w = gtk.Window() - console = GTKInterpreterConsole() - console.set_size_request(640, 480) - w.add(console) - - def destroy(arg=None): - gtk.main_quit() - - def key_event(widget, event): - if gtk.gdk.keyval_name(event.keyval) == 'd' and \ - event.state & gtk.gdk.CONTROL_MASK: - destroy() - return False - - w.connect("destroy", destroy) - - w.add_events(gtk.gdk.KEY_PRESS_MASK) - w.connect('key_press_event', key_event) - w.show_all() - - console.execute_line('import matplotlib') - console.execute_line("matplotlib.use('GTKAgg')") - console.execute_line('matplotlib.interactive(1)') - console.execute_line('from pylab import *') - - if len(sys.argv) > 1: - fname = sys.argv[1] - if not os.path.exists(fname): - print('%s does not exist' % fname) - for line in file(fname): - line = line.strip() - - console.execute_line(line) - gtk.main() - -if __name__ == '__main__': - main() diff --git a/examples/user_interfaces/lineprops_dialog_gtk.py b/examples/user_interfaces/lineprops_dialog_gtk.py deleted file mode 100644 index 49e864071969..000000000000 --- a/examples/user_interfaces/lineprops_dialog_gtk.py +++ /dev/null @@ -1,24 +0,0 @@ -import matplotlib -matplotlib.use('GTKAgg') -from matplotlib.backends.backend_gtk import DialogLineprops - -import numpy as np -import matplotlib.pyplot as plt - - -def f(t): - s1 = np.cos(2*np.pi*t) - e1 = np.exp(-t) - return np.multiply(s1, e1) - -t1 = np.arange(0.0, 5.0, 0.1) -t2 = np.arange(0.0, 5.0, 0.02) -t3 = np.arange(0.0, 2.0, 0.01) - -fig, ax = plt.subplots() -l1, = ax.plot(t1, f(t1), 'bo', label='line 1') -l2, = ax.plot(t2, f(t2), 'k--', label='line 2') - -dlg = DialogLineprops([l1, l2]) -dlg.show() -plt.show() diff --git a/examples/user_interfaces/mathtext_wx.py b/examples/user_interfaces/mathtext_wx.py deleted file mode 100644 index 24ad8db59a0d..000000000000 --- a/examples/user_interfaces/mathtext_wx.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Demonstrates how to convert mathtext to a wx.Bitmap for display in various -controls on wxPython. -""" - -import matplotlib -matplotlib.use("WxAgg") -from numpy import arange, sin, pi, cos, log -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas -from matplotlib.backends.backend_wx import NavigationToolbar2Wx, wxc -from matplotlib.figure import Figure - -import wx - -IS_GTK = 'wxGTK' in wx.PlatformInfo -IS_WIN = 'wxMSW' in wx.PlatformInfo - -############################################################ -# This is where the "magic" happens. -from matplotlib.mathtext import MathTextParser -mathtext_parser = MathTextParser("Bitmap") - - -def mathtext_to_wxbitmap(s): - ftimage, depth = mathtext_parser.parse(s, 150) - return wxc.BitmapFromBuffer( - ftimage.get_width(), ftimage.get_height(), - ftimage.as_rgba_str()) -############################################################ - -functions = [ - (r'$\sin(2 \pi x)$', lambda x: sin(2*pi*x)), - (r'$\frac{4}{3}\pi x^3$', lambda x: (4.0/3.0)*pi*x**3), - (r'$\cos(2 \pi x)$', lambda x: cos(2*pi*x)), - (r'$\log(x)$', lambda x: log(x)) -] - - -class CanvasFrame(wx.Frame): - def __init__(self, parent, title): - wx.Frame.__init__(self, parent, -1, title, size=(550, 350)) - self.SetBackgroundColour(wxc.NamedColour("WHITE")) - - self.figure = Figure() - self.axes = self.figure.add_subplot(111) - - self.canvas = FigureCanvas(self, -1, self.figure) - - self.change_plot(0) - - self.sizer = wx.BoxSizer(wx.VERTICAL) - self.add_buttonbar() - self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) - self.add_toolbar() # comment this out for no toolbar - - menuBar = wx.MenuBar() - - # File Menu - menu = wx.Menu() - menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") - menuBar.Append(menu, "&File") - - if IS_GTK or IS_WIN: - # Equation Menu - menu = wx.Menu() - for i, (mt, func) in enumerate(functions): - bm = mathtext_to_wxbitmap(mt) - item = wx.MenuItem(menu, 1000 + i, " ") - item.SetBitmap(bm) - menu.AppendItem(item) - self.Bind(wx.EVT_MENU, self.OnChangePlot, item) - menuBar.Append(menu, "&Functions") - - self.SetMenuBar(menuBar) - - self.SetSizer(self.sizer) - self.Fit() - - def add_buttonbar(self): - self.button_bar = wx.Panel(self) - self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.sizer.Add(self.button_bar, 0, wx.LEFT | wx.TOP | wx.GROW) - - for i, (mt, func) in enumerate(functions): - bm = mathtext_to_wxbitmap(mt) - button = wx.BitmapButton(self.button_bar, 1000 + i, bm) - self.button_bar_sizer.Add(button, 1, wx.GROW) - self.Bind(wx.EVT_BUTTON, self.OnChangePlot, button) - - self.button_bar.SetSizer(self.button_bar_sizer) - - def add_toolbar(self): - """Copied verbatim from embedding_wx2.py""" - self.toolbar = NavigationToolbar2Wx(self.canvas) - self.toolbar.Realize() - # By adding toolbar in sizer, we are able to put it at the bottom - # of the frame - so appearance is closer to GTK version. - self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - # update the axes menu on the toolbar - self.toolbar.update() - - def OnChangePlot(self, event): - self.change_plot(event.GetId() - 1000) - - def change_plot(self, plot_number): - t = arange(1.0, 3.0, 0.01) - s = functions[plot_number][1](t) - self.axes.clear() - self.axes.plot(t, s) - self.canvas.draw() - - -class MyApp(wx.App): - def OnInit(self): - frame = CanvasFrame(None, "wxPython mathtext demo app") - self.SetTopWindow(frame) - frame.Show(True) - return True - -app = MyApp() -app.MainLoop() diff --git a/examples/user_interfaces/mpl_with_glade.glade b/examples/user_interfaces/mpl_with_glade.glade deleted file mode 100644 index 96e3278b490e..000000000000 --- a/examples/user_interfaces/mpl_with_glade.glade +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - True - window1 - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - - - - - - - 4 - True - False - 2 - - - - True - - - - True - _File - True - - - - - - - True - gtk-new - True - - - - - - - True - gtk-open - True - - - - - - - True - gtk-save - True - - - - - - - True - gtk-save-as - True - - - - - - - True - - - - - - True - gtk-quit - True - - - - - - - - - - - True - _Edit - True - - - - - - - True - gtk-cut - True - - - - - - - True - gtk-copy - True - - - - - - - True - gtk-paste - True - - - - - - - True - gtk-delete - True - - - - - - - - - - - True - _View - True - - - - - - - - - - - True - _Help - True - - - - - - - True - _About - True - - - - - - - - - - 0 - False - False - - - - - - - - - - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-dialog-info - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - Click Me! - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - 0 - False - False - - - - - - - diff --git a/examples/user_interfaces/mpl_with_glade.py b/examples/user_interfaces/mpl_with_glade.py deleted file mode 100755 index 340092ebeba3..000000000000 --- a/examples/user_interfaces/mpl_with_glade.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import print_function -import matplotlib -matplotlib.use('GTK') - -from matplotlib.figure import Figure -from matplotlib.axes import Subplot -from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas -from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar -from matplotlib.widgets import SpanSelector - -from numpy import arange, sin, pi -import gtk -import gtk.glade - - -def simple_msg(msg, parent=None, title=None): - dialog = gtk.MessageDialog( - parent=None, - type=gtk.MESSAGE_INFO, - buttons=gtk.BUTTONS_OK, - message_format=msg) - if parent is not None: - dialog.set_transient_for(parent) - if title is not None: - dialog.set_title(title) - dialog.show() - dialog.run() - dialog.destroy() - return None - - -class GladeHandlers(object): - def on_buttonClickMe_clicked(event): - simple_msg('Nothing to say, really', - parent=widgets['windowMain'], - title='Thanks!') - - -class WidgetsWrapper(object): - def __init__(self): - self.widgets = gtk.glade.XML('mpl_with_glade.glade') - self.widgets.signal_autoconnect(GladeHandlers.__dict__) - - self['windowMain'].connect('destroy', lambda x: gtk.main_quit()) - self['windowMain'].move(10, 10) - self.figure = Figure(figsize=(8, 6), dpi=72) - self.axis = self.figure.add_subplot(111) - - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - self.axis.plot(t, s) - self.axis.set_xlabel('time (s)') - self.axis.set_ylabel('voltage') - - self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea - self.canvas.show() - self.canvas.set_size_request(600, 400) - self.canvas.set_events( - gtk.gdk.BUTTON_PRESS_MASK | - gtk.gdk.KEY_PRESS_MASK | - gtk.gdk.KEY_RELEASE_MASK - ) - self.canvas.set_flags(gtk.HAS_FOCUS | gtk.CAN_FOCUS) - self.canvas.grab_focus() - - def keypress(widget, event): - print('key press') - - def buttonpress(widget, event): - print('button press') - - self.canvas.connect('key_press_event', keypress) - self.canvas.connect('button_press_event', buttonpress) - - def onselect(xmin, xmax): - print(xmin, xmax) - - span = SpanSelector(self.axis, onselect, 'horizontal', useblit=False, - rectprops=dict(alpha=0.5, facecolor='red')) - - self['vboxMain'].pack_start(self.canvas, True, True) - self['vboxMain'].show() - - # below is optional if you want the navigation toolbar - self.navToolbar = NavigationToolbar(self.canvas, self['windowMain']) - self.navToolbar.lastDir = '/var/tmp/' - self['vboxMain'].pack_start(self.navToolbar) - self.navToolbar.show() - - sep = gtk.HSeparator() - sep.show() - self['vboxMain'].pack_start(sep, True, True) - - self['vboxMain'].reorder_child(self['buttonClickMe'], -1) - - def __getitem__(self, key): - return self.widgets.get_widget(key) - -widgets = WidgetsWrapper() -gtk.main() diff --git a/examples/user_interfaces/mpl_with_glade_316.py b/examples/user_interfaces/mpl_with_glade_316.py deleted file mode 100644 index ca537b508de5..000000000000 --- a/examples/user_interfaces/mpl_with_glade_316.py +++ /dev/null @@ -1,40 +0,0 @@ -from gi.repository import Gtk - -from matplotlib.figure import Figure -from matplotlib.axes import Subplot -from numpy import arange, sin, pi -from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas - - -class Window1Signals(object): - def on_window1_destroy(self, widget): - Gtk.main_quit() - - -def main(): - builder = Gtk.Builder() - builder.add_objects_from_file("mpl_with_glade_316.glade", ("window1", "")) - builder.connect_signals(Window1Signals()) - window = builder.get_object("window1") - sw = builder.get_object("scrolledwindow1") - - # Start of Matplotlib specific code - figure = Figure(figsize=(8, 6), dpi=71) - axis = figure.add_subplot(111) - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - axis.plot(t, s) - - axis.set_xlabel('time [s]') - axis.set_ylabel('voltage [V]') - - canvas = FigureCanvas(figure) # a Gtk.DrawingArea - canvas.set_size_request(800, 600) - sw.add_with_viewport(canvas) - # End of Matplotlib specific code - - window.show_all() - Gtk.main() - -if __name__ == "__main__": - main() diff --git a/examples/user_interfaces/pylab_with_gtk.py b/examples/user_interfaces/pylab_with_gtk.py deleted file mode 100644 index 06479b03b697..000000000000 --- a/examples/user_interfaces/pylab_with_gtk.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -An example of how to use pylab to manage your figure windows, but -modify the GUI by accessing the underlying gtk widgets -""" -from __future__ import print_function -import matplotlib -matplotlib.use('GTKAgg') -import matplotlib.pyplot as plt - - -fig, ax = plt.subplots() -plt.plot([1, 2, 3], 'ro-', label='easy as 1 2 3') -plt.plot([1, 4, 9], 'gs--', label='easy as 1 2 3 squared') -plt.legend() - - -manager = plt.get_current_fig_manager() -# you can also access the window or vbox attributes this way -toolbar = manager.toolbar - -# now let's add a button to the toolbar -import gtk -next = 8 # where to insert this in the mpl toolbar -button = gtk.Button('Click me') -button.show() - - -def clicked(button): - print('hi mom') -button.connect('clicked', clicked) - -toolitem = gtk.ToolItem() -toolitem.show() -toolitem.set_tooltip( - toolbar.tooltips, - 'Click me for fun and profit') - -toolitem.add(button) -toolbar.insert(toolitem, next) -next += 1 - -# now let's add a widget to the vbox -label = gtk.Label() -label.set_markup('Drag mouse over axes for position') -label.show() -vbox = manager.vbox -vbox.pack_start(label, False, False) -vbox.reorder_child(manager.toolbar, -1) - - -def update(event): - if event.xdata is None: - label.set_markup('Drag mouse over axes for position') - else: - label.set_markup('x,y=(%f, %f)' % (event.xdata, event.ydata)) - -plt.connect('motion_notify_event', update) - -plt.show() diff --git a/examples/user_interfaces/rec_edit_gtk_custom.py b/examples/user_interfaces/rec_edit_gtk_custom.py deleted file mode 100644 index 0d3a20374816..000000000000 --- a/examples/user_interfaces/rec_edit_gtk_custom.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -generate an editable gtk treeview widget for record arrays with custom -formatting of the cells and show how to limit string entries to a list -of strings -""" -from __future__ import print_function -import gtk -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.cbook as cbook -import mpl_toolkits.gtktools as gtktools - - -datafile = cbook.get_sample_data('demodata.csv', asfileobj=False) -r = mlab.csv2rec(datafile, converterd={'weekdays': str}) - - -formatd = mlab.get_formatd(r) -formatd['date'] = mlab.FormatDate('%Y-%m-%d') -formatd['prices'] = mlab.FormatMillions(precision=1) -formatd['gain'] = mlab.FormatPercent(precision=2) - -# use a drop down combo for weekdays -stringd = dict(weekdays=['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']) -constant = ['clientid'] # block editing of this field - - -liststore = gtktools.RecListStore(r, formatd=formatd, stringd=stringd) -treeview = gtktools.RecTreeView(liststore, constant=constant) - - -def mycallback(liststore, rownum, colname, oldval, newval): - print('verify: old=%s, new=%s, rec=%s' % (oldval, newval, liststore.r[rownum][colname])) - -liststore.callbacks.connect('cell_changed', mycallback) - -win = gtk.Window() -win.set_title('click to edit') -win.add(treeview) -win.show_all() -win.connect('delete-event', lambda *args: gtk.main_quit()) -gtk.main() diff --git a/examples/user_interfaces/rec_edit_gtk_simple.py b/examples/user_interfaces/rec_edit_gtk_simple.py deleted file mode 100644 index 4d01056604f8..000000000000 --- a/examples/user_interfaces/rec_edit_gtk_simple.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Load a CSV file into a record array and edit it in a gtk treeview -""" - -import gtk -import numpy as np -import matplotlib.mlab as mlab -import matplotlib.cbook as cbook -import mpl_toolkits.gtktools as gtktools - -datafile = cbook.get_sample_data('demodata.csv', asfileobj=False) -r = mlab.csv2rec(datafile, converterd={'weekdays': str}) - -liststore, treeview, win = gtktools.edit_recarray(r) -win.set_title('click to edit') -win.connect('delete-event', lambda *args: gtk.main_quit()) -gtk.main() diff --git a/examples/user_interfaces/svg_histogram.py b/examples/user_interfaces/svg_histogram.py deleted file mode 100755 index fea08b8cd688..000000000000 --- a/examples/user_interfaces/svg_histogram.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -Demonstrate how to create an interactive histogram, in which bars -are hidden or shown by cliking on legend markers. - -The interactivity is encoded in ecmascript (javascript) and inserted in -the SVG code in a post-processing step. To render the image, open it in -a web browser. SVG is supported in most web browsers used by Linux and -OSX users. Windows IE9 supports SVG, but earlier versions do not. - -Notes ------ -The matplotlib backend lets us assign ids to each object. This is the -mechanism used here to relate matplotlib objects created in python and -the corresponding SVG constructs that are parsed in the second step. -While flexible, ids are cumbersome to use for large collection of -objects. Two mechanisms could be used to simplify things: - * systematic grouping of objects into SVG tags, - * assingning classes to each SVG object according to its origin. - -For example, instead of modifying the properties of each individual bar, -the bars from the `hist` function could either be grouped in -a PatchCollection, or be assigned a class="hist_##" attribute. - -CSS could also be used more extensively to replace repetitive markup -troughout the generated SVG. - -Author: david.huard@gmail.com - -""" - - -import numpy as np -import matplotlib.pyplot as plt -import xml.etree.ElementTree as ET -from io import BytesIO -import json - -plt.rcParams['svg.embed_char_paths'] = 'none' - -# Apparently, this `register_namespace` method works only with -# python 2.7 and up and is necessary to avoid garbling the XML name -# space with ns0. -ET.register_namespace("", "http://www.w3.org/2000/svg") - - -# --- Create histogram, legend and title --- -plt.figure() -r = np.random.randn(100) -r1 = r + 1 -labels = ['Rabbits', 'Frogs'] -H = plt.hist([r, r1], label=labels) -containers = H[-1] -leg = plt.legend(frameon=False) -plt.title("From a web browser, click on the legend\n" - "marker to toggle the corresponding histogram.") - - -# --- Add ids to the svg objects we'll modify - -hist_patches = {} -for ic, c in enumerate(containers): - hist_patches['hist_%d' % ic] = [] - for il, element in enumerate(c): - element.set_gid('hist_%d_patch_%d' % (ic, il)) - hist_patches['hist_%d' % ic].append('hist_%d_patch_%d' % (ic, il)) - -# Set ids for the legend patches -for i, t in enumerate(leg.get_patches()): - t.set_gid('leg_patch_%d' % i) - -# Set ids for the text patches -for i, t in enumerate(leg.get_texts()): - t.set_gid('leg_text_%d' % i) - -# Save SVG in a fake file object. -f = BytesIO() -plt.savefig(f, format="svg") - -# Create XML tree from the SVG file. -tree, xmlid = ET.XMLID(f.getvalue()) - - -# --- Add interactivity --- - -# Add attributes to the patch objects. -for i, t in enumerate(leg.get_patches()): - el = xmlid['leg_patch_%d' % i] - el.set('cursor', 'pointer') - el.set('onclick', "toggle_hist(this)") - -# Add attributes to the text objects. -for i, t in enumerate(leg.get_texts()): - el = xmlid['leg_text_%d' % i] - el.set('cursor', 'pointer') - el.set('onclick', "toggle_hist(this)") - -# Create script defining the function `toggle_hist`. -# We create a global variable `container` that stores the patches id -# belonging to each histogram. Then a function "toggle_element" sets the -# visibility attribute of all patches of each histogram and the opacity -# of the marker itself. - -script = """ - -""" % json.dumps(hist_patches) - -# Add a transition effect -css = tree.getchildren()[0][0] -css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \ - "-moz-transition:opacity 0.4s ease-out;}" - -# Insert the script and save to file. -tree.insert(0, ET.XML(script)) - -ET.ElementTree(tree).write("svg_histogram.svg") diff --git a/examples/user_interfaces/svg_tooltip.py b/examples/user_interfaces/svg_tooltip.py deleted file mode 100644 index 2f136156f816..000000000000 --- a/examples/user_interfaces/svg_tooltip.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -SVG tooltip example -=================== - -This example shows how to create a tooltip that will show up when -hovering over a matplotlib patch. - -Although it is possible to create the tooltip from CSS or javascript, -here we create it in matplotlib and simply toggle its visibility on -when hovering over the patch. This approach provides total control over -the tooltip placement and appearance, at the expense of more code up -front. - -The alternative approach would be to put the tooltip content in `title` -atttributes of SVG objects. Then, using an existing js/CSS library, it -would be relatively straightforward to create the tooltip in the -browser. The content would be dictated by the `title` attribute, and -the appearance by the CSS. - - -:author: David Huard -""" - - -import matplotlib.pyplot as plt -import xml.etree.ElementTree as ET -from io import BytesIO - -ET.register_namespace("", "http://www.w3.org/2000/svg") - -fig, ax = plt.subplots() - -# Create patches to which tooltips will be assigned. -circle = plt.Circle((0, 0), 5, fc='blue') -rect = plt.Rectangle((-5, 10), 10, 5, fc='green') - -ax.add_patch(circle) -ax.add_patch(rect) - -# Create the tooltips -circle_tip = ax.annotate( - 'This is a blue circle.', - xy=(0, 0), - xytext=(30, -30), - textcoords='offset points', - color='w', - ha='left', - bbox=dict(boxstyle='round,pad=.5', fc=(.1, .1, .1, .92), - ec=(1., 1., 1.), lw=1, zorder=1)) - -rect_tip = ax.annotate( - 'This is a green rectangle.', - xy=(-5, 10), - xytext=(30, 40), - textcoords='offset points', - color='w', - ha='left', - bbox=dict(boxstyle='round,pad=.5', fc=(.1, .1, .1, .92), - ec=(1., 1., 1.), lw=1, zorder=1)) - -# Set id for the patches -for i, t in enumerate(ax.patches): - t.set_gid('patch_%d' % i) - -# Set id for the annotations -for i, t in enumerate(ax.texts): - t.set_gid('tooltip_%d' % i) - - -# Save the figure in a fake file object -ax.set_xlim(-30, 30) -ax.set_ylim(-30, 30) -ax.set_aspect('equal') - -f = BytesIO() -plt.savefig(f, format="svg") - -# --- Add interactivity --- - -# Create XML tree from the SVG file. -tree, xmlid = ET.XMLID(f.getvalue()) -tree.set('onload', 'init(evt)') - -# Hide the tooltips -for i, t in enumerate(ax.texts): - el = xmlid['tooltip_%d' % i] - el.set('visibility', 'hidden') - -# Assign onmouseover and onmouseout callbacks to patches. -for i, t in enumerate(ax.patches): - el = xmlid['patch_%d' % i] - el.set('onmouseover', "ShowTooltip(this)") - el.set('onmouseout', "HideTooltip(this)") - -# This is the script defining the ShowTooltip and HideTooltip functions. -script = """ - - """ - -# Insert the script at the top of the file and save it. -tree.insert(0, ET.XML(script)) -ET.ElementTree(tree).write('svg_tooltip.svg') diff --git a/examples/user_interfaces/toolmanager.py b/examples/user_interfaces/toolmanager.py deleted file mode 100644 index 64a5a14c2d95..000000000000 --- a/examples/user_interfaces/toolmanager.py +++ /dev/null @@ -1,93 +0,0 @@ -'''This example demonstrates how to: -* Modify the Toolbar -* Create tools -* Add tools -* Remove tools -Using `matplotlib.backend_managers.ToolManager` -''' - - -from __future__ import print_function -import matplotlib -matplotlib.use('GTK3Cairo') -matplotlib.rcParams['toolbar'] = 'toolmanager' -import matplotlib.pyplot as plt -from matplotlib.backend_tools import ToolBase, ToolToggleBase -from gi.repository import Gtk, Gdk - - -class ListTools(ToolBase): - '''List all the tools controlled by the `ToolManager`''' - # keyboard shortcut - default_keymap = 'm' - description = 'List Tools' - - def trigger(self, *args, **kwargs): - print('_' * 80) - print("{0:12} {1:45} {2}".format('Name (id)', - 'Tool description', - 'Keymap')) - print('-' * 80) - tools = self.toolmanager.tools - for name in sorted(tools.keys()): - if not tools[name].description: - continue - keys = ', '.join(sorted(self.toolmanager.get_tool_keymap(name))) - print("{0:12} {1:45} {2}".format(name, - tools[name].description, - keys)) - print('_' * 80) - print("Active Toggle tools") - print("{0:12} {1:45}".format("Group", "Active")) - print('-' * 80) - for group, active in self.toolmanager.active_toggle.items(): - print("{0:12} {1:45}".format(str(group), str(active))) - - -class GroupHideTool(ToolToggleBase): - '''Show lines with a given gid''' - default_keymap = 'G' - description = 'Show by gid' - default_toggled = True - - def __init__(self, *args, **kwargs): - self.gid = kwargs.pop('gid') - ToolToggleBase.__init__(self, *args, **kwargs) - - def enable(self, *args): - self.set_lines_visibility(True) - - def disable(self, *args): - self.set_lines_visibility(False) - - def set_lines_visibility(self, state): - gr_lines = [] - for ax in self.figure.get_axes(): - for line in ax.get_lines(): - if line.get_gid() == self.gid: - line.set_visible(state) - self.figure.canvas.draw() - - -fig = plt.figure() -plt.plot([1, 2, 3], gid='mygroup') -plt.plot([2, 3, 4], gid='unknown') -plt.plot([3, 2, 1], gid='mygroup') - -# Add the custom tools that we created -fig.canvas.manager.toolmanager.add_tool('List', ListTools) -fig.canvas.manager.toolmanager.add_tool('Show', GroupHideTool, gid='mygroup') - - -# Add an existing tool to new group `foo`. -# It can be added as many times as we want -fig.canvas.manager.toolbar.add_tool('zoom', 'foo') - -# Remove the forward button -fig.canvas.manager.toolmanager.remove_tool('forward') - -# To add a custom tool to the toolbar at specific location inside -# the navigation group -fig.canvas.manager.toolbar.add_tool('Show', 'navigation', 1) - -plt.show() diff --git a/examples/user_interfaces/wxcursor_demo.py b/examples/user_interfaces/wxcursor_demo.py deleted file mode 100644 index eab1562c1bc4..000000000000 --- a/examples/user_interfaces/wxcursor_demo.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Example to draw a cursor and report the data coords in wx -""" -# matplotlib requires wxPython 2.8+ -# set the wxPython version in lib\site-packages\wx.pth file -# or if you have wxversion installed un-comment the lines below -#import wxversion -#wxversion.ensureMinimal('2.8') - -import matplotlib -matplotlib.use('WXAgg') - -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas -from matplotlib.backends.backend_wx import NavigationToolbar2Wx, wxc -from matplotlib.figure import Figure -from numpy import arange, sin, pi - -import wx - - -class CanvasFrame(wx.Frame): - def __init__(self, ): - wx.Frame.__init__(self, None, -1, - 'CanvasFrame', size=(550, 350)) - - self.SetBackgroundColour(wxc.NamedColour("WHITE")) - - self.figure = Figure() - self.axes = self.figure.add_subplot(111) - t = arange(0.0, 3.0, 0.01) - s = sin(2*pi*t) - - self.axes.plot(t, s) - self.axes.set_xlabel('t') - self.axes.set_ylabel('sin(t)') - self.figure_canvas = FigureCanvas(self, -1, self.figure) - - # Note that event is a MplEvent - self.figure_canvas.mpl_connect('motion_notify_event', self.UpdateStatusBar) - self.figure_canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor) - - self.sizer = wx.BoxSizer(wx.VERTICAL) - self.sizer.Add(self.figure_canvas, 1, wx.LEFT | wx.TOP | wx.GROW) - self.SetSizer(self.sizer) - self.Fit() - - self.statusBar = wx.StatusBar(self, -1) - self.SetStatusBar(self.statusBar) - - self.toolbar = NavigationToolbar2Wx(self.figure_canvas) - self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) - self.toolbar.Show() - - def ChangeCursor(self, event): - self.figure_canvas.SetCursor(wxc.StockCursor(wx.CURSOR_BULLSEYE)) - - def UpdateStatusBar(self, event): - if event.inaxes: - x, y = event.xdata, event.ydata - self.statusBar.SetStatusText(("x= " + str(x) + - " y=" + str(y)), - 0) - - -class App(wx.App): - def OnInit(self): - 'Create the main window and insert the custom frame' - frame = CanvasFrame() - self.SetTopWindow(frame) - frame.Show(True) - return True - -if __name__ == '__main__': - app = App(0) - app.MainLoop() diff --git a/examples/widgets/README.txt b/examples/widgets/README.txt deleted file mode 100644 index 077c373df60e..000000000000 --- a/examples/widgets/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Examples of how to write primitive, but GUI agnoistic, widgets in -matplotlib diff --git a/examples/widgets/buttons.py b/examples/widgets/buttons.py deleted file mode 100644 index d2655211ccab..000000000000 --- a/examples/widgets/buttons.py +++ /dev/null @@ -1,40 +0,0 @@ - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import Button - -freqs = np.arange(2, 20, 3) - -fig, ax = plt.subplots() -plt.subplots_adjust(bottom=0.2) -t = np.arange(0.0, 1.0, 0.001) -s = np.sin(2*np.pi*freqs[0]*t) -l, = plt.plot(t, s, lw=2) - - -class Index(object): - ind = 0 - - def next(self, event): - self.ind += 1 - i = self.ind % len(freqs) - ydata = np.sin(2*np.pi*freqs[i]*t) - l.set_ydata(ydata) - plt.draw() - - def prev(self, event): - self.ind -= 1 - i = self.ind % len(freqs) - ydata = np.sin(2*np.pi*freqs[i]*t) - l.set_ydata(ydata) - plt.draw() - -callback = Index() -axprev = plt.axes([0.7, 0.05, 0.1, 0.075]) -axnext = plt.axes([0.81, 0.05, 0.1, 0.075]) -bnext = Button(axnext, 'Next') -bnext.on_clicked(callback.next) -bprev = Button(axprev, 'Previous') -bprev.on_clicked(callback.prev) - -plt.show() diff --git a/examples/widgets/check_buttons.py b/examples/widgets/check_buttons.py deleted file mode 100644 index 936e6d96341c..000000000000 --- a/examples/widgets/check_buttons.py +++ /dev/null @@ -1,30 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import CheckButtons - -t = np.arange(0.0, 2.0, 0.01) -s0 = np.sin(2*np.pi*t) -s1 = np.sin(4*np.pi*t) -s2 = np.sin(6*np.pi*t) - -fig, ax = plt.subplots() -l0, = ax.plot(t, s0, visible=False, lw=2) -l1, = ax.plot(t, s1, lw=2) -l2, = ax.plot(t, s2, lw=2) -plt.subplots_adjust(left=0.2) - -rax = plt.axes([0.05, 0.4, 0.1, 0.15]) -check = CheckButtons(rax, ('2 Hz', '4 Hz', '6 Hz'), (False, True, True)) - - -def func(label): - if label == '2 Hz': - l0.set_visible(not l0.get_visible()) - elif label == '4 Hz': - l1.set_visible(not l1.get_visible()) - elif label == '6 Hz': - l2.set_visible(not l2.get_visible()) - plt.draw() -check.on_clicked(func) - -plt.show() diff --git a/examples/widgets/cursor.py b/examples/widgets/cursor.py deleted file mode 100755 index 209223b72a12..000000000000 --- a/examples/widgets/cursor.py +++ /dev/null @@ -1,17 +0,0 @@ -from matplotlib.widgets import Cursor -import numpy as np -import matplotlib.pyplot as plt - - -fig = plt.figure(figsize=(8, 6)) -ax = fig.add_subplot(111, facecolor='#FFFFCC') - -x, y = 4*(np.random.rand(2, 100) - .5) -ax.plot(x, y, 'o') -ax.set_xlim(-2, 2) -ax.set_ylim(-2, 2) - -# set useblit = True on gtkagg for enhanced performance -cursor = Cursor(ax, useblit=True, color='red', linewidth=2) - -plt.show() diff --git a/examples/widgets/lasso_selector_demo.py b/examples/widgets/lasso_selector_demo.py deleted file mode 100644 index 4d1de112f731..000000000000 --- a/examples/widgets/lasso_selector_demo.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import print_function - -import numpy as np - -from matplotlib.widgets import LassoSelector -from matplotlib.path import Path - -try: - raw_input -except NameError: - # Python 3 - raw_input = input - - -class SelectFromCollection(object): - """Select indices from a matplotlib collection using `LassoSelector`. - - Selected indices are saved in the `ind` attribute. This tool highlights - selected points by fading them out (i.e., reducing their alpha values). - If your collection has alpha < 1, this tool will permanently alter them. - - Note that this tool selects collection objects based on their *origins* - (i.e., `offsets`). - - Parameters - ---------- - ax : :class:`~matplotlib.axes.Axes` - Axes to interact with. - - collection : :class:`matplotlib.collections.Collection` subclass - Collection you want to select from. - - alpha_other : 0 <= float <= 1 - To highlight a selection, this tool sets all selected points to an - alpha value of 1 and non-selected points to `alpha_other`. - """ - - def __init__(self, ax, collection, alpha_other=0.3): - self.canvas = ax.figure.canvas - self.collection = collection - self.alpha_other = alpha_other - - self.xys = collection.get_offsets() - self.Npts = len(self.xys) - - # Ensure that we have separate colors for each object - self.fc = collection.get_facecolors() - if len(self.fc) == 0: - raise ValueError('Collection must have a facecolor') - elif len(self.fc) == 1: - self.fc = np.tile(self.fc, self.Npts).reshape(self.Npts, -1) - - self.lasso = LassoSelector(ax, onselect=self.onselect) - self.ind = [] - - def onselect(self, verts): - path = Path(verts) - self.ind = np.nonzero([path.contains_point(xy) for xy in self.xys])[0] - self.fc[:, -1] = self.alpha_other - self.fc[self.ind, -1] = 1 - self.collection.set_facecolors(self.fc) - self.canvas.draw_idle() - - def disconnect(self): - self.lasso.disconnect_events() - self.fc[:, -1] = 1 - self.collection.set_facecolors(self.fc) - self.canvas.draw_idle() - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - - plt.ion() - data = np.random.rand(100, 2) - - subplot_kw = dict(xlim=(0, 1), ylim=(0, 1), autoscale_on=False) - fig, ax = plt.subplots(subplot_kw=subplot_kw) - - pts = ax.scatter(data[:, 0], data[:, 1], s=80) - selector = SelectFromCollection(ax, pts) - - plt.draw() - raw_input('Press any key to accept selected points') - print("Selected points:") - print(selector.xys[selector.ind]) - selector.disconnect() - - # Block end of script so you can check that the lasso is disconnected. - raw_input('Press any key to quit') diff --git a/examples/widgets/menu.py b/examples/widgets/menu.py deleted file mode 100644 index 846dd5ce1917..000000000000 --- a/examples/widgets/menu.py +++ /dev/null @@ -1,176 +0,0 @@ -from __future__ import division, print_function -import numpy as np -import matplotlib -import matplotlib.colors as colors -import matplotlib.patches as patches -import matplotlib.mathtext as mathtext -import matplotlib.pyplot as plt -import matplotlib.artist as artist -import matplotlib.image as image - - -class ItemProperties(object): - def __init__(self, fontsize=14, labelcolor='black', bgcolor='yellow', - alpha=1.0): - self.fontsize = fontsize - self.labelcolor = labelcolor - self.bgcolor = bgcolor - self.alpha = alpha - - self.labelcolor_rgb = colors.to_rgba(labelcolor)[:3] - self.bgcolor_rgb = colors.to_rgba(bgcolor)[:3] - - -class MenuItem(artist.Artist): - parser = mathtext.MathTextParser("Bitmap") - padx = 5 - pady = 5 - - def __init__(self, fig, labelstr, props=None, hoverprops=None, - on_select=None): - artist.Artist.__init__(self) - - self.set_figure(fig) - self.labelstr = labelstr - - if props is None: - props = ItemProperties() - - if hoverprops is None: - hoverprops = ItemProperties() - - self.props = props - self.hoverprops = hoverprops - - self.on_select = on_select - - x, self.depth = self.parser.to_mask( - labelstr, fontsize=props.fontsize, dpi=fig.dpi) - - if props.fontsize != hoverprops.fontsize: - raise NotImplementedError( - 'support for different font sizes not implemented') - - self.labelwidth = x.shape[1] - self.labelheight = x.shape[0] - - self.labelArray = np.zeros((x.shape[0], x.shape[1], 4)) - self.labelArray[:, :, -1] = x/255. - - self.label = image.FigureImage(fig, origin='upper') - self.label.set_array(self.labelArray) - - # we'll update these later - self.rect = patches.Rectangle((0, 0), 1, 1) - - self.set_hover_props(False) - - fig.canvas.mpl_connect('button_release_event', self.check_select) - - def check_select(self, event): - over, junk = self.rect.contains(event) - if not over: - return - - if self.on_select is not None: - self.on_select(self) - - def set_extent(self, x, y, w, h): - print(x, y, w, h) - self.rect.set_x(x) - self.rect.set_y(y) - self.rect.set_width(w) - self.rect.set_height(h) - - self.label.ox = x + self.padx - self.label.oy = y - self.depth + self.pady/2. - - self.rect._update_patch_transform() - self.hover = False - - def draw(self, renderer): - self.rect.draw(renderer) - self.label.draw(renderer) - - def set_hover_props(self, b): - if b: - props = self.hoverprops - else: - props = self.props - - r, g, b = props.labelcolor_rgb - self.labelArray[:, :, 0] = r - self.labelArray[:, :, 1] = g - self.labelArray[:, :, 2] = b - self.label.set_array(self.labelArray) - self.rect.set(facecolor=props.bgcolor, alpha=props.alpha) - - def set_hover(self, event): - 'check the hover status of event and return true if status is changed' - b, junk = self.rect.contains(event) - - changed = (b != self.hover) - - if changed: - self.set_hover_props(b) - - self.hover = b - return changed - - -class Menu(object): - def __init__(self, fig, menuitems): - self.figure = fig - fig.suppressComposite = True - - self.menuitems = menuitems - self.numitems = len(menuitems) - - maxw = max([item.labelwidth for item in menuitems]) - maxh = max([item.labelheight for item in menuitems]) - - totalh = self.numitems*maxh + (self.numitems + 1)*2*MenuItem.pady - - x0 = 100 - y0 = 400 - - width = maxw + 2*MenuItem.padx - height = maxh + MenuItem.pady - - for item in menuitems: - left = x0 - bottom = y0 - maxh - MenuItem.pady - - item.set_extent(left, bottom, width, height) - - fig.artists.append(item) - y0 -= maxh + MenuItem.pady - - fig.canvas.mpl_connect('motion_notify_event', self.on_move) - - def on_move(self, event): - draw = False - for item in self.menuitems: - draw = item.set_hover(event) - if draw: - self.figure.canvas.draw() - break - - -fig = plt.figure() -fig.subplots_adjust(left=0.3) -props = ItemProperties(labelcolor='black', bgcolor='yellow', - fontsize=15, alpha=0.2) -hoverprops = ItemProperties(labelcolor='white', bgcolor='blue', - fontsize=15, alpha=0.2) - -menuitems = [] -for label in ('open', 'close', 'save', 'save as', 'quit'): - def on_select(item): - print('you selected %s' % item.labelstr) - item = MenuItem(fig, label, props=props, hoverprops=hoverprops, - on_select=on_select) - menuitems.append(item) - -menu = Menu(fig, menuitems) -plt.show() diff --git a/examples/widgets/multicursor.py b/examples/widgets/multicursor.py deleted file mode 100644 index f6c5a36bff80..000000000000 --- a/examples/widgets/multicursor.py +++ /dev/null @@ -1,17 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import MultiCursor - -t = np.arange(0.0, 2.0, 0.01) -s1 = np.sin(2*np.pi*t) -s2 = np.sin(4*np.pi*t) -fig = plt.figure() -ax1 = fig.add_subplot(211) -ax1.plot(t, s1) - - -ax2 = fig.add_subplot(212, sharex=ax1) -ax2.plot(t, s2) - -multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1) -plt.show() diff --git a/examples/widgets/radio_buttons.py b/examples/widgets/radio_buttons.py deleted file mode 100644 index f993db59bf72..000000000000 --- a/examples/widgets/radio_buttons.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import RadioButtons - -t = np.arange(0.0, 2.0, 0.01) -s0 = np.sin(2*np.pi*t) -s1 = np.sin(4*np.pi*t) -s2 = np.sin(8*np.pi*t) - -fig, ax = plt.subplots() -l, = ax.plot(t, s0, lw=2, color='red') -plt.subplots_adjust(left=0.3) - -axcolor = 'lightgoldenrodyellow' -rax = plt.axes([0.05, 0.7, 0.15, 0.15], facecolor=axcolor) -radio = RadioButtons(rax, ('2 Hz', '4 Hz', '8 Hz')) - - -def hzfunc(label): - hzdict = {'2 Hz': s0, '4 Hz': s1, '8 Hz': s2} - ydata = hzdict[label] - l.set_ydata(ydata) - plt.draw() -radio.on_clicked(hzfunc) - -rax = plt.axes([0.05, 0.4, 0.15, 0.15], facecolor=axcolor) -radio2 = RadioButtons(rax, ('red', 'blue', 'green')) - - -def colorfunc(label): - l.set_color(label) - plt.draw() -radio2.on_clicked(colorfunc) - -rax = plt.axes([0.05, 0.1, 0.15, 0.15], facecolor=axcolor) -radio3 = RadioButtons(rax, ('-', '--', '-.', 'steps', ':')) - - -def stylefunc(label): - l.set_linestyle(label) - plt.draw() -radio3.on_clicked(stylefunc) - -plt.show() diff --git a/examples/widgets/rectangle_selector.py b/examples/widgets/rectangle_selector.py deleted file mode 100644 index eaedc686ea31..000000000000 --- a/examples/widgets/rectangle_selector.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import print_function -""" -Do a mouseclick somewhere, move the mouse to some destination, release -the button. This class gives click- and release-events and also draws -a line or a box from the click-point to the actual mouseposition -(within the same axes) until the button is released. Within the -method 'self.ignore()' it is checked wether the button from eventpress -and eventrelease are the same. - -""" -from matplotlib.widgets import RectangleSelector -import numpy as np -import matplotlib.pyplot as plt - - -def line_select_callback(eclick, erelease): - 'eclick and erelease are the press and release events' - x1, y1 = eclick.xdata, eclick.ydata - x2, y2 = erelease.xdata, erelease.ydata - print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) - print(" The button you used were: %s %s" % (eclick.button, erelease.button)) - - -def toggle_selector(event): - print(' Key pressed.') - if event.key in ['Q', 'q'] and toggle_selector.RS.active: - print(' RectangleSelector deactivated.') - toggle_selector.RS.set_active(False) - if event.key in ['A', 'a'] and not toggle_selector.RS.active: - print(' RectangleSelector activated.') - toggle_selector.RS.set_active(True) - - -fig, current_ax = plt.subplots() # make a new plotingrange -N = 100000 # If N is large one can see -x = np.linspace(0.0, 10.0, N) # improvement by use blitting! - -plt.plot(x, +np.sin(.2*np.pi*x), lw=3.5, c='b', alpha=.7) # plot something -plt.plot(x, +np.cos(.2*np.pi*x), lw=3.5, c='r', alpha=.5) -plt.plot(x, -np.sin(.2*np.pi*x), lw=3.5, c='g', alpha=.3) - -print("\n click --> release") - -# drawtype is 'box' or 'line' or 'none' -toggle_selector.RS = RectangleSelector(current_ax, line_select_callback, - drawtype='box', useblit=True, - button=[1, 3], # don't use middle button - minspanx=5, minspany=5, - spancoords='pixels', - interactive=True) -plt.connect('key_press_event', toggle_selector) -plt.show() diff --git a/examples/widgets/slider_demo.py b/examples/widgets/slider_demo.py deleted file mode 100644 index 70e6cb8d1ea7..000000000000 --- a/examples/widgets/slider_demo.py +++ /dev/null @@ -1,48 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import Slider, Button, RadioButtons - -fig, ax = plt.subplots() -plt.subplots_adjust(left=0.25, bottom=0.25) -t = np.arange(0.0, 1.0, 0.001) -a0 = 5 -f0 = 3 -s = a0*np.sin(2*np.pi*f0*t) -l, = plt.plot(t, s, lw=2, color='red') -plt.axis([0, 1, -10, 10]) - -axcolor = 'lightgoldenrodyellow' -axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor) -axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor) - -sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0) -samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0) - - -def update(val): - amp = samp.val - freq = sfreq.val - l.set_ydata(amp*np.sin(2*np.pi*freq*t)) - fig.canvas.draw_idle() -sfreq.on_changed(update) -samp.on_changed(update) - -resetax = plt.axes([0.8, 0.025, 0.1, 0.04]) -button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975') - - -def reset(event): - sfreq.reset() - samp.reset() -button.on_clicked(reset) - -rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor) -radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0) - - -def colorfunc(label): - l.set_color(label) - fig.canvas.draw_idle() -radio.on_clicked(colorfunc) - -plt.show() diff --git a/examples/widgets/span_selector.py b/examples/widgets/span_selector.py deleted file mode 100755 index d7bd5a1e426a..000000000000 --- a/examples/widgets/span_selector.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -The SpanSelector is a mouse widget to select a xmin/xmax range and plot the -detail view of the selected region in the lower axes -""" -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import SpanSelector - -fig = plt.figure(figsize=(8, 6)) -ax = fig.add_subplot(211, facecolor='#FFFFCC') - -x = np.arange(0.0, 5.0, 0.01) -y = np.sin(2*np.pi*x) + 0.5*np.random.randn(len(x)) - -ax.plot(x, y, '-') -ax.set_ylim(-2, 2) -ax.set_title('Press left mouse button and drag to test') - -ax2 = fig.add_subplot(212, axisbg='#FFFFCC') -line2, = ax2.plot(x, y, '-') - - -def onselect(xmin, xmax): - indmin, indmax = np.searchsorted(x, (xmin, xmax)) - indmax = min(len(x) - 1, indmax) - - thisx = x[indmin:indmax] - thisy = y[indmin:indmax] - line2.set_data(thisx, thisy) - ax2.set_xlim(thisx[0], thisx[-1]) - ax2.set_ylim(thisy.min(), thisy.max()) - fig.canvas.draw() - -# set useblit True on gtkagg for enhanced performance -span = SpanSelector(ax, onselect, 'horizontal', useblit=True, - rectprops=dict(alpha=0.5, facecolor='red')) - - -plt.show() diff --git a/examples/widgets/textbox.py b/examples/widgets/textbox.py deleted file mode 100644 index 271675a9757d..000000000000 --- a/examples/widgets/textbox.py +++ /dev/null @@ -1,23 +0,0 @@ - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.widgets import TextBox -fig, ax = plt.subplots() -plt.subplots_adjust(bottom=0.2) -t = np.arange(-2.0, 2.0, 0.001) -s = t ** 2 -initial_text = "t ** 2" -l, = plt.plot(t, s, lw=2) - - -def submit(text): - ydata = eval(text) - l.set_ydata(ydata) - ax.set_ylim(np.min(ydata), np.max(ydata)) - plt.draw() - -axbox = plt.axes([0.1, 0.05, 0.8, 0.075]) -text_box = TextBox(axbox, 'Evaluate', initial=initial_text) -text_box.on_submit(submit) - -plt.show() diff --git a/extern/agg24-svn/include/agg_basics.h b/extern/agg24-svn/include/agg_basics.h index 112bb3524c94..309713002b37 100644 --- a/extern/agg24-svn/include/agg_basics.h +++ b/extern/agg24-svn/include/agg_basics.h @@ -228,7 +228,7 @@ namespace agg { AGG_INLINE static unsigned mul(unsigned a, unsigned b) { - register unsigned q = a * b + (1 << (Shift-1)); + unsigned q = a * b + (1 << (Shift-1)); return (q + (q >> Shift)) >> Shift; } }; diff --git a/extern/agg24-svn/include/agg_image_accessors.h b/extern/agg24-svn/include/agg_image_accessors.h index 7c3902824437..c651d6d2e8dd 100644 --- a/extern/agg24-svn/include/agg_image_accessors.h +++ b/extern/agg24-svn/include/agg_image_accessors.h @@ -174,8 +174,8 @@ namespace agg private: AGG_INLINE const int8u* pixel() const { - register int x = m_x; - register int y = m_y; + int x = m_x; + int y = m_y; if(x < 0) x = 0; if(y < 0) y = 0; if(x >= (int)m_pixf->width()) x = m_pixf->width() - 1; diff --git a/extern/agg24-svn/include/agg_image_filters.h b/extern/agg24-svn/include/agg_image_filters.h index 8e1bc8f0dba4..e5b813dfc8a6 100644 --- a/extern/agg24-svn/include/agg_image_filters.h +++ b/extern/agg24-svn/include/agg_image_filters.h @@ -53,8 +53,13 @@ namespace agg double r = filter.radius(); realloc_lut(r); unsigned i; +#ifndef MPL_FIX_AGG_IMAGE_FILTER_LUT_BUGS unsigned pivot = diameter() << (image_subpixel_shift - 1); for(i = 0; i < pivot; i++) +#else + unsigned pivot = (diameter() << (image_subpixel_shift - 1)) - 1; + for(i = 0; i < pivot + 1; i++) +#endif { double x = double(i) / double(image_subpixel_scale); double y = filter.calc_weight(x); @@ -62,7 +67,11 @@ namespace agg m_weight_array[pivot - i] = (int16)iround(y * image_filter_scale); } unsigned end = (diameter() << image_subpixel_shift) - 1; +#ifndef MPL_FIX_AGG_IMAGE_FILTER_LUT_BUGS m_weight_array[0] = m_weight_array[end]; +#else + m_weight_array[end] = (int16)iround(filter.calc_weight(diameter() / 2) * image_filter_scale); +#endif if(normalization) { normalize(); diff --git a/extern/agg24-svn/include/agg_math_stroke.h b/extern/agg24-svn/include/agg_math_stroke.h index 4806dcd4bf79..4871d96cef61 100644 --- a/extern/agg24-svn/include/agg_math_stroke.h +++ b/extern/agg24-svn/include/agg_math_stroke.h @@ -391,7 +391,8 @@ namespace agg vc.remove_all(); double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y); - if(cp != 0 && (cp > 0) == (m_width > 0)) + if ((cp > agg::vertex_dist_epsilon && m_width > 0) || + (cp < -agg::vertex_dist_epsilon && m_width < 0)) { // Inner join //--------------- diff --git a/extern/agg24-svn/include/agg_pixfmt_gray.h b/extern/agg24-svn/include/agg_pixfmt_gray.h index d03dc8650170..438f04d33d4c 100644 --- a/extern/agg24-svn/include/agg_pixfmt_gray.h +++ b/extern/agg24-svn/include/agg_pixfmt_gray.h @@ -136,14 +136,13 @@ namespace agg typedef typename color_type::calc_type calc_type; enum { - num_components = 1, pix_width = sizeof(value_type) * Step, pix_step = Step, pix_offset = Offset, }; struct pixel_type { - value_type c[num_components]; + value_type c[pix_step]; void set(value_type v) { @@ -167,22 +166,22 @@ namespace agg pixel_type* next() { - return (pixel_type*)(c + pix_step); + return this + 1; } const pixel_type* next() const { - return (const pixel_type*)(c + pix_step); + return this + 1; } pixel_type* advance(int n) { - return (pixel_type*)(c + n * pix_step); + return this + n; } const pixel_type* advance(int n) const { - return (const pixel_type*)(c + n * pix_step); + return this + n; } }; diff --git a/extern/agg24-svn/include/agg_pixfmt_rgb.h b/extern/agg24-svn/include/agg_pixfmt_rgb.h index 6fa8772ce061..7095fbce58d6 100644 --- a/extern/agg24-svn/include/agg_pixfmt_rgb.h +++ b/extern/agg24-svn/include/agg_pixfmt_rgb.h @@ -192,14 +192,13 @@ namespace agg typedef typename color_type::calc_type calc_type; enum { - num_components = 3, pix_step = Step, pix_offset = Offset, pix_width = sizeof(value_type) * pix_step }; struct pixel_type { - value_type c[num_components]; + value_type c[pix_step]; void set(value_type r, value_type g, value_type b) { @@ -230,22 +229,22 @@ namespace agg pixel_type* next() { - return (pixel_type*)(c + pix_step); + return this + 1; } const pixel_type* next() const { - return (const pixel_type*)(c + pix_step); + return this + 1; } pixel_type* advance(int n) { - return (pixel_type*)(c + n * pix_step); + return this + n; } const pixel_type* advance(int n) const { - return (const pixel_type*)(c + n * pix_step); + return this + n; } }; diff --git a/extern/agg24-svn/include/agg_pixfmt_rgba.h b/extern/agg24-svn/include/agg_pixfmt_rgba.h index 701ccad6ef6a..e9cd523b375f 100644 --- a/extern/agg24-svn/include/agg_pixfmt_rgba.h +++ b/extern/agg24-svn/include/agg_pixfmt_rgba.h @@ -1515,13 +1515,12 @@ namespace agg typedef typename color_type::calc_type calc_type; enum { - num_components = 4, pix_step = 4, pix_width = sizeof(value_type) * pix_step, }; struct pixel_type { - value_type c[num_components]; + value_type c[pix_step]; void set(value_type r, value_type g, value_type b, value_type a) { @@ -1555,22 +1554,22 @@ namespace agg pixel_type* next() { - return (pixel_type*)(c + pix_step); + return this + 1; } const pixel_type* next() const { - return (const pixel_type*)(c + pix_step); + return this + 1; } pixel_type* advance(int n) { - return (pixel_type*)(c + n * pix_step); + return this + n; } const pixel_type* advance(int n) const { - return (const pixel_type*)(c + n * pix_step); + return this + n; } }; @@ -2193,13 +2192,12 @@ namespace agg typedef typename color_type::calc_type calc_type; enum { - num_components = 4, pix_step = 4, pix_width = sizeof(value_type) * pix_step, }; struct pixel_type { - value_type c[num_components]; + value_type c[pix_step]; void set(value_type r, value_type g, value_type b, value_type a) { @@ -2233,22 +2231,22 @@ namespace agg pixel_type* next() { - return (pixel_type*)(c + pix_step); + return this + 1; } const pixel_type* next() const { - return (const pixel_type*)(c + pix_step); + return this + 1; } pixel_type* advance(int n) { - return (pixel_type*)(c + n * pix_step); + return this + n; } const pixel_type* advance(int n) const { - return (const pixel_type*)(c + n * pix_step); + return this + n; } }; diff --git a/extern/agg24-svn/include/agg_rasterizer_cells_aa.h b/extern/agg24-svn/include/agg_rasterizer_cells_aa.h old mode 100755 new mode 100644 index 3809bdb70510..44a55417b492 --- a/extern/agg24-svn/include/agg_rasterizer_cells_aa.h +++ b/extern/agg24-svn/include/agg_rasterizer_cells_aa.h @@ -325,10 +325,13 @@ namespace agg if(dx >= dx_limit || dx <= -dx_limit) { - int cx = (x1 + x2) >> 1; - int cy = (y1 + y2) >> 1; + // These are overflow safe versions of (x1 + x2) >> 1; divide each by 2 + // first, then add 1 if both were odd. + int cx = (x1 >> 1) + (x2 >> 1) + ((x1 & 1) & (x2 & 1)); + int cy = (y1 >> 1) + (y2 >> 1) + ((y1 & 1) & (y2 & 1)); line(x1, y1, cx, cy); line(cx, cy, x2, y2); + return; } int dy = y2 - y1; diff --git a/extern/agg24-svn/include/agg_rasterizer_compound_aa.h b/extern/agg24-svn/include/agg_rasterizer_compound_aa.h old mode 100755 new mode 100644 diff --git a/extern/agg24-svn/include/agg_rasterizer_scanline_aa_nogamma.h b/extern/agg24-svn/include/agg_rasterizer_scanline_aa_nogamma.h index 6c3d96fc6160..7729b3359a10 100644 --- a/extern/agg24-svn/include/agg_rasterizer_scanline_aa_nogamma.h +++ b/extern/agg24-svn/include/agg_rasterizer_scanline_aa_nogamma.h @@ -60,7 +60,7 @@ namespace agg int not_equal(int ex, int ey, const cell_aa&) const { - return (ex - x) | (ey - y); + return ex != x || ey != y; } }; diff --git a/extern/agg24-svn/include/agg_rendering_buffer.h b/extern/agg24-svn/include/agg_rendering_buffer.h index 0eff6ff27d3d..191347f63e27 100644 --- a/extern/agg24-svn/include/agg_rendering_buffer.h +++ b/extern/agg24-svn/include/agg_rendering_buffer.h @@ -62,7 +62,7 @@ namespace agg m_stride = stride; if(stride < 0) { - m_start = m_buf - int(height - 1) * stride; + m_start = m_buf - (AGG_INT64)(height - 1) * stride; } } @@ -80,10 +80,10 @@ namespace agg //-------------------------------------------------------------------- AGG_INLINE T* row_ptr(int, int y, unsigned) { - return m_start + y * m_stride; + return m_start + y * (AGG_INT64)m_stride; } - AGG_INLINE T* row_ptr(int y) { return m_start + y * m_stride; } - AGG_INLINE const T* row_ptr(int y) const { return m_start + y * m_stride; } + AGG_INLINE T* row_ptr(int y) { return m_start + y * (AGG_INT64)m_stride; } + AGG_INLINE const T* row_ptr(int y) const { return m_start + y * (AGG_INT64)m_stride; } AGG_INLINE row_data row (int y) const { return row_data(0, m_width-1, row_ptr(y)); @@ -181,7 +181,7 @@ namespace agg if(stride < 0) { - row_ptr = m_buf - int(height - 1) * stride; + row_ptr = m_buf - (AGG_INT64)(height - 1) * stride; } T** rows = &m_rows[0]; diff --git a/extern/agg24-svn/include/agg_scanline_storage_aa.h b/extern/agg24-svn/include/agg_scanline_storage_aa.h index 7148c8992224..b3471fce7682 100644 --- a/extern/agg24-svn/include/agg_scanline_storage_aa.h +++ b/extern/agg24-svn/include/agg_scanline_storage_aa.h @@ -720,10 +720,10 @@ namespace agg m_ptr = m_data; if(m_ptr < m_end) { - m_min_x = read_int32() + m_dx; - m_min_y = read_int32() + m_dy; - m_max_x = read_int32() + m_dx; - m_max_y = read_int32() + m_dy; + m_min_x = read_int32u() + m_dx; + m_min_y = read_int32u() + m_dy; + m_max_x = read_int32u() + m_dx; + m_max_y = read_int32u() + m_dy; } return m_ptr < m_end; } diff --git a/extern/agg24-svn/include/agg_span_gouraud_rgba.h b/extern/agg24-svn/include/agg_span_gouraud_rgba.h index 89192d227bee..1b2857202971 100644 --- a/extern/agg24-svn/include/agg_span_gouraud_rgba.h +++ b/extern/agg24-svn/include/agg_span_gouraud_rgba.h @@ -198,10 +198,10 @@ namespace agg vg = g.y(); vb = b.y(); va = a.y(); - if(vr < 0) vr = 0; if(vr > lim) vr = lim; - if(vg < 0) vg = 0; if(vg > lim) vg = lim; - if(vb < 0) vb = 0; if(vb > lim) vb = lim; - if(va < 0) va = 0; if(va > lim) va = lim; + if(vr < 0) { vr = 0; }; if(vr > lim) { vr = lim; }; + if(vg < 0) { vg = 0; }; if(vg > lim) { vg = lim; }; + if(vb < 0) { vb = 0; }; if(vb > lim) { vb = lim; }; + if(va < 0) { va = 0; }; if(va > lim) { va = lim; }; span->r = (value_type)vr; span->g = (value_type)vg; span->b = (value_type)vb; @@ -245,10 +245,10 @@ namespace agg vg = g.y(); vb = b.y(); va = a.y(); - if(vr < 0) vr = 0; if(vr > lim) vr = lim; - if(vg < 0) vg = 0; if(vg > lim) vg = lim; - if(vb < 0) vb = 0; if(vb > lim) vb = lim; - if(va < 0) va = 0; if(va > lim) va = lim; + if(vr < 0) { vr = 0; }; if(vr > lim) { vr = lim; }; + if(vg < 0) { vg = 0; }; if(vg > lim) { vg = lim; }; + if(vb < 0) { vb = 0; }; if(vb > lim) { vb = lim; }; + if(va < 0) { va = 0; }; if(va > lim) { va = lim; }; span->r = (value_type)vr; span->g = (value_type)vg; span->b = (value_type)vb; diff --git a/extern/agg24-svn/include/agg_span_gradient_contour.h b/extern/agg24-svn/include/agg_span_gradient_contour.h index 142c2e6d1a34..899bb799b6c5 100644 --- a/extern/agg24-svn/include/agg_span_gradient_contour.h +++ b/extern/agg24-svn/include/agg_span_gradient_contour.h @@ -2,7 +2,7 @@ // AGG Contribution Pack - Gradients 1 (AGG CP - Gradients 1) // http://milan.marusinec.sk/aggcp // -// For Anti-Grain Geometry - Version 2.4 +// For Anti-Grain Geometry - Version 2.4 // http://www.antigrain.org // // Contribution Created By: @@ -49,7 +49,7 @@ namespace agg int m_frame; double m_d1; - double m_d2; + double m_d2; public: gradient_contour() : @@ -90,7 +90,7 @@ namespace agg void frame(int f ) { m_frame = f; } int frame() { return m_frame; } - + int calculate(int x, int y, int d) const { if (m_buffer) @@ -193,7 +193,7 @@ namespace agg // Setup VG Engine & Render agg::rendering_buffer rb; rb.attach(buffer ,width ,height ,width ); - + agg::pixfmt_gray8 pf(rb); agg::renderer_base renb(pf ); @@ -209,14 +209,14 @@ namespace agg ras.add_path(trans ); // II. Distance Transform - // Create Float Buffer + 0 vs infinity (1e20) assignment + // Create Float Buffer + 0 vs. infinity (1e20) assignment float* image = new float[width * height]; if (image) { - for (int y = 0, l = 0; y < height; y++ ) + for (int y = 0, l = 0; y < height; y++ ) { - for (int x = 0; x < width; x++, l++ ) + for (int x = 0; x < width; x++, l++ ) { if (buffer[l ] == 0) { @@ -227,7 +227,7 @@ namespace agg image[l ] = float(infinity ); } } - + } // DT of 2d @@ -247,9 +247,9 @@ namespace agg if ((spanf) && (spang) && (spanr) && (spann)) { // Transform along columns - for (int x = 0; x < width; x++ ) + for (int x = 0; x < width; x++ ) { - for (int y = 0; y < height; y++ ) + for (int y = 0; y < height; y++ ) { spanf[y] = image[y * width + x]; } @@ -257,16 +257,16 @@ namespace agg // DT of 1d dt(spanf ,spang ,spanr ,spann ,height ); - for (int y = 0; y < height; y++ ) + for (int y = 0; y < height; y++ ) { image[y * width + x] = spanr[y]; } } // Transform along rows - for (int y = 0; y < height; y++ ) + for (int y = 0; y < height; y++ ) { - for (int x = 0; x < width; x++ ) + for (int x = 0; x < width; x++ ) { spanf[x] = image[y * width + x]; } @@ -274,7 +274,7 @@ namespace agg // DT of 1d dt(spanf ,spang ,spanr ,spann ,width ); - for (int x = 0; x < width; x++ ) + for (int x = 0; x < width; x++ ) { image[y * width + x] = spanr[x]; } @@ -284,9 +284,9 @@ namespace agg float min = sqrt(image[0] ); float max = min; - for (int y = 0, l = 0; y < height; y++ ) + for (int y = 0, l = 0; y < height; y++ ) { - for (int x = 0; x < width; x++, l++ ) + for (int x = 0; x < width; x++, l++ ) { image[l] = sqrt(image[l]); @@ -312,9 +312,9 @@ namespace agg { float scale = 255 / (max - min ); - for (int y = 0, l = 0; y < height; y++ ) + for (int y = 0, l = 0; y < height; y++ ) { - for (int x = 0; x < width; x++ ,l++ ) + for (int x = 0; x < width; x++ ,l++ ) { buffer[l] = int8u(int((image[l] - min ) * scale )); } @@ -335,7 +335,7 @@ namespace agg result = m_buffer; } - + if (spanf) { delete [] spanf; } if (spang) { delete [] spang; } if (spanr) { delete [] spanr; } diff --git a/extern/agg24-svn/include/agg_span_image_filter_gray.h b/extern/agg24-svn/include/agg_span_image_filter_gray.h index e2c688e004cb..7ca583af724d 100644 --- a/extern/agg24-svn/include/agg_span_image_filter_gray.h +++ b/extern/agg24-svn/include/agg_span_image_filter_gray.h @@ -397,7 +397,9 @@ namespace agg fg += weight * *fg_ptr; fg >>= image_filter_shift; +#ifndef MPL_DISABLE_AGG_GRAY_CLIPPING if(fg > color_type::full_value()) fg = color_type::full_value(); +#endif span->v = (value_type)fg; span->a = color_type::full_value(); @@ -491,8 +493,10 @@ namespace agg } fg = color_type::downshift(fg, image_filter_shift); +#ifndef MPL_DISABLE_AGG_GRAY_CLIPPING if(fg < 0) fg = 0; if(fg > color_type::full_value()) fg = color_type::full_value(); +#endif span->v = (value_type)fg; span->a = color_type::full_value(); @@ -593,8 +597,10 @@ namespace agg } fg /= total_weight; +#ifndef MPL_DISABLE_AGG_GRAY_CLIPPING if(fg < 0) fg = 0; if(fg > color_type::full_value()) fg = color_type::full_value(); +#endif span->v = (value_type)fg; span->a = color_type::full_value(); @@ -701,8 +707,10 @@ namespace agg } fg /= total_weight; +#ifndef MPL_DISABLE_AGG_GRAY_CLIPPING if(fg < 0) fg = 0; if(fg > color_type::full_value()) fg = color_type::full_value(); +#endif span->v = (value_type)fg; span->a = color_type::full_value(); diff --git a/extern/agg24-svn/include/agg_span_interpolator_linear.h b/extern/agg24-svn/include/agg_span_interpolator_linear.h index ef10505ce11a..39cc7610b00c 100644 --- a/extern/agg24-svn/include/agg_span_interpolator_linear.h +++ b/extern/agg24-svn/include/agg_span_interpolator_linear.h @@ -53,6 +53,10 @@ namespace agg //---------------------------------------------------------------- void begin(double x, double y, unsigned len) { +#ifdef MPL_FIX_AGG_INTERPOLATION_ENDPOINT_BUG + len -= 1; +#endif + double tx; double ty; @@ -75,6 +79,10 @@ namespace agg //---------------------------------------------------------------- void resynchronize(double xe, double ye, unsigned len) { +#ifdef MPL_FIX_AGG_INTERPOLATION_ENDPOINT_BUG + len -= 1; +#endif + m_trans->transform(&xe, &ye); m_li_x = dda2_line_interpolator(m_li_x.y(), iround(xe * subpixel_scale), len); m_li_y = dda2_line_interpolator(m_li_y.y(), iround(ye * subpixel_scale), len); diff --git a/extern/agg24-svn/include/agg_trans_affine.h b/extern/agg24-svn/include/agg_trans_affine.h index 8933d7618a37..1a611638833b 100644 --- a/extern/agg24-svn/include/agg_trans_affine.h +++ b/extern/agg24-svn/include/agg_trans_affine.h @@ -292,7 +292,7 @@ namespace agg //------------------------------------------------------------------------ inline void trans_affine::transform(double* x, double* y) const { - register double tmp = *x; + double tmp = *x; *x = tmp * sx + *y * shx + tx; *y = tmp * shy + *y * sy + ty; } @@ -300,7 +300,7 @@ namespace agg //------------------------------------------------------------------------ inline void trans_affine::transform_2x2(double* x, double* y) const { - register double tmp = *x; + double tmp = *x; *x = tmp * sx + *y * shx; *y = tmp * shy + *y * sy; } @@ -308,9 +308,9 @@ namespace agg //------------------------------------------------------------------------ inline void trans_affine::inverse_transform(double* x, double* y) const { - register double d = determinant_reciprocal(); - register double a = (*x - tx) * d; - register double b = (*y - ty) * d; + double d = determinant_reciprocal(); + double a = (*x - tx) * d; + double b = (*y - ty) * d; *x = a * sy - b * shx; *y = b * sx - a * shy; } diff --git a/extern/agg24-svn/include/ctrl/agg_gamma_spline.h b/extern/agg24-svn/include/ctrl/agg_gamma_spline.h index 4f21710d9f29..052f972e85c1 100644 --- a/extern/agg24-svn/include/ctrl/agg_gamma_spline.h +++ b/extern/agg24-svn/include/ctrl/agg_gamma_spline.h @@ -56,7 +56,7 @@ namespace agg // bounding rectangle. Function values() calculates the curve by these // 4 values. After calling it one can get the gamma-array with call gamma(). // Class also supports the vertex source interface, i.e rewind() and - // vertex(). It's made for convinience and used in class gamma_ctrl. + // vertex(). It's made for convenience and used in class gamma_ctrl. // Before calling rewind/vertex one must set the bounding box // box() using pixel coordinates. //------------------------------------------------------------------------ diff --git a/extern/agg24-svn/include/platform/agg_platform_support.h b/extern/agg24-svn/include/platform/agg_platform_support.h index 556d07dc387a..9a63411e5084 100644 --- a/extern/agg24-svn/include/platform/agg_platform_support.h +++ b/extern/agg24-svn/include/platform/agg_platform_support.h @@ -18,7 +18,7 @@ // It's not a part of the AGG library, it's just a helper class to create // interactive demo examples. Since the examples should not be too complex // this class is provided to support some very basic interactive graphical -// funtionality, such as putting the rendered image to the window, simple +// functionality, such as putting the rendered image to the window, simple // keyboard and mouse input, window resizing, setting the window title, // and catching the "idle" events. // diff --git a/extern/agg24-svn/meson.build b/extern/agg24-svn/meson.build new file mode 100644 index 000000000000..a1c088423cb8 --- /dev/null +++ b/extern/agg24-svn/meson.build @@ -0,0 +1,22 @@ +# We need a patched Agg not available elsewhere, so always use the vendored +# version. + +agg_incdir = include_directories('include') + +agg_lib = static_library('agg', + 'src/agg_bezier_arc.cpp', + 'src/agg_curves.cpp', + 'src/agg_image_filters.cpp', + 'src/agg_trans_affine.cpp', + 'src/agg_vcgen_contour.cpp', + 'src/agg_vcgen_dash.cpp', + 'src/agg_vcgen_stroke.cpp', + 'src/agg_vpgen_segmentator.cpp', + include_directories : agg_incdir, + gnu_symbol_visibility: 'inlineshidden', +) + +agg_dep = declare_dependency( + include_directories: agg_incdir, + link_with: agg_lib, +) diff --git a/extern/agg24-svn/src/agg_image_filters.cpp b/extern/agg24-svn/src/agg_image_filters.cpp index 549d9adbf5af..1571308be55a 100644 --- a/extern/agg24-svn/src/agg_image_filters.cpp +++ b/extern/agg24-svn/src/agg_image_filters.cpp @@ -88,6 +88,7 @@ namespace agg } } +#ifndef MPL_FIX_AGG_IMAGE_FILTER_LUT_BUGS unsigned pivot = m_diameter << (image_subpixel_shift - 1); for(i = 0; i < pivot; i++) @@ -96,6 +97,7 @@ namespace agg } unsigned end = (diameter() << image_subpixel_shift) - 1; m_weight_array[0] = m_weight_array[end]; +#endif } diff --git a/extern/agg24-svn/src/platform/win32/agg_platform_support.cpp b/extern/agg24-svn/src/platform/win32/agg_platform_support.cpp index d9eba30fd8b7..ea9123802f2b 100644 --- a/extern/agg24-svn/src/platform/win32/agg_platform_support.cpp +++ b/extern/agg24-svn/src/platform/win32/agg_platform_support.cpp @@ -1480,7 +1480,7 @@ namespace agg tok.len = 0; if(m_src_string == 0 || m_start == -1) return tok; - register const char *pstr = m_src_string + m_start; + const char *pstr = m_src_string + m_start; if(*pstr == 0) { diff --git a/extern/meson.build b/extern/meson.build new file mode 100644 index 000000000000..5463183a9099 --- /dev/null +++ b/extern/meson.build @@ -0,0 +1,33 @@ +# Bundled code. +subdir('agg24-svn') + +# External code. + +# FreeType 2.3 has libtool version 9.11.3 as can be checked from the tarball. +# For FreeType>=2.4, there is a conversion table in docs/VERSIONS.txt in the +# FreeType source tree. +if get_option('system-freetype') + freetype_dep = dependency('freetype2', version: '>=9.11.3') +else + # This is the version of FreeType to use when building a local version. It + # must match the value in `lib/matplotlib.__init__.py`. Also update the docs + # in `docs/devel/dependencies.rst`. Bump the cache key in + # `.circleci/config.yml` when changing requirements. + LOCAL_FREETYPE_VERSION = '2.6.1' + + freetype_proj = subproject( + f'freetype-@LOCAL_FREETYPE_VERSION@', + default_options: ['default_library=static']) + freetype_dep = freetype_proj.get_variable('freetype_dep') +endif + +if get_option('system-qhull') + qhull_dep = dependency('qhull_r', version: '>=8.0.2', required: false) + if not qhull_dep.found() + cc.check_header('libqhull_r/qhull_ra.h', required: true) + qhull_dep = cc.find_library('qhull_r') + endif +else + qhull_proj = subproject('qhull') + qhull_dep = qhull_proj.get_variable('qhull_dep') +endif diff --git a/extern/qhull/COPYING.txt b/extern/qhull/COPYING.txt deleted file mode 100644 index a4a4ade56e19..000000000000 --- a/extern/qhull/COPYING.txt +++ /dev/null @@ -1,38 +0,0 @@ - Qhull, Copyright (c) 1993-2012 - - C.B. Barber - Arlington, MA - - and - - The National Science and Technology Research Center for - Computation and Visualization of Geometric Structures - (The Geometry Center) - University of Minnesota - - email: qhull@qhull.org - -This software includes Qhull from C.B. Barber and The Geometry Center. -Qhull is copyrighted as noted above. Qhull is free software and may -be obtained via http from www.qhull.org. It may be freely copied, modified, -and redistributed under the following conditions: - -1. All copyright notices must remain intact in all files. - -2. A copy of this text file must be distributed along with any copies - of Qhull that you redistribute; this includes copies that you have - modified, or copies of programs or other software products that - include Qhull. - -3. If you modify Qhull, you must include a notice giving the - name of the person performing the modification, the date of - modification, and the reason for such modification. - -4. When distributing modified versions of Qhull, or other software - products that include Qhull, you must provide notice that the original - source code may be obtained as noted above. - -5. There is no warranty or other guarantee of fitness for Qhull, it is - provided solely "as is". Bug reports or fixes may be sent to - qhull_bug@qhull.org; the authors may or may not act on them as - they desire. diff --git a/extern/qhull/geom.c b/extern/qhull/geom.c deleted file mode 100644 index a1517874aab4..000000000000 --- a/extern/qhull/geom.c +++ /dev/null @@ -1,1231 +0,0 @@ -/*
      ---------------------------------
    -
    -   geom.c
    -   geometric routines of qhull
    -
    -   see qh-geom.htm and geom.h
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/geom.c#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -
    -   infrequent code goes into geom2.c
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*---------------------------------
    -
    -  qh_distplane( point, facet, dist )
    -    return distance from point to facet
    -
    -  returns:
    -    dist
    -    if qh.RANDOMdist, joggles result
    -
    -  notes:
    -    dist > 0 if point is above facet (i.e., outside)
    -    does not error (for qh_sortfacets, qh_outerinner)
    -
    -  see:
    -    qh_distnorm in geom2.c
    -    qh_distplane [geom.c], QhullFacet::distance, and QhullHyperplane::distance are copies
    -*/
    -void qh_distplane(pointT *point, facetT *facet, realT *dist) {
    -  coordT *normal= facet->normal, *coordp, randr;
    -  int k;
    -
    -  switch (qh hull_dim){
    -  case 2:
    -    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1];
    -    break;
    -  case 3:
    -    *dist= facet->offset + point[0] * normal[0] + point[1] * normal[1] + point[2] * normal[2];
    -    break;
    -  case 4:
    -    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3];
    -    break;
    -  case 5:
    -    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4];
    -    break;
    -  case 6:
    -    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5];
    -    break;
    -  case 7:
    -    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6];
    -    break;
    -  case 8:
    -    *dist= facet->offset+point[0]*normal[0]+point[1]*normal[1]+point[2]*normal[2]+point[3]*normal[3]+point[4]*normal[4]+point[5]*normal[5]+point[6]*normal[6]+point[7]*normal[7];
    -    break;
    -  default:
    -    *dist= facet->offset;
    -    coordp= point;
    -    for (k=qh hull_dim; k--; )
    -      *dist += *coordp++ * *normal++;
    -    break;
    -  }
    -  zinc_(Zdistplane);
    -  if (!qh RANDOMdist && qh IStracing < 4)
    -    return;
    -  if (qh RANDOMdist) {
    -    randr= qh_RANDOMint;
    -    *dist += (2.0 * randr / qh_RANDOMmax - 1.0) *
    -      qh RANDOMfactor * qh MAXabs_coord;
    -  }
    -  if (qh IStracing >= 4) {
    -    qh_fprintf(qh ferr, 8001, "qh_distplane: ");
    -    qh_fprintf(qh ferr, 8002, qh_REAL_1, *dist);
    -    qh_fprintf(qh ferr, 8003, "from p%d to f%d\n", qh_pointid(point), facet->id);
    -  }
    -  return;
    -} /* distplane */
    -
    -
    -/*---------------------------------
    -
    -  qh_findbest( point, startfacet, bestoutside, qh_ISnewfacets, qh_NOupper, dist, isoutside, numpart )
    -    find facet that is furthest below a point
    -    for upperDelaunay facets
    -      returns facet only if !qh_NOupper and clearly above
    -
    -  input:
    -    starts search at 'startfacet' (can not be flipped)
    -    if !bestoutside(qh_ALL), stops at qh.MINoutside
    -
    -  returns:
    -    best facet (reports error if NULL)
    -    early out if isoutside defined and bestdist > qh.MINoutside
    -    dist is distance to facet
    -    isoutside is true if point is outside of facet
    -    numpart counts the number of distance tests
    -
    -  see also:
    -    qh_findbestnew()
    -
    -  notes:
    -    If merging (testhorizon), searches horizon facets of coplanar best facets because
    -    after qh_distplane, this and qh_partitionpoint are the most expensive in 3-d
    -      avoid calls to distplane, function calls, and real number operations.
    -    caller traces result
    -    Optimized for outside points.   Tried recording a search set for qh_findhorizon.
    -    Made code more complicated.
    -
    -  when called by qh_partitionvisible():
    -    indicated by qh_ISnewfacets
    -    qh.newfacet_list is list of simplicial, new facets
    -    qh_findbestnew set if qh_sharpnewfacets returns True (to use qh_findbestnew)
    -    qh.bestfacet_notsharp set if qh_sharpnewfacets returns False
    -
    -  when called by qh_findfacet(), qh_partitionpoint(), qh_partitioncoplanar(),
    -                 qh_check_bestdist(), qh_addpoint()
    -    indicated by !qh_ISnewfacets
    -    returns best facet in neighborhood of given facet
    -      this is best facet overall if dist > -   qh.MAXcoplanar
    -        or hull has at least a "spherical" curvature
    -
    -  design:
    -    initialize and test for early exit
    -    repeat while there are better facets
    -      for each neighbor of facet
    -        exit if outside facet found
    -        test for better facet
    -    if point is inside and partitioning
    -      test for new facets with a "sharp" intersection
    -      if so, future calls go to qh_findbestnew()
    -    test horizon facets
    -*/
    -facetT *qh_findbest(pointT *point, facetT *startfacet,
    -                     boolT bestoutside, boolT isnewfacets, boolT noupper,
    -                     realT *dist, boolT *isoutside, int *numpart) {
    -  realT bestdist= -REALmax/2 /* avoid underflow */;
    -  facetT *facet, *neighbor, **neighborp;
    -  facetT *bestfacet= NULL, *lastfacet= NULL;
    -  int oldtrace= qh IStracing;
    -  unsigned int visitid= ++qh visit_id;
    -  int numpartnew=0;
    -  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
    -
    -  zinc_(Zfindbest);
    -  if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
    -    if (qh TRACElevel > qh IStracing)
    -      qh IStracing= qh TRACElevel;
    -    qh_fprintf(qh ferr, 8004, "qh_findbest: point p%d starting at f%d isnewfacets? %d, unless %d exit if > %2.2g\n",
    -             qh_pointid(point), startfacet->id, isnewfacets, bestoutside, qh MINoutside);
    -    qh_fprintf(qh ferr, 8005, "  testhorizon? %d noupper? %d", testhorizon, noupper);
    -    qh_fprintf(qh ferr, 8006, "  Last point added was p%d.", qh furthest_id);
    -    qh_fprintf(qh ferr, 8007, "  Last merge was #%d.  max_outside %2.2g\n", zzval_(Ztotmerge), qh max_outside);
    -  }
    -  if (isoutside)
    -    *isoutside= True;
    -  if (!startfacet->flipped) {  /* test startfacet */
    -    *numpart= 1;
    -    qh_distplane(point, startfacet, dist);  /* this code is duplicated below */
    -    if (!bestoutside && *dist >= qh MINoutside
    -    && (!startfacet->upperdelaunay || !noupper)) {
    -      bestfacet= startfacet;
    -      goto LABELreturn_best;
    -    }
    -    bestdist= *dist;
    -    if (!startfacet->upperdelaunay) {
    -      bestfacet= startfacet;
    -    }
    -  }else
    -    *numpart= 0;
    -  startfacet->visitid= visitid;
    -  facet= startfacet;
    -  while (facet) {
    -    trace4((qh ferr, 4001, "qh_findbest: neighbors of f%d, bestdist %2.2g f%d\n",
    -                facet->id, bestdist, getid_(bestfacet)));
    -    lastfacet= facet;
    -    FOREACHneighbor_(facet) {
    -      if (!neighbor->newfacet && isnewfacets)
    -        continue;
    -      if (neighbor->visitid == visitid)
    -        continue;
    -      neighbor->visitid= visitid;
    -      if (!neighbor->flipped) {  /* code duplicated above */
    -        (*numpart)++;
    -        qh_distplane(point, neighbor, dist);
    -        if (*dist > bestdist) {
    -          if (!bestoutside && *dist >= qh MINoutside
    -          && (!neighbor->upperdelaunay || !noupper)) {
    -            bestfacet= neighbor;
    -            goto LABELreturn_best;
    -          }
    -          if (!neighbor->upperdelaunay) {
    -            bestfacet= neighbor;
    -            bestdist= *dist;
    -            break; /* switch to neighbor */
    -          }else if (!bestfacet) {
    -            bestdist= *dist;
    -            break; /* switch to neighbor */
    -          }
    -        } /* end of *dist>bestdist */
    -      } /* end of !flipped */
    -    } /* end of FOREACHneighbor */
    -    facet= neighbor;  /* non-NULL only if *dist>bestdist */
    -  } /* end of while facet (directed search) */
    -  if (isnewfacets) {
    -    if (!bestfacet) {
    -      bestdist= -REALmax/2;
    -      bestfacet= qh_findbestnew(point, startfacet->next, &bestdist, bestoutside, isoutside, &numpartnew);
    -      testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
    -    }else if (!qh findbest_notsharp && bestdist < - qh DISTround) {
    -      if (qh_sharpnewfacets()) {
    -        /* seldom used, qh_findbestnew will retest all facets */
    -        zinc_(Zfindnewsharp);
    -        bestfacet= qh_findbestnew(point, bestfacet, &bestdist, bestoutside, isoutside, &numpartnew);
    -        testhorizon= False; /* qh_findbestnew calls qh_findbesthorizon */
    -        qh findbestnew= True;
    -      }else
    -        qh findbest_notsharp= True;
    -    }
    -  }
    -  if (!bestfacet)
    -    bestfacet= qh_findbestlower(lastfacet, point, &bestdist, numpart);
    -  if (testhorizon)
    -    bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet, noupper, &bestdist, &numpartnew);
    -  *dist= bestdist;
    -  if (isoutside && bestdist < qh MINoutside)
    -    *isoutside= False;
    -LABELreturn_best:
    -  zadd_(Zfindbesttot, *numpart);
    -  zmax_(Zfindbestmax, *numpart);
    -  (*numpart) += numpartnew;
    -  qh IStracing= oldtrace;
    -  return bestfacet;
    -}  /* findbest */
    -
    -
    -/*---------------------------------
    -
    -  qh_findbesthorizon( qh_IScheckmax, point, startfacet, qh_NOupper, &bestdist, &numpart )
    -    search coplanar and better horizon facets from startfacet/bestdist
    -    ischeckmax turns off statistics and minsearch update
    -    all arguments must be initialized
    -  returns(ischeckmax):
    -    best facet
    -  returns(!ischeckmax):
    -    best facet that is not upperdelaunay
    -    allows upperdelaunay that is clearly outside
    -  returns:
    -    bestdist is distance to bestfacet
    -    numpart -- updates number of distance tests
    -
    -  notes:
    -    no early out -- use qh_findbest() or qh_findbestnew()
    -    Searches coplanar or better horizon facets
    -
    -  when called by qh_check_maxout() (qh_IScheckmax)
    -    startfacet must be closest to the point
    -      Otherwise, if point is beyond and below startfacet, startfacet may be a local minimum
    -      even though other facets are below the point.
    -    updates facet->maxoutside for good, visited facets
    -    may return NULL
    -
    -    searchdist is qh.max_outside + 2 * DISTround
    -      + max( MINvisible('Vn'), MAXcoplanar('Un'));
    -    This setting is a guess.  It must be at least max_outside + 2*DISTround
    -    because a facet may have a geometric neighbor across a vertex
    -
    -  design:
    -    for each horizon facet of coplanar best facets
    -      continue if clearly inside
    -      unless upperdelaunay or clearly outside
    -         update best facet
    -*/
    -facetT *qh_findbesthorizon(boolT ischeckmax, pointT* point, facetT *startfacet, boolT noupper, realT *bestdist, int *numpart) {
    -  facetT *bestfacet= startfacet;
    -  realT dist;
    -  facetT *neighbor, **neighborp, *facet;
    -  facetT *nextfacet= NULL; /* optimize last facet of coplanarfacetset */
    -  int numpartinit= *numpart, coplanarfacetset_size;
    -  unsigned int visitid= ++qh visit_id;
    -  boolT newbest= False; /* for tracing */
    -  realT minsearch, searchdist;  /* skip facets that are too far from point */
    -
    -  if (!ischeckmax) {
    -    zinc_(Zfindhorizon);
    -  }else {
    -#if qh_MAXoutside
    -    if ((!qh ONLYgood || startfacet->good) && *bestdist > startfacet->maxoutside)
    -      startfacet->maxoutside= *bestdist;
    -#endif
    -  }
    -  searchdist= qh_SEARCHdist; /* multiple of qh.max_outside and precision constants */
    -  minsearch= *bestdist - searchdist;
    -  if (ischeckmax) {
    -    /* Always check coplanar facets.  Needed for RBOX 1000 s Z1 G1e-13 t996564279 | QHULL Tv */
    -    minimize_(minsearch, -searchdist);
    -  }
    -  coplanarfacetset_size= 0;
    -  facet= startfacet;
    -  while (True) {
    -    trace4((qh ferr, 4002, "qh_findbesthorizon: neighbors of f%d bestdist %2.2g f%d ischeckmax? %d noupper? %d minsearch %2.2g searchdist %2.2g\n",
    -                facet->id, *bestdist, getid_(bestfacet), ischeckmax, noupper,
    -                minsearch, searchdist));
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid == visitid)
    -        continue;
    -      neighbor->visitid= visitid;
    -      if (!neighbor->flipped) {
    -        qh_distplane(point, neighbor, &dist);
    -        (*numpart)++;
    -        if (dist > *bestdist) {
    -          if (!neighbor->upperdelaunay || ischeckmax || (!noupper && dist >= qh MINoutside)) {
    -            bestfacet= neighbor;
    -            *bestdist= dist;
    -            newbest= True;
    -            if (!ischeckmax) {
    -              minsearch= dist - searchdist;
    -              if (dist > *bestdist + searchdist) {
    -                zinc_(Zfindjump);  /* everything in qh.coplanarfacetset at least searchdist below */
    -                coplanarfacetset_size= 0;
    -              }
    -            }
    -          }
    -        }else if (dist < minsearch)
    -          continue;  /* if ischeckmax, dist can't be positive */
    -#if qh_MAXoutside
    -        if (ischeckmax && dist > neighbor->maxoutside)
    -          neighbor->maxoutside= dist;
    -#endif
    -      } /* end of !flipped */
    -      if (nextfacet) {
    -        if (!coplanarfacetset_size++) {
    -          SETfirst_(qh coplanarfacetset)= nextfacet;
    -          SETtruncate_(qh coplanarfacetset, 1);
    -        }else
    -          qh_setappend(&qh coplanarfacetset, nextfacet); /* Was needed for RBOX 1000 s W1e-13 P0 t996547055 | QHULL d Qbb Qc Tv
    -                                                 and RBOX 1000 s Z1 G1e-13 t996564279 | qhull Tv  */
    -      }
    -      nextfacet= neighbor;
    -    } /* end of EACHneighbor */
    -    facet= nextfacet;
    -    if (facet)
    -      nextfacet= NULL;
    -    else if (!coplanarfacetset_size)
    -      break;
    -    else if (!--coplanarfacetset_size) {
    -      facet= SETfirstt_(qh coplanarfacetset, facetT);
    -      SETtruncate_(qh coplanarfacetset, 0);
    -    }else
    -      facet= (facetT*)qh_setdellast(qh coplanarfacetset);
    -  } /* while True, for each facet in qh.coplanarfacetset */
    -  if (!ischeckmax) {
    -    zadd_(Zfindhorizontot, *numpart - numpartinit);
    -    zmax_(Zfindhorizonmax, *numpart - numpartinit);
    -    if (newbest)
    -      zinc_(Zparthorizon);
    -  }
    -  trace4((qh ferr, 4003, "qh_findbesthorizon: newbest? %d bestfacet f%d bestdist %2.2g\n", newbest, getid_(bestfacet), *bestdist));
    -  return bestfacet;
    -}  /* findbesthorizon */
    -
    -/*---------------------------------
    -
    -  qh_findbestnew( point, startfacet, dist, isoutside, numpart )
    -    find best newfacet for point
    -    searches all of qh.newfacet_list starting at startfacet
    -    searches horizon facets of coplanar best newfacets
    -    searches all facets if startfacet == qh.facet_list
    -  returns:
    -    best new or horizon facet that is not upperdelaunay
    -    early out if isoutside and not 'Qf'
    -    dist is distance to facet
    -    isoutside is true if point is outside of facet
    -    numpart is number of distance tests
    -
    -  notes:
    -    Always used for merged new facets (see qh_USEfindbestnew)
    -    Avoids upperdelaunay facet unless (isoutside and outside)
    -
    -    Uses qh.visit_id, qh.coplanarfacetset.
    -    If share visit_id with qh_findbest, coplanarfacetset is incorrect.
    -
    -    If merging (testhorizon), searches horizon facets of coplanar best facets because
    -    a point maybe coplanar to the bestfacet, below its horizon facet,
    -    and above a horizon facet of a coplanar newfacet.  For example,
    -      rbox 1000 s Z1 G1e-13 | qhull
    -      rbox 1000 s W1e-13 P0 t992110337 | QHULL d Qbb Qc
    -
    -    qh_findbestnew() used if
    -       qh_sharpnewfacets -- newfacets contains a sharp angle
    -       if many merges, qh_premerge found a merge, or 'Qf' (qh.findbestnew)
    -
    -  see also:
    -    qh_partitionall() and qh_findbest()
    -
    -  design:
    -    for each new facet starting from startfacet
    -      test distance from point to facet
    -      return facet if clearly outside
    -      unless upperdelaunay and a lowerdelaunay exists
    -         update best facet
    -    test horizon facets
    -*/
    -facetT *qh_findbestnew(pointT *point, facetT *startfacet,
    -           realT *dist, boolT bestoutside, boolT *isoutside, int *numpart) {
    -  realT bestdist= -REALmax/2;
    -  facetT *bestfacet= NULL, *facet;
    -  int oldtrace= qh IStracing, i;
    -  unsigned int visitid= ++qh visit_id;
    -  realT distoutside= 0.0;
    -  boolT isdistoutside; /* True if distoutside is defined */
    -  boolT testhorizon = True; /* needed if precise, e.g., rbox c D6 | qhull Q0 Tv */
    -
    -  if (!startfacet) {
    -    if (qh MERGING)
    -      qh_fprintf(qh ferr, 6001, "qhull precision error (qh_findbestnew): merging has formed and deleted a cone of new facets.  Can not continue.\n");
    -    else
    -      qh_fprintf(qh ferr, 6002, "qhull internal error (qh_findbestnew): no new facets for point p%d\n",
    -              qh furthest_id);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  zinc_(Zfindnew);
    -  if (qh BESToutside || bestoutside)
    -    isdistoutside= False;
    -  else {
    -    isdistoutside= True;
    -    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
    -  }
    -  if (isoutside)
    -    *isoutside= True;
    -  *numpart= 0;
    -  if (qh IStracing >= 3 || (qh TRACElevel && qh TRACEpoint >= 0 && qh TRACEpoint == qh_pointid(point))) {
    -    if (qh TRACElevel > qh IStracing)
    -      qh IStracing= qh TRACElevel;
    -    qh_fprintf(qh ferr, 8008, "qh_findbestnew: point p%d facet f%d. Stop? %d if dist > %2.2g\n",
    -             qh_pointid(point), startfacet->id, isdistoutside, distoutside);
    -    qh_fprintf(qh ferr, 8009, "  Last point added p%d visitid %d.",  qh furthest_id, visitid);
    -    qh_fprintf(qh ferr, 8010, "  Last merge was #%d.\n", zzval_(Ztotmerge));
    -  }
    -  /* visit all new facets starting with startfacet, maybe qh facet_list */
    -  for (i=0, facet=startfacet; i < 2; i++, facet= qh newfacet_list) {
    -    FORALLfacet_(facet) {
    -      if (facet == startfacet && i)
    -        break;
    -      facet->visitid= visitid;
    -      if (!facet->flipped) {
    -        qh_distplane(point, facet, dist);
    -        (*numpart)++;
    -        if (*dist > bestdist) {
    -          if (!facet->upperdelaunay || *dist >= qh MINoutside) {
    -            bestfacet= facet;
    -            if (isdistoutside && *dist >= distoutside)
    -              goto LABELreturn_bestnew;
    -            bestdist= *dist;
    -          }
    -        }
    -      } /* end of !flipped */
    -    } /* FORALLfacet from startfacet or qh newfacet_list */
    -  }
    -  if (testhorizon || !bestfacet)
    -    bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, bestfacet ? bestfacet : startfacet,
    -                                        !qh_NOupper, &bestdist, numpart);
    -  *dist= bestdist;
    -  if (isoutside && *dist < qh MINoutside)
    -    *isoutside= False;
    -LABELreturn_bestnew:
    -  zadd_(Zfindnewtot, *numpart);
    -  zmax_(Zfindnewmax, *numpart);
    -  trace4((qh ferr, 4004, "qh_findbestnew: bestfacet f%d bestdist %2.2g\n", getid_(bestfacet), *dist));
    -  qh IStracing= oldtrace;
    -  return bestfacet;
    -}  /* findbestnew */
    -
    -/* ============ hyperplane functions -- keep code together [?] ============ */
    -
    -/*---------------------------------
    -
    -  qh_backnormal( rows, numrow, numcol, sign, normal, nearzero )
    -    given an upper-triangular rows array and a sign,
    -    solve for normal equation x using back substitution over rows U
    -
    -  returns:
    -     normal= x
    -
    -     if will not be able to divzero() when normalized(qh.MINdenom_2 and qh.MINdenom_1_2),
    -       if fails on last row
    -         this means that the hyperplane intersects [0,..,1]
    -         sets last coordinate of normal to sign
    -       otherwise
    -         sets tail of normal to [...,sign,0,...], i.e., solves for b= [0...0]
    -         sets nearzero
    -
    -  notes:
    -     assumes numrow == numcol-1
    -
    -     see Golub & van Loan 4.4-9 for back substitution
    -
    -     solves Ux=b where Ax=b and PA=LU
    -     b= [0,...,0,sign or 0]  (sign is either -1 or +1)
    -     last row of A= [0,...,0,1]
    -
    -     1) Ly=Pb == y=b since P only permutes the 0's of   b
    -
    -  design:
    -    for each row from end
    -      perform back substitution
    -      if near zero
    -        use qh_divzero for division
    -        if zero divide and not last row
    -          set tail of normal to 0
    -*/
    -void qh_backnormal(realT **rows, int numrow, int numcol, boolT sign,
    -        coordT *normal, boolT *nearzero) {
    -  int i, j;
    -  coordT *normalp, *normal_tail, *ai, *ak;
    -  realT diagonal;
    -  boolT waszero;
    -  int zerocol= -1;
    -
    -  normalp= normal + numcol - 1;
    -  *normalp--= (sign ? -1.0 : 1.0);
    -  for (i=numrow; i--; ) {
    -    *normalp= 0.0;
    -    ai= rows[i] + i + 1;
    -    ak= normalp+1;
    -    for (j=i+1; j < numcol; j++)
    -      *normalp -= *ai++ * *ak++;
    -    diagonal= (rows[i])[i];
    -    if (fabs_(diagonal) > qh MINdenom_2)
    -      *(normalp--) /= diagonal;
    -    else {
    -      waszero= False;
    -      *normalp= qh_divzero(*normalp, diagonal, qh MINdenom_1_2, &waszero);
    -      if (waszero) {
    -        zerocol= i;
    -        *(normalp--)= (sign ? -1.0 : 1.0);
    -        for (normal_tail= normalp+2; normal_tail < normal + numcol; normal_tail++)
    -          *normal_tail= 0.0;
    -      }else
    -        normalp--;
    -    }
    -  }
    -  if (zerocol != -1) {
    -    zzinc_(Zback0);
    -    *nearzero= True;
    -    trace4((qh ferr, 4005, "qh_backnormal: zero diagonal at column %d.\n", i));
    -    qh_precision("zero diagonal on back substitution");
    -  }
    -} /* backnormal */
    -
    -/*---------------------------------
    -
    -  qh_gausselim( rows, numrow, numcol, sign )
    -    Gaussian elimination with partial pivoting
    -
    -  returns:
    -    rows is upper triangular (includes row exchanges)
    -    flips sign for each row exchange
    -    sets nearzero if pivot[k] < qh.NEARzero[k], else clears it
    -
    -  notes:
    -    if nearzero, the determinant's sign may be incorrect.
    -    assumes numrow <= numcol
    -
    -  design:
    -    for each row
    -      determine pivot and exchange rows if necessary
    -      test for near zero
    -      perform gaussian elimination step
    -*/
    -void qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero) {
    -  realT *ai, *ak, *rowp, *pivotrow;
    -  realT n, pivot, pivot_abs= 0.0, temp;
    -  int i, j, k, pivoti, flip=0;
    -
    -  *nearzero= False;
    -  for (k=0; k < numrow; k++) {
    -    pivot_abs= fabs_((rows[k])[k]);
    -    pivoti= k;
    -    for (i=k+1; i < numrow; i++) {
    -      if ((temp= fabs_((rows[i])[k])) > pivot_abs) {
    -        pivot_abs= temp;
    -        pivoti= i;
    -      }
    -    }
    -    if (pivoti != k) {
    -      rowp= rows[pivoti];
    -      rows[pivoti]= rows[k];
    -      rows[k]= rowp;
    -      *sign ^= 1;
    -      flip ^= 1;
    -    }
    -    if (pivot_abs <= qh NEARzero[k]) {
    -      *nearzero= True;
    -      if (pivot_abs == 0.0) {   /* remainder of column == 0 */
    -        if (qh IStracing >= 4) {
    -          qh_fprintf(qh ferr, 8011, "qh_gausselim: 0 pivot at column %d. (%2.2g < %2.2g)\n", k, pivot_abs, qh DISTround);
    -          qh_printmatrix(qh ferr, "Matrix:", rows, numrow, numcol);
    -        }
    -        zzinc_(Zgauss0);
    -        qh_precision("zero pivot for Gaussian elimination");
    -        goto LABELnextcol;
    -      }
    -    }
    -    pivotrow= rows[k] + k;
    -    pivot= *pivotrow++;  /* signed value of pivot, and remainder of row */
    -    for (i=k+1; i < numrow; i++) {
    -      ai= rows[i] + k;
    -      ak= pivotrow;
    -      n= (*ai++)/pivot;   /* divzero() not needed since |pivot| >= |*ai| */
    -      for (j= numcol - (k+1); j--; )
    -        *ai++ -= n * *ak++;
    -    }
    -  LABELnextcol:
    -    ;
    -  }
    -  wmin_(Wmindenom, pivot_abs);  /* last pivot element */
    -  if (qh IStracing >= 5)
    -    qh_printmatrix(qh ferr, "qh_gausselem: result", rows, numrow, numcol);
    -} /* gausselim */
    -
    -
    -/*---------------------------------
    -
    -  qh_getangle( vect1, vect2 )
    -    returns the dot product of two vectors
    -    if qh.RANDOMdist, joggles result
    -
    -  notes:
    -    the angle may be > 1.0 or < -1.0 because of roundoff errors
    -
    -*/
    -realT qh_getangle(pointT *vect1, pointT *vect2) {
    -  realT angle= 0, randr;
    -  int k;
    -
    -  for (k=qh hull_dim; k--; )
    -    angle += *vect1++ * *vect2++;
    -  if (qh RANDOMdist) {
    -    randr= qh_RANDOMint;
    -    angle += (2.0 * randr / qh_RANDOMmax - 1.0) *
    -      qh RANDOMfactor;
    -  }
    -  trace4((qh ferr, 4006, "qh_getangle: %2.2g\n", angle));
    -  return(angle);
    -} /* getangle */
    -
    -
    -/*---------------------------------
    -
    -  qh_getcenter( vertices )
    -    returns arithmetic center of a set of vertices as a new point
    -
    -  notes:
    -    allocates point array for center
    -*/
    -pointT *qh_getcenter(setT *vertices) {
    -  int k;
    -  pointT *center, *coord;
    -  vertexT *vertex, **vertexp;
    -  int count= qh_setsize(vertices);
    -
    -  if (count < 2) {
    -    qh_fprintf(qh ferr, 6003, "qhull internal error (qh_getcenter): not defined for %d points\n", count);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  center= (pointT *)qh_memalloc(qh normal_size);
    -  for (k=0; k < qh hull_dim; k++) {
    -    coord= center+k;
    -    *coord= 0.0;
    -    FOREACHvertex_(vertices)
    -      *coord += vertex->point[k];
    -    *coord /= count;
    -  }
    -  return(center);
    -} /* getcenter */
    -
    -
    -/*---------------------------------
    -
    -  qh_getcentrum( facet )
    -    returns the centrum for a facet as a new point
    -
    -  notes:
    -    allocates the centrum
    -*/
    -pointT *qh_getcentrum(facetT *facet) {
    -  realT dist;
    -  pointT *centrum, *point;
    -
    -  point= qh_getcenter(facet->vertices);
    -  zzinc_(Zcentrumtests);
    -  qh_distplane(point, facet, &dist);
    -  centrum= qh_projectpoint(point, facet, dist);
    -  qh_memfree(point, qh normal_size);
    -  trace4((qh ferr, 4007, "qh_getcentrum: for f%d, %d vertices dist= %2.2g\n",
    -          facet->id, qh_setsize(facet->vertices), dist));
    -  return centrum;
    -} /* getcentrum */
    -
    -
    -/*---------------------------------
    -
    -  qh_getdistance( facet, neighbor, mindist, maxdist )
    -    returns the maxdist and mindist distance of any vertex from neighbor
    -
    -  returns:
    -    the max absolute value
    -
    -  design:
    -    for each vertex of facet that is not in neighbor
    -      test the distance from vertex to neighbor
    -*/
    -realT qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist) {
    -  vertexT *vertex, **vertexp;
    -  realT dist, maxd, mind;
    -
    -  FOREACHvertex_(facet->vertices)
    -    vertex->seen= False;
    -  FOREACHvertex_(neighbor->vertices)
    -    vertex->seen= True;
    -  mind= 0.0;
    -  maxd= 0.0;
    -  FOREACHvertex_(facet->vertices) {
    -    if (!vertex->seen) {
    -      zzinc_(Zbestdist);
    -      qh_distplane(vertex->point, neighbor, &dist);
    -      if (dist < mind)
    -        mind= dist;
    -      else if (dist > maxd)
    -        maxd= dist;
    -    }
    -  }
    -  *mindist= mind;
    -  *maxdist= maxd;
    -  mind= -mind;
    -  if (maxd > mind)
    -    return maxd;
    -  else
    -    return mind;
    -} /* getdistance */
    -
    -
    -/*---------------------------------
    -
    -  qh_normalize( normal, dim, toporient )
    -    normalize a vector and report if too small
    -    does not use min norm
    -
    -  see:
    -    qh_normalize2
    -*/
    -void qh_normalize(coordT *normal, int dim, boolT toporient) {
    -  qh_normalize2( normal, dim, toporient, NULL, NULL);
    -} /* normalize */
    -
    -/*---------------------------------
    -
    -  qh_normalize2( normal, dim, toporient, minnorm, ismin )
    -    normalize a vector and report if too small
    -    qh.MINdenom/MINdenom1 are the upper limits for divide overflow
    -
    -  returns:
    -    normalized vector
    -    flips sign if !toporient
    -    if minnorm non-NULL,
    -      sets ismin if normal < minnorm
    -
    -  notes:
    -    if zero norm
    -       sets all elements to sqrt(1.0/dim)
    -    if divide by zero (divzero())
    -       sets largest element to   +/-1
    -       bumps Znearlysingular
    -
    -  design:
    -    computes norm
    -    test for minnorm
    -    if not near zero
    -      normalizes normal
    -    else if zero norm
    -      sets normal to standard value
    -    else
    -      uses qh_divzero to normalize
    -      if nearzero
    -        sets norm to direction of maximum value
    -*/
    -void qh_normalize2 (coordT *normal, int dim, boolT toporient,
    -            realT *minnorm, boolT *ismin) {
    -  int k;
    -  realT *colp, *maxp, norm= 0, temp, *norm1, *norm2, *norm3;
    -  boolT zerodiv;
    -
    -  norm1= normal+1;
    -  norm2= normal+2;
    -  norm3= normal+3;
    -  if (dim == 2)
    -    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1));
    -  else if (dim == 3)
    -    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2));
    -  else if (dim == 4) {
    -    norm= sqrt((*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
    -               + (*norm3)*(*norm3));
    -  }else if (dim > 4) {
    -    norm= (*normal)*(*normal) + (*norm1)*(*norm1) + (*norm2)*(*norm2)
    -               + (*norm3)*(*norm3);
    -    for (k=dim-4, colp=normal+4; k--; colp++)
    -      norm += (*colp) * (*colp);
    -    norm= sqrt(norm);
    -  }
    -  if (minnorm) {
    -    if (norm < *minnorm)
    -      *ismin= True;
    -    else
    -      *ismin= False;
    -  }
    -  wmin_(Wmindenom, norm);
    -  if (norm > qh MINdenom) {
    -    if (!toporient)
    -      norm= -norm;
    -    *normal /= norm;
    -    *norm1 /= norm;
    -    if (dim == 2)
    -      ; /* all done */
    -    else if (dim == 3)
    -      *norm2 /= norm;
    -    else if (dim == 4) {
    -      *norm2 /= norm;
    -      *norm3 /= norm;
    -    }else if (dim >4) {
    -      *norm2 /= norm;
    -      *norm3 /= norm;
    -      for (k=dim-4, colp=normal+4; k--; )
    -        *colp++ /= norm;
    -    }
    -  }else if (norm == 0.0) {
    -    temp= sqrt(1.0/dim);
    -    for (k=dim, colp=normal; k--; )
    -      *colp++ = temp;
    -  }else {
    -    if (!toporient)
    -      norm= -norm;
    -    for (k=dim, colp=normal; k--; colp++) { /* k used below */
    -      temp= qh_divzero(*colp, norm, qh MINdenom_1, &zerodiv);
    -      if (!zerodiv)
    -        *colp= temp;
    -      else {
    -        maxp= qh_maxabsval(normal, dim);
    -        temp= ((*maxp * norm >= 0.0) ? 1.0 : -1.0);
    -        for (k=dim, colp=normal; k--; colp++)
    -          *colp= 0.0;
    -        *maxp= temp;
    -        zzinc_(Znearlysingular);
    -        trace0((qh ferr, 1, "qh_normalize: norm=%2.2g too small during p%d\n",
    -               norm, qh furthest_id));
    -        return;
    -      }
    -    }
    -  }
    -} /* normalize */
    -
    -
    -/*---------------------------------
    -
    -  qh_projectpoint( point, facet, dist )
    -    project point onto a facet by dist
    -
    -  returns:
    -    returns a new point
    -
    -  notes:
    -    if dist= distplane(point,facet)
    -      this projects point to hyperplane
    -    assumes qh_memfree_() is valid for normal_size
    -*/
    -pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist) {
    -  pointT *newpoint, *np, *normal;
    -  int normsize= qh normal_size;
    -  int k;
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  qh_memalloc_(normsize, freelistp, newpoint, pointT);
    -  np= newpoint;
    -  normal= facet->normal;
    -  for (k=qh hull_dim; k--; )
    -    *(np++)= *point++ - dist * *normal++;
    -  return(newpoint);
    -} /* projectpoint */
    -
    -
    -/*---------------------------------
    -
    -  qh_setfacetplane( facet )
    -    sets the hyperplane for a facet
    -    if qh.RANDOMdist, joggles hyperplane
    -
    -  notes:
    -    uses global buffers qh.gm_matrix and qh.gm_row
    -    overwrites facet->normal if already defined
    -    updates Wnewvertex if PRINTstatistics
    -    sets facet->upperdelaunay if upper envelope of Delaunay triangulation
    -
    -  design:
    -    copy vertex coordinates to qh.gm_matrix/gm_row
    -    compute determinate
    -    if nearzero
    -      recompute determinate with gaussian elimination
    -      if nearzero
    -        force outside orientation by testing interior point
    -*/
    -void qh_setfacetplane(facetT *facet) {
    -  pointT *point;
    -  vertexT *vertex, **vertexp;
    -  int normsize= qh normal_size;
    -  int k,i, oldtrace= 0;
    -  realT dist;
    -  void **freelistp; /* used !qh_NOmem */
    -  coordT *coord, *gmcoord;
    -  pointT *point0= SETfirstt_(facet->vertices, vertexT)->point;
    -  boolT nearzero= False;
    -
    -  zzinc_(Zsetplane);
    -  if (!facet->normal)
    -    qh_memalloc_(normsize, freelistp, facet->normal, coordT);
    -  if (facet == qh tracefacet) {
    -    oldtrace= qh IStracing;
    -    qh IStracing= 5;
    -    qh_fprintf(qh ferr, 8012, "qh_setfacetplane: facet f%d created.\n", facet->id);
    -    qh_fprintf(qh ferr, 8013, "  Last point added to hull was p%d.", qh furthest_id);
    -    if (zzval_(Ztotmerge))
    -      qh_fprintf(qh ferr, 8014, "  Last merge was #%d.", zzval_(Ztotmerge));
    -    qh_fprintf(qh ferr, 8015, "\n\nCurrent summary is:\n");
    -      qh_printsummary(qh ferr);
    -  }
    -  if (qh hull_dim <= 4) {
    -    i= 0;
    -    if (qh RANDOMdist) {
    -      gmcoord= qh gm_matrix;
    -      FOREACHvertex_(facet->vertices) {
    -        qh gm_row[i++]= gmcoord;
    -        coord= vertex->point;
    -        for (k=qh hull_dim; k--; )
    -          *(gmcoord++)= *coord++ * qh_randomfactor(qh RANDOMa, qh RANDOMb);
    -      }
    -    }else {
    -      FOREACHvertex_(facet->vertices)
    -       qh gm_row[i++]= vertex->point;
    -    }
    -    qh_sethyperplane_det(qh hull_dim, qh gm_row, point0, facet->toporient,
    -                facet->normal, &facet->offset, &nearzero);
    -  }
    -  if (qh hull_dim > 4 || nearzero) {
    -    i= 0;
    -    gmcoord= qh gm_matrix;
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->point != point0) {
    -        qh gm_row[i++]= gmcoord;
    -        coord= vertex->point;
    -        point= point0;
    -        for (k=qh hull_dim; k--; )
    -          *(gmcoord++)= *coord++ - *point++;
    -      }
    -    }
    -    qh gm_row[i]= gmcoord;  /* for areasimplex */
    -    if (qh RANDOMdist) {
    -      gmcoord= qh gm_matrix;
    -      for (i=qh hull_dim-1; i--; ) {
    -        for (k=qh hull_dim; k--; )
    -          *(gmcoord++) *= qh_randomfactor(qh RANDOMa, qh RANDOMb);
    -      }
    -    }
    -    qh_sethyperplane_gauss(qh hull_dim, qh gm_row, point0, facet->toporient,
    -                facet->normal, &facet->offset, &nearzero);
    -    if (nearzero) {
    -      if (qh_orientoutside(facet)) {
    -        trace0((qh ferr, 2, "qh_setfacetplane: flipped orientation after testing interior_point during p%d\n", qh furthest_id));
    -      /* this is part of using Gaussian Elimination.  For example in 5-d
    -           1 1 1 1 0
    -           1 1 1 1 1
    -           0 0 0 1 0
    -           0 1 0 0 0
    -           1 0 0 0 0
    -           norm= 0.38 0.38 -0.76 0.38 0
    -         has a determinate of 1, but g.e. after subtracting pt. 0 has
    -         0's in the diagonal, even with full pivoting.  It does work
    -         if you subtract pt. 4 instead. */
    -      }
    -    }
    -  }
    -  facet->upperdelaunay= False;
    -  if (qh DELAUNAY) {
    -    if (qh UPPERdelaunay) {     /* matches qh_triangulate_facet and qh.lower_threshold in qh_initbuild */
    -      if (facet->normal[qh hull_dim -1] >= qh ANGLEround * qh_ZEROdelaunay)
    -        facet->upperdelaunay= True;
    -    }else {
    -      if (facet->normal[qh hull_dim -1] > -qh ANGLEround * qh_ZEROdelaunay)
    -        facet->upperdelaunay= True;
    -    }
    -  }
    -  if (qh PRINTstatistics || qh IStracing || qh TRACElevel || qh JOGGLEmax < REALmax) {
    -    qh old_randomdist= qh RANDOMdist;
    -    qh RANDOMdist= False;
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->point != point0) {
    -        boolT istrace= False;
    -        zinc_(Zdiststat);
    -        qh_distplane(vertex->point, facet, &dist);
    -        dist= fabs_(dist);
    -        zinc_(Znewvertex);
    -        wadd_(Wnewvertex, dist);
    -        if (dist > wwval_(Wnewvertexmax)) {
    -          wwval_(Wnewvertexmax)= dist;
    -          if (dist > qh max_outside) {
    -            qh max_outside= dist;  /* used by qh_maxouter() */
    -            if (dist > qh TRACEdist)
    -              istrace= True;
    -          }
    -        }else if (-dist > qh TRACEdist)
    -          istrace= True;
    -        if (istrace) {
    -          qh_fprintf(qh ferr, 8016, "qh_setfacetplane: ====== vertex p%d(v%d) increases max_outside to %2.2g for new facet f%d last p%d\n",
    -                qh_pointid(vertex->point), vertex->id, dist, facet->id, qh furthest_id);
    -          qh_errprint("DISTANT", facet, NULL, NULL, NULL);
    -        }
    -      }
    -    }
    -    qh RANDOMdist= qh old_randomdist;
    -  }
    -  if (qh IStracing >= 3) {
    -    qh_fprintf(qh ferr, 8017, "qh_setfacetplane: f%d offset %2.2g normal: ",
    -             facet->id, facet->offset);
    -    for (k=0; k < qh hull_dim; k++)
    -      qh_fprintf(qh ferr, 8018, "%2.2g ", facet->normal[k]);
    -    qh_fprintf(qh ferr, 8019, "\n");
    -  }
    -  if (facet == qh tracefacet)
    -    qh IStracing= oldtrace;
    -} /* setfacetplane */
    -
    -
    -/*---------------------------------
    -
    -  qh_sethyperplane_det( dim, rows, point0, toporient, normal, offset, nearzero )
    -    given dim X dim array indexed by rows[], one row per point,
    -        toporient(flips all signs),
    -        and point0 (any row)
    -    set normalized hyperplane equation from oriented simplex
    -
    -  returns:
    -    normal (normalized)
    -    offset (places point0 on the hyperplane)
    -    sets nearzero if hyperplane not through points
    -
    -  notes:
    -    only defined for dim == 2..4
    -    rows[] is not modified
    -    solves det(P-V_0, V_n-V_0, ..., V_1-V_0)=0, i.e. every point is on hyperplane
    -    see Bower & Woodworth, A programmer's geometry, Butterworths 1983.
    -
    -  derivation of 3-d minnorm
    -    Goal: all vertices V_i within qh.one_merge of hyperplane
    -    Plan: exactly translate the facet so that V_0 is the origin
    -          exactly rotate the facet so that V_1 is on the x-axis and y_2=0.
    -          exactly rotate the effective perturbation to only effect n_0
    -             this introduces a factor of sqrt(3)
    -    n_0 = ((y_2-y_0)*(z_1-z_0) - (z_2-z_0)*(y_1-y_0)) / norm
    -    Let M_d be the max coordinate difference
    -    Let M_a be the greater of M_d and the max abs. coordinate
    -    Let u be machine roundoff and distround be max error for distance computation
    -    The max error for n_0 is sqrt(3) u M_a M_d / norm.  n_1 is approx. 1 and n_2 is approx. 0
    -    The max error for distance of V_1 is sqrt(3) u M_a M_d M_d / norm.  Offset=0 at origin
    -    Then minnorm = 1.8 u M_a M_d M_d / qh.ONEmerge
    -    Note that qh.one_merge is approx. 45.5 u M_a and norm is usually about M_d M_d
    -
    -  derivation of 4-d minnorm
    -    same as above except rotate the facet so that V_1 on x-axis and w_2, y_3, w_3=0
    -     [if two vertices fixed on x-axis, can rotate the other two in yzw.]
    -    n_0 = det3_(...) = y_2 det2_(z_1, w_1, z_3, w_3) = - y_2 w_1 z_3
    -     [all other terms contain at least two factors nearly zero.]
    -    The max error for n_0 is sqrt(4) u M_a M_d M_d / norm
    -    Then minnorm = 2 u M_a M_d M_d M_d / qh.ONEmerge
    -    Note that qh.one_merge is approx. 82 u M_a and norm is usually about M_d M_d M_d
    -*/
    -void qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
    -          boolT toporient, coordT *normal, realT *offset, boolT *nearzero) {
    -  realT maxround, dist;
    -  int i;
    -  pointT *point;
    -
    -
    -  if (dim == 2) {
    -    normal[0]= dY(1,0);
    -    normal[1]= dX(0,1);
    -    qh_normalize2 (normal, dim, toporient, NULL, NULL);
    -    *offset= -(point0[0]*normal[0]+point0[1]*normal[1]);
    -    *nearzero= False;  /* since nearzero norm => incident points */
    -  }else if (dim == 3) {
    -    normal[0]= det2_(dY(2,0), dZ(2,0),
    -                     dY(1,0), dZ(1,0));
    -    normal[1]= det2_(dX(1,0), dZ(1,0),
    -                     dX(2,0), dZ(2,0));
    -    normal[2]= det2_(dX(2,0), dY(2,0),
    -                     dX(1,0), dY(1,0));
    -    qh_normalize2 (normal, dim, toporient, NULL, NULL);
    -    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
    -               + point0[2]*normal[2]);
    -    maxround= qh DISTround;
    -    for (i=dim; i--; ) {
    -      point= rows[i];
    -      if (point != point0) {
    -        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
    -               + point[2]*normal[2]);
    -        if (dist > maxround || dist < -maxround) {
    -          *nearzero= True;
    -          break;
    -        }
    -      }
    -    }
    -  }else if (dim == 4) {
    -    normal[0]= - det3_(dY(2,0), dZ(2,0), dW(2,0),
    -                        dY(1,0), dZ(1,0), dW(1,0),
    -                        dY(3,0), dZ(3,0), dW(3,0));
    -    normal[1]=   det3_(dX(2,0), dZ(2,0), dW(2,0),
    -                        dX(1,0), dZ(1,0), dW(1,0),
    -                        dX(3,0), dZ(3,0), dW(3,0));
    -    normal[2]= - det3_(dX(2,0), dY(2,0), dW(2,0),
    -                        dX(1,0), dY(1,0), dW(1,0),
    -                        dX(3,0), dY(3,0), dW(3,0));
    -    normal[3]=   det3_(dX(2,0), dY(2,0), dZ(2,0),
    -                        dX(1,0), dY(1,0), dZ(1,0),
    -                        dX(3,0), dY(3,0), dZ(3,0));
    -    qh_normalize2 (normal, dim, toporient, NULL, NULL);
    -    *offset= -(point0[0]*normal[0] + point0[1]*normal[1]
    -               + point0[2]*normal[2] + point0[3]*normal[3]);
    -    maxround= qh DISTround;
    -    for (i=dim; i--; ) {
    -      point= rows[i];
    -      if (point != point0) {
    -        dist= *offset + (point[0]*normal[0] + point[1]*normal[1]
    -               + point[2]*normal[2] + point[3]*normal[3]);
    -        if (dist > maxround || dist < -maxround) {
    -          *nearzero= True;
    -          break;
    -        }
    -      }
    -    }
    -  }
    -  if (*nearzero) {
    -    zzinc_(Zminnorm);
    -    trace0((qh ferr, 3, "qh_sethyperplane_det: degenerate norm during p%d.\n", qh furthest_id));
    -    zzinc_(Znearlysingular);
    -  }
    -} /* sethyperplane_det */
    -
    -
    -/*---------------------------------
    -
    -  qh_sethyperplane_gauss( dim, rows, point0, toporient, normal, offset, nearzero )
    -    given(dim-1) X dim array of rows[i]= V_{i+1} - V_0 (point0)
    -    set normalized hyperplane equation from oriented simplex
    -
    -  returns:
    -    normal (normalized)
    -    offset (places point0 on the hyperplane)
    -
    -  notes:
    -    if nearzero
    -      orientation may be incorrect because of incorrect sign flips in gausselim
    -    solves [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0 .. 0 1]
    -        or [V_n-V_0,...,V_1-V_0, 0 .. 0 1] * N == [0]
    -    i.e., N is normal to the hyperplane, and the unnormalized
    -        distance to [0 .. 1] is either 1 or   0
    -
    -  design:
    -    perform gaussian elimination
    -    flip sign for negative values
    -    perform back substitution
    -    normalize result
    -    compute offset
    -*/
    -void qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
    -                boolT toporient, coordT *normal, coordT *offset, boolT *nearzero) {
    -  coordT *pointcoord, *normalcoef;
    -  int k;
    -  boolT sign= toporient, nearzero2= False;
    -
    -  qh_gausselim(rows, dim-1, dim, &sign, nearzero);
    -  for (k=dim-1; k--; ) {
    -    if ((rows[k])[k] < 0)
    -      sign ^= 1;
    -  }
    -  if (*nearzero) {
    -    zzinc_(Znearlysingular);
    -    trace0((qh ferr, 4, "qh_sethyperplane_gauss: nearly singular or axis parallel hyperplane during p%d.\n", qh furthest_id));
    -    qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
    -  }else {
    -    qh_backnormal(rows, dim-1, dim, sign, normal, &nearzero2);
    -    if (nearzero2) {
    -      zzinc_(Znearlysingular);
    -      trace0((qh ferr, 5, "qh_sethyperplane_gauss: singular or axis parallel hyperplane at normalization during p%d.\n", qh furthest_id));
    -    }
    -  }
    -  if (nearzero2)
    -    *nearzero= True;
    -  qh_normalize2(normal, dim, True, NULL, NULL);
    -  pointcoord= point0;
    -  normalcoef= normal;
    -  *offset= -(*pointcoord++ * *normalcoef++);
    -  for (k=dim-1; k--; )
    -    *offset -= *pointcoord++ * *normalcoef++;
    -} /* sethyperplane_gauss */
    diff --git a/extern/qhull/geom.h b/extern/qhull/geom.h
    deleted file mode 100644
    index b1a3aeccc7c0..000000000000
    --- a/extern/qhull/geom.h
    +++ /dev/null
    @@ -1,173 +0,0 @@
    -/*
      ---------------------------------
    -
    -  geom.h
    -    header file for geometric routines
    -
    -   see qh-geom.htm and geom.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/geom.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFgeom
    -#define qhDEFgeom 1
    -
    -#include "libqhull.h"
    -
    -/* ============ -macros- ======================== */
    -
    -/*----------------------------------
    -
    -  fabs_(a)
    -    returns the absolute value of a
    -*/
    -#define fabs_( a ) ((( a ) < 0 ) ? -( a ):( a ))
    -
    -/*----------------------------------
    -
    -  fmax_(a,b)
    -    returns the maximum value of a and b
    -*/
    -#define fmax_( a,b )  ( ( a ) < ( b ) ? ( b ) : ( a ) )
    -
    -/*----------------------------------
    -
    -  fmin_(a,b)
    -    returns the minimum value of a and b
    -*/
    -#define fmin_( a,b )  ( ( a ) > ( b ) ? ( b ) : ( a ) )
    -
    -/*----------------------------------
    -
    -  maximize_(maxval, val)
    -    set maxval to val if val is greater than maxval
    -*/
    -#define maximize_( maxval, val ) { if (( maxval ) < ( val )) ( maxval )= ( val ); }
    -
    -/*----------------------------------
    -
    -  minimize_(minval, val)
    -    set minval to val if val is less than minval
    -*/
    -#define minimize_( minval, val ) { if (( minval ) > ( val )) ( minval )= ( val ); }
    -
    -/*----------------------------------
    -
    -  det2_(a1, a2,
    -        b1, b2)
    -
    -    compute a 2-d determinate
    -*/
    -#define det2_( a1,a2,b1,b2 ) (( a1 )*( b2 ) - ( a2 )*( b1 ))
    -
    -/*----------------------------------
    -
    -  det3_(a1, a2, a3,
    -       b1, b2, b3,
    -       c1, c2, c3)
    -
    -    compute a 3-d determinate
    -*/
    -#define det3_( a1,a2,a3,b1,b2,b3,c1,c2,c3 ) ( ( a1 )*det2_( b2,b3,c2,c3 ) \
    -                - ( b1 )*det2_( a2,a3,c2,c3 ) + ( c1 )*det2_( a2,a3,b2,b3 ) )
    -
    -/*----------------------------------
    -
    -  dX( p1, p2 )
    -  dY( p1, p2 )
    -  dZ( p1, p2 )
    -
    -    given two indices into rows[],
    -
    -    compute the difference between X, Y, or Z coordinates
    -*/
    -#define dX( p1,p2 )  ( *( rows[p1] ) - *( rows[p2] ))
    -#define dY( p1,p2 )  ( *( rows[p1]+1 ) - *( rows[p2]+1 ))
    -#define dZ( p1,p2 )  ( *( rows[p1]+2 ) - *( rows[p2]+2 ))
    -#define dW( p1,p2 )  ( *( rows[p1]+3 ) - *( rows[p2]+3 ))
    -
    -/*============= prototypes in alphabetical order, infrequent at end ======= */
    -
    -void    qh_backnormal(realT **rows, int numrow, int numcol, boolT sign, coordT *normal, boolT *nearzero);
    -void    qh_distplane(pointT *point, facetT *facet, realT *dist);
    -facetT *qh_findbest(pointT *point, facetT *startfacet,
    -                     boolT bestoutside, boolT isnewfacets, boolT noupper,
    -                     realT *dist, boolT *isoutside, int *numpart);
    -facetT *qh_findbesthorizon(boolT ischeckmax, pointT *point,
    -                     facetT *startfacet, boolT noupper, realT *bestdist, int *numpart);
    -facetT *qh_findbestnew(pointT *point, facetT *startfacet, realT *dist,
    -                     boolT bestoutside, boolT *isoutside, int *numpart);
    -void    qh_gausselim(realT **rows, int numrow, int numcol, boolT *sign, boolT *nearzero);
    -realT   qh_getangle(pointT *vect1, pointT *vect2);
    -pointT *qh_getcenter(setT *vertices);
    -pointT *qh_getcentrum(facetT *facet);
    -realT   qh_getdistance(facetT *facet, facetT *neighbor, realT *mindist, realT *maxdist);
    -void    qh_normalize(coordT *normal, int dim, boolT toporient);
    -void    qh_normalize2 (coordT *normal, int dim, boolT toporient,
    -            realT *minnorm, boolT *ismin);
    -pointT *qh_projectpoint(pointT *point, facetT *facet, realT dist);
    -
    -void    qh_setfacetplane(facetT *newfacets);
    -void    qh_sethyperplane_det(int dim, coordT **rows, coordT *point0,
    -              boolT toporient, coordT *normal, realT *offset, boolT *nearzero);
    -void    qh_sethyperplane_gauss(int dim, coordT **rows, pointT *point0,
    -             boolT toporient, coordT *normal, coordT *offset, boolT *nearzero);
    -boolT   qh_sharpnewfacets(void);
    -
    -/*========= infrequently used code in geom2.c =============*/
    -
    -coordT *qh_copypoints(coordT *points, int numpoints, int dimension);
    -void    qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]);
    -realT   qh_determinant(realT **rows, int dim, boolT *nearzero);
    -realT   qh_detjoggle(pointT *points, int numpoints, int dimension);
    -void    qh_detroundoff(void);
    -realT   qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero);
    -realT   qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp);
    -realT   qh_distround(int dimension, realT maxabs, realT maxsumabs);
    -realT   qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv);
    -realT   qh_facetarea(facetT *facet);
    -realT   qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
    -          vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset);
    -pointT *qh_facetcenter(setT *vertices);
    -facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp, facetT **facetlist);
    -void    qh_getarea(facetT *facetlist);
    -boolT   qh_gram_schmidt(int dim, realT **rows);
    -boolT   qh_inthresholds(coordT *normal, realT *angle);
    -void    qh_joggleinput(void);
    -realT  *qh_maxabsval(realT *normal, int dim);
    -setT   *qh_maxmin(pointT *points, int numpoints, int dimension);
    -realT   qh_maxouter(void);
    -void    qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex);
    -realT   qh_minabsval(realT *normal, int dim);
    -int     qh_mindiff(realT *vecA, realT *vecB, int dim);
    -boolT   qh_orientoutside(facetT *facet);
    -void    qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
    -coordT  qh_pointdist(pointT *point1, pointT *point2, int dim);
    -void    qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol);
    -void    qh_printpoints(FILE *fp, const char *string, setT *points);
    -void    qh_projectinput(void);
    -void    qh_projectpoints(signed char *project, int n, realT *points,
    -             int numpoints, int dim, realT *newpoints, int newdim);
    -void    qh_rotateinput(realT **rows);
    -void    qh_rotatepoints(realT *points, int numpoints, int dim, realT **rows);
    -void    qh_scaleinput(void);
    -void    qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
    -                   coordT high, coordT newhigh);
    -void    qh_scalepoints(pointT *points, int numpoints, int dim,
    -                realT *newlows, realT *newhighs);
    -boolT   qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
    -              coordT *normal, coordT *offset, coordT *feasible);
    -coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
    -pointT *qh_voronoi_center(int dim, setT *points);
    -
    -#endif /* qhDEFgeom */
    diff --git a/extern/qhull/geom2.c b/extern/qhull/geom2.c
    deleted file mode 100644
    index 2ba4a9f251f1..000000000000
    --- a/extern/qhull/geom2.c
    +++ /dev/null
    @@ -1,2080 +0,0 @@
    -/*
      ---------------------------------
    -
    -
    -   geom2.c
    -   infrequently used geometric routines of qhull
    -
    -   see qh-geom.htm and geom.h
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/geom2.c#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -
    -   frequently used code goes into geom.c
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*================== functions in alphabetic order ============*/
    -
    -/*---------------------------------
    -
    -  qh_copypoints( points, numpoints, dimension)
    -    return qh_malloc'd copy of points
    -*/
    -coordT *qh_copypoints(coordT *points, int numpoints, int dimension) {
    -  int size;
    -  coordT *newpoints;
    -
    -  size= numpoints * dimension * (int)sizeof(coordT);
    -  if (!(newpoints=(coordT*)qh_malloc((size_t)size))) {
    -    qh_fprintf(qh ferr, 6004, "qhull error: insufficient memory to copy %d points\n",
    -        numpoints);
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  memcpy((char *)newpoints, (char *)points, (size_t)size);
    -  return newpoints;
    -} /* copypoints */
    -
    -/*---------------------------------
    -
    -  qh_crossproduct( dim, vecA, vecB, vecC )
    -    crossproduct of 2 dim vectors
    -    C= A x B
    -
    -  notes:
    -    from Glasner, Graphics Gems I, p. 639
    -    only defined for dim==3
    -*/
    -void qh_crossproduct(int dim, realT vecA[3], realT vecB[3], realT vecC[3]){
    -
    -  if (dim == 3) {
    -    vecC[0]=   det2_(vecA[1], vecA[2],
    -                     vecB[1], vecB[2]);
    -    vecC[1]= - det2_(vecA[0], vecA[2],
    -                     vecB[0], vecB[2]);
    -    vecC[2]=   det2_(vecA[0], vecA[1],
    -                     vecB[0], vecB[1]);
    -  }
    -} /* vcross */
    -
    -/*---------------------------------
    -
    -  qh_determinant( rows, dim, nearzero )
    -    compute signed determinant of a square matrix
    -    uses qh.NEARzero to test for degenerate matrices
    -
    -  returns:
    -    determinant
    -    overwrites rows and the matrix
    -    if dim == 2 or 3
    -      nearzero iff determinant < qh NEARzero[dim-1]
    -      (!quite correct, not critical)
    -    if dim >= 4
    -      nearzero iff diagonal[k] < qh NEARzero[k]
    -*/
    -realT qh_determinant(realT **rows, int dim, boolT *nearzero) {
    -  realT det=0;
    -  int i;
    -  boolT sign= False;
    -
    -  *nearzero= False;
    -  if (dim < 2) {
    -    qh_fprintf(qh ferr, 6005, "qhull internal error (qh_determinate): only implemented for dimension >= 2\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }else if (dim == 2) {
    -    det= det2_(rows[0][0], rows[0][1],
    -                 rows[1][0], rows[1][1]);
    -    if (fabs_(det) < qh NEARzero[1])  /* not really correct, what should this be? */
    -      *nearzero= True;
    -  }else if (dim == 3) {
    -    det= det3_(rows[0][0], rows[0][1], rows[0][2],
    -                 rows[1][0], rows[1][1], rows[1][2],
    -                 rows[2][0], rows[2][1], rows[2][2]);
    -    if (fabs_(det) < qh NEARzero[2])  /* not really correct, what should this be? */
    -      *nearzero= True;
    -  }else {
    -    qh_gausselim(rows, dim, dim, &sign, nearzero);  /* if nearzero, diagonal still ok*/
    -    det= 1.0;
    -    for (i=dim; i--; )
    -      det *= (rows[i])[i];
    -    if (sign)
    -      det= -det;
    -  }
    -  return det;
    -} /* determinant */
    -
    -/*---------------------------------
    -
    -  qh_detjoggle( points, numpoints, dimension )
    -    determine default max joggle for point array
    -      as qh_distround * qh_JOGGLEdefault
    -
    -  returns:
    -    initial value for JOGGLEmax from points and REALepsilon
    -
    -  notes:
    -    computes DISTround since qh_maxmin not called yet
    -    if qh SCALElast, last dimension will be scaled later to MAXwidth
    -
    -    loop duplicated from qh_maxmin
    -*/
    -realT qh_detjoggle(pointT *points, int numpoints, int dimension) {
    -  realT abscoord, distround, joggle, maxcoord, mincoord;
    -  pointT *point, *pointtemp;
    -  realT maxabs= -REALmax;
    -  realT sumabs= 0;
    -  realT maxwidth= 0;
    -  int k;
    -
    -  for (k=0; k < dimension; k++) {
    -    if (qh SCALElast && k == dimension-1)
    -      abscoord= maxwidth;
    -    else if (qh DELAUNAY && k == dimension-1) /* will qh_setdelaunay() */
    -      abscoord= 2 * maxabs * maxabs;  /* may be low by qh hull_dim/2 */
    -    else {
    -      maxcoord= -REALmax;
    -      mincoord= REALmax;
    -      FORALLpoint_(points, numpoints) {
    -        maximize_(maxcoord, point[k]);
    -        minimize_(mincoord, point[k]);
    -      }
    -      maximize_(maxwidth, maxcoord-mincoord);
    -      abscoord= fmax_(maxcoord, -mincoord);
    -    }
    -    sumabs += abscoord;
    -    maximize_(maxabs, abscoord);
    -  } /* for k */
    -  distround= qh_distround(qh hull_dim, maxabs, sumabs);
    -  joggle= distround * qh_JOGGLEdefault;
    -  maximize_(joggle, REALepsilon * qh_JOGGLEdefault);
    -  trace2((qh ferr, 2001, "qh_detjoggle: joggle=%2.2g maxwidth=%2.2g\n", joggle, maxwidth));
    -  return joggle;
    -} /* detjoggle */
    -
    -/*---------------------------------
    -
    -  qh_detroundoff()
    -    determine maximum roundoff errors from
    -      REALepsilon, REALmax, REALmin, qh.hull_dim, qh.MAXabs_coord,
    -      qh.MAXsumcoord, qh.MAXwidth, qh.MINdenom_1
    -
    -    accounts for qh.SETroundoff, qh.RANDOMdist, qh MERGEexact
    -      qh.premerge_cos, qh.postmerge_cos, qh.premerge_centrum,
    -      qh.postmerge_centrum, qh.MINoutside,
    -      qh_RATIOnearinside, qh_COPLANARratio, qh_WIDEcoplanar
    -
    -  returns:
    -    sets qh.DISTround, etc. (see below)
    -    appends precision constants to qh.qhull_options
    -
    -  see:
    -    qh_maxmin() for qh.NEARzero
    -
    -  design:
    -    determine qh.DISTround for distance computations
    -    determine minimum denominators for qh_divzero
    -    determine qh.ANGLEround for angle computations
    -    adjust qh.premerge_cos,... for roundoff error
    -    determine qh.ONEmerge for maximum error due to a single merge
    -    determine qh.NEARinside, qh.MAXcoplanar, qh.MINvisible,
    -      qh.MINoutside, qh.WIDEfacet
    -    initialize qh.max_vertex and qh.minvertex
    -*/
    -void qh_detroundoff(void) {
    -
    -  qh_option("_max-width", NULL, &qh MAXwidth);
    -  if (!qh SETroundoff) {
    -    qh DISTround= qh_distround(qh hull_dim, qh MAXabs_coord, qh MAXsumcoord);
    -    if (qh RANDOMdist)
    -      qh DISTround += qh RANDOMfactor * qh MAXabs_coord;
    -    qh_option("Error-roundoff", NULL, &qh DISTround);
    -  }
    -  qh MINdenom= qh MINdenom_1 * qh MAXabs_coord;
    -  qh MINdenom_1_2= sqrt(qh MINdenom_1 * qh hull_dim) ;  /* if will be normalized */
    -  qh MINdenom_2= qh MINdenom_1_2 * qh MAXabs_coord;
    -                                              /* for inner product */
    -  qh ANGLEround= 1.01 * qh hull_dim * REALepsilon;
    -  if (qh RANDOMdist)
    -    qh ANGLEround += qh RANDOMfactor;
    -  if (qh premerge_cos < REALmax/2) {
    -    qh premerge_cos -= qh ANGLEround;
    -    if (qh RANDOMdist)
    -      qh_option("Angle-premerge-with-random", NULL, &qh premerge_cos);
    -  }
    -  if (qh postmerge_cos < REALmax/2) {
    -    qh postmerge_cos -= qh ANGLEround;
    -    if (qh RANDOMdist)
    -      qh_option("Angle-postmerge-with-random", NULL, &qh postmerge_cos);
    -  }
    -  qh premerge_centrum += 2 * qh DISTround;    /*2 for centrum and distplane()*/
    -  qh postmerge_centrum += 2 * qh DISTround;
    -  if (qh RANDOMdist && (qh MERGEexact || qh PREmerge))
    -    qh_option("Centrum-premerge-with-random", NULL, &qh premerge_centrum);
    -  if (qh RANDOMdist && qh POSTmerge)
    -    qh_option("Centrum-postmerge-with-random", NULL, &qh postmerge_centrum);
    -  { /* compute ONEmerge, max vertex offset for merging simplicial facets */
    -    realT maxangle= 1.0, maxrho;
    -
    -    minimize_(maxangle, qh premerge_cos);
    -    minimize_(maxangle, qh postmerge_cos);
    -    /* max diameter * sin theta + DISTround for vertex to its hyperplane */
    -    qh ONEmerge= sqrt((realT)qh hull_dim) * qh MAXwidth *
    -      sqrt(1.0 - maxangle * maxangle) + qh DISTround;
    -    maxrho= qh hull_dim * qh premerge_centrum + qh DISTround;
    -    maximize_(qh ONEmerge, maxrho);
    -    maxrho= qh hull_dim * qh postmerge_centrum + qh DISTround;
    -    maximize_(qh ONEmerge, maxrho);
    -    if (qh MERGING)
    -      qh_option("_one-merge", NULL, &qh ONEmerge);
    -  }
    -  qh NEARinside= qh ONEmerge * qh_RATIOnearinside; /* only used if qh KEEPnearinside */
    -  if (qh JOGGLEmax < REALmax/2 && (qh KEEPcoplanar || qh KEEPinside)) {
    -    realT maxdist;             /* adjust qh.NEARinside for joggle */
    -    qh KEEPnearinside= True;
    -    maxdist= sqrt((realT)qh hull_dim) * qh JOGGLEmax + qh DISTround;
    -    maxdist= 2*maxdist;        /* vertex and coplanar point can joggle in opposite directions */
    -    maximize_(qh NEARinside, maxdist);  /* must agree with qh_nearcoplanar() */
    -  }
    -  if (qh KEEPnearinside)
    -    qh_option("_near-inside", NULL, &qh NEARinside);
    -  if (qh JOGGLEmax < qh DISTround) {
    -    qh_fprintf(qh ferr, 6006, "qhull error: the joggle for 'QJn', %.2g, is below roundoff for distance computations, %.2g\n",
    -         qh JOGGLEmax, qh DISTround);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (qh MINvisible > REALmax/2) {
    -    if (!qh MERGING)
    -      qh MINvisible= qh DISTround;
    -    else if (qh hull_dim <= 3)
    -      qh MINvisible= qh premerge_centrum;
    -    else
    -      qh MINvisible= qh_COPLANARratio * qh premerge_centrum;
    -    if (qh APPROXhull && qh MINvisible > qh MINoutside)
    -      qh MINvisible= qh MINoutside;
    -    qh_option("Visible-distance", NULL, &qh MINvisible);
    -  }
    -  if (qh MAXcoplanar > REALmax/2) {
    -    qh MAXcoplanar= qh MINvisible;
    -    qh_option("U-coplanar-distance", NULL, &qh MAXcoplanar);
    -  }
    -  if (!qh APPROXhull) {             /* user may specify qh MINoutside */
    -    qh MINoutside= 2 * qh MINvisible;
    -    if (qh premerge_cos < REALmax/2)
    -      maximize_(qh MINoutside, (1- qh premerge_cos) * qh MAXabs_coord);
    -    qh_option("Width-outside", NULL, &qh MINoutside);
    -  }
    -  qh WIDEfacet= qh MINoutside;
    -  maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MAXcoplanar);
    -  maximize_(qh WIDEfacet, qh_WIDEcoplanar * qh MINvisible);
    -  qh_option("_wide-facet", NULL, &qh WIDEfacet);
    -  if (qh MINvisible > qh MINoutside + 3 * REALepsilon
    -  && !qh BESToutside && !qh FORCEoutput)
    -    qh_fprintf(qh ferr, 7001, "qhull input warning: minimum visibility V%.2g is greater than \nminimum outside W%.2g.  Flipped facets are likely.\n",
    -             qh MINvisible, qh MINoutside);
    -  qh max_vertex= qh DISTround;
    -  qh min_vertex= -qh DISTround;
    -  /* numeric constants reported in printsummary */
    -} /* detroundoff */
    -
    -/*---------------------------------
    -
    -  qh_detsimplex( apex, points, dim, nearzero )
    -    compute determinant of a simplex with point apex and base points
    -
    -  returns:
    -     signed determinant and nearzero from qh_determinant
    -
    -  notes:
    -     uses qh.gm_matrix/qh.gm_row (assumes they're big enough)
    -
    -  design:
    -    construct qm_matrix by subtracting apex from points
    -    compute determinate
    -*/
    -realT qh_detsimplex(pointT *apex, setT *points, int dim, boolT *nearzero) {
    -  pointT *coorda, *coordp, *gmcoord, *point, **pointp;
    -  coordT **rows;
    -  int k,  i=0;
    -  realT det;
    -
    -  zinc_(Zdetsimplex);
    -  gmcoord= qh gm_matrix;
    -  rows= qh gm_row;
    -  FOREACHpoint_(points) {
    -    if (i == dim)
    -      break;
    -    rows[i++]= gmcoord;
    -    coordp= point;
    -    coorda= apex;
    -    for (k=dim; k--; )
    -      *(gmcoord++)= *coordp++ - *coorda++;
    -  }
    -  if (i < dim) {
    -    qh_fprintf(qh ferr, 6007, "qhull internal error (qh_detsimplex): #points %d < dimension %d\n",
    -               i, dim);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  det= qh_determinant(rows, dim, nearzero);
    -  trace2((qh ferr, 2002, "qh_detsimplex: det=%2.2g for point p%d, dim %d, nearzero? %d\n",
    -          det, qh_pointid(apex), dim, *nearzero));
    -  return det;
    -} /* detsimplex */
    -
    -/*---------------------------------
    -
    -  qh_distnorm( dim, point, normal, offset )
    -    return distance from point to hyperplane at normal/offset
    -
    -  returns:
    -    dist
    -
    -  notes:
    -    dist > 0 if point is outside of hyperplane
    -
    -  see:
    -    qh_distplane in geom.c
    -*/
    -realT qh_distnorm(int dim, pointT *point, pointT *normal, realT *offsetp) {
    -  coordT *normalp= normal, *coordp= point;
    -  realT dist;
    -  int k;
    -
    -  dist= *offsetp;
    -  for (k=dim; k--; )
    -    dist += *(coordp++) * *(normalp++);
    -  return dist;
    -} /* distnorm */
    -
    -/*---------------------------------
    -
    -  qh_distround(dimension, maxabs, maxsumabs )
    -    compute maximum round-off error for a distance computation
    -      to a normalized hyperplane
    -    maxabs is the maximum absolute value of a coordinate
    -    maxsumabs is the maximum possible sum of absolute coordinate values
    -
    -  returns:
    -    max dist round for REALepsilon
    -
    -  notes:
    -    calculate roundoff error according to
    -    Lemma 3.2-1 of Golub and van Loan "Matrix Computation"
    -    use sqrt(dim) since one vector is normalized
    -      or use maxsumabs since one vector is < 1
    -*/
    -realT qh_distround(int dimension, realT maxabs, realT maxsumabs) {
    -  realT maxdistsum, maxround;
    -
    -  maxdistsum= sqrt((realT)dimension) * maxabs;
    -  minimize_( maxdistsum, maxsumabs);
    -  maxround= REALepsilon * (dimension * maxdistsum * 1.01 + maxabs);
    -              /* adds maxabs for offset */
    -  trace4((qh ferr, 4008, "qh_distround: %2.2g maxabs %2.2g maxsumabs %2.2g maxdistsum %2.2g\n",
    -                 maxround, maxabs, maxsumabs, maxdistsum));
    -  return maxround;
    -} /* distround */
    -
    -/*---------------------------------
    -
    -  qh_divzero( numer, denom, mindenom1, zerodiv )
    -    divide by a number that's nearly zero
    -    mindenom1= minimum denominator for dividing into 1.0
    -
    -  returns:
    -    quotient
    -    sets zerodiv and returns 0.0 if it would overflow
    -
    -  design:
    -    if numer is nearly zero and abs(numer) < abs(denom)
    -      return numer/denom
    -    else if numer is nearly zero
    -      return 0 and zerodiv
    -    else if denom/numer non-zero
    -      return numer/denom
    -    else
    -      return 0 and zerodiv
    -*/
    -realT qh_divzero(realT numer, realT denom, realT mindenom1, boolT *zerodiv) {
    -  realT temp, numerx, denomx;
    -
    -
    -  if (numer < mindenom1 && numer > -mindenom1) {
    -    numerx= fabs_(numer);
    -    denomx= fabs_(denom);
    -    if (numerx < denomx) {
    -      *zerodiv= False;
    -      return numer/denom;
    -    }else {
    -      *zerodiv= True;
    -      return 0.0;
    -    }
    -  }
    -  temp= denom/numer;
    -  if (temp > mindenom1 || temp < -mindenom1) {
    -    *zerodiv= False;
    -    return numer/denom;
    -  }else {
    -    *zerodiv= True;
    -    return 0.0;
    -  }
    -} /* divzero */
    -
    -
    -/*---------------------------------
    -
    -  qh_facetarea( facet )
    -    return area for a facet
    -
    -  notes:
    -    if non-simplicial,
    -      uses centrum to triangulate facet and sums the projected areas.
    -    if (qh DELAUNAY),
    -      computes projected area instead for last coordinate
    -    assumes facet->normal exists
    -    projecting tricoplanar facets to the hyperplane does not appear to make a difference
    -
    -  design:
    -    if simplicial
    -      compute area
    -    else
    -      for each ridge
    -        compute area from centrum to ridge
    -    negate area if upper Delaunay facet
    -*/
    -realT qh_facetarea(facetT *facet) {
    -  vertexT *apex;
    -  pointT *centrum;
    -  realT area= 0.0;
    -  ridgeT *ridge, **ridgep;
    -
    -  if (facet->simplicial) {
    -    apex= SETfirstt_(facet->vertices, vertexT);
    -    area= qh_facetarea_simplex(qh hull_dim, apex->point, facet->vertices,
    -                    apex, facet->toporient, facet->normal, &facet->offset);
    -  }else {
    -    if (qh CENTERtype == qh_AScentrum)
    -      centrum= facet->center;
    -    else
    -      centrum= qh_getcentrum(facet);
    -    FOREACHridge_(facet->ridges)
    -      area += qh_facetarea_simplex(qh hull_dim, centrum, ridge->vertices,
    -                 NULL, (boolT)(ridge->top == facet),  facet->normal, &facet->offset);
    -    if (qh CENTERtype != qh_AScentrum)
    -      qh_memfree(centrum, qh normal_size);
    -  }
    -  if (facet->upperdelaunay && qh DELAUNAY)
    -    area= -area;  /* the normal should be [0,...,1] */
    -  trace4((qh ferr, 4009, "qh_facetarea: f%d area %2.2g\n", facet->id, area));
    -  return area;
    -} /* facetarea */
    -
    -/*---------------------------------
    -
    -  qh_facetarea_simplex( dim, apex, vertices, notvertex, toporient, normal, offset )
    -    return area for a simplex defined by
    -      an apex, a base of vertices, an orientation, and a unit normal
    -    if simplicial or tricoplanar facet,
    -      notvertex is defined and it is skipped in vertices
    -
    -  returns:
    -    computes area of simplex projected to plane [normal,offset]
    -    returns 0 if vertex too far below plane (qh WIDEfacet)
    -      vertex can't be apex of tricoplanar facet
    -
    -  notes:
    -    if (qh DELAUNAY),
    -      computes projected area instead for last coordinate
    -    uses qh gm_matrix/gm_row and qh hull_dim
    -    helper function for qh_facetarea
    -
    -  design:
    -    if Notvertex
    -      translate simplex to apex
    -    else
    -      project simplex to normal/offset
    -      translate simplex to apex
    -    if Delaunay
    -      set last row/column to 0 with -1 on diagonal
    -    else
    -      set last row to Normal
    -    compute determinate
    -    scale and flip sign for area
    -*/
    -realT qh_facetarea_simplex(int dim, coordT *apex, setT *vertices,
    -        vertexT *notvertex,  boolT toporient, coordT *normal, realT *offset) {
    -  pointT *coorda, *coordp, *gmcoord;
    -  coordT **rows, *normalp;
    -  int k,  i=0;
    -  realT area, dist;
    -  vertexT *vertex, **vertexp;
    -  boolT nearzero;
    -
    -  gmcoord= qh gm_matrix;
    -  rows= qh gm_row;
    -  FOREACHvertex_(vertices) {
    -    if (vertex == notvertex)
    -      continue;
    -    rows[i++]= gmcoord;
    -    coorda= apex;
    -    coordp= vertex->point;
    -    normalp= normal;
    -    if (notvertex) {
    -      for (k=dim; k--; )
    -        *(gmcoord++)= *coordp++ - *coorda++;
    -    }else {
    -      dist= *offset;
    -      for (k=dim; k--; )
    -        dist += *coordp++ * *normalp++;
    -      if (dist < -qh WIDEfacet) {
    -        zinc_(Znoarea);
    -        return 0.0;
    -      }
    -      coordp= vertex->point;
    -      normalp= normal;
    -      for (k=dim; k--; )
    -        *(gmcoord++)= (*coordp++ - dist * *normalp++) - *coorda++;
    -    }
    -  }
    -  if (i != dim-1) {
    -    qh_fprintf(qh ferr, 6008, "qhull internal error (qh_facetarea_simplex): #points %d != dim %d -1\n",
    -               i, dim);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  rows[i]= gmcoord;
    -  if (qh DELAUNAY) {
    -    for (i=0; i < dim-1; i++)
    -      rows[i][dim-1]= 0.0;
    -    for (k=dim; k--; )
    -      *(gmcoord++)= 0.0;
    -    rows[dim-1][dim-1]= -1.0;
    -  }else {
    -    normalp= normal;
    -    for (k=dim; k--; )
    -      *(gmcoord++)= *normalp++;
    -  }
    -  zinc_(Zdetsimplex);
    -  area= qh_determinant(rows, dim, &nearzero);
    -  if (toporient)
    -    area= -area;
    -  area *= qh AREAfactor;
    -  trace4((qh ferr, 4010, "qh_facetarea_simplex: area=%2.2g for point p%d, toporient %d, nearzero? %d\n",
    -          area, qh_pointid(apex), toporient, nearzero));
    -  return area;
    -} /* facetarea_simplex */
    -
    -/*---------------------------------
    -
    -  qh_facetcenter( vertices )
    -    return Voronoi center (Voronoi vertex) for a facet's vertices
    -
    -  returns:
    -    return temporary point equal to the center
    -
    -  see:
    -    qh_voronoi_center()
    -*/
    -pointT *qh_facetcenter(setT *vertices) {
    -  setT *points= qh_settemp(qh_setsize(vertices));
    -  vertexT *vertex, **vertexp;
    -  pointT *center;
    -
    -  FOREACHvertex_(vertices)
    -    qh_setappend(&points, vertex->point);
    -  center= qh_voronoi_center(qh hull_dim-1, points);
    -  qh_settempfree(&points);
    -  return center;
    -} /* facetcenter */
    -
    -/*---------------------------------
    -
    -  qh_findgooddist( point, facetA, dist, facetlist )
    -    find best good facet visible for point from facetA
    -    assumes facetA is visible from point
    -
    -  returns:
    -    best facet, i.e., good facet that is furthest from point
    -      distance to best facet
    -      NULL if none
    -
    -    moves good, visible facets (and some other visible facets)
    -      to end of qh facet_list
    -
    -  notes:
    -    uses qh visit_id
    -
    -  design:
    -    initialize bestfacet if facetA is good
    -    move facetA to end of facetlist
    -    for each facet on facetlist
    -      for each unvisited neighbor of facet
    -        move visible neighbors to end of facetlist
    -        update best good neighbor
    -        if no good neighbors, update best facet
    -*/
    -facetT *qh_findgooddist(pointT *point, facetT *facetA, realT *distp,
    -               facetT **facetlist) {
    -  realT bestdist= -REALmax, dist;
    -  facetT *neighbor, **neighborp, *bestfacet=NULL, *facet;
    -  boolT goodseen= False;
    -
    -  if (facetA->good) {
    -    zzinc_(Zcheckpart);  /* calls from check_bestdist occur after print stats */
    -    qh_distplane(point, facetA, &bestdist);
    -    bestfacet= facetA;
    -    goodseen= True;
    -  }
    -  qh_removefacet(facetA);
    -  qh_appendfacet(facetA);
    -  *facetlist= facetA;
    -  facetA->visitid= ++qh visit_id;
    -  FORALLfacet_(*facetlist) {
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid == qh visit_id)
    -        continue;
    -      neighbor->visitid= qh visit_id;
    -      if (goodseen && !neighbor->good)
    -        continue;
    -      zzinc_(Zcheckpart);
    -      qh_distplane(point, neighbor, &dist);
    -      if (dist > 0) {
    -        qh_removefacet(neighbor);
    -        qh_appendfacet(neighbor);
    -        if (neighbor->good) {
    -          goodseen= True;
    -          if (dist > bestdist) {
    -            bestdist= dist;
    -            bestfacet= neighbor;
    -          }
    -        }
    -      }
    -    }
    -  }
    -  if (bestfacet) {
    -    *distp= bestdist;
    -    trace2((qh ferr, 2003, "qh_findgooddist: p%d is %2.2g above good facet f%d\n",
    -      qh_pointid(point), bestdist, bestfacet->id));
    -    return bestfacet;
    -  }
    -  trace4((qh ferr, 4011, "qh_findgooddist: no good facet for p%d above f%d\n",
    -      qh_pointid(point), facetA->id));
    -  return NULL;
    -}  /* findgooddist */
    -
    -/*---------------------------------
    -
    -  qh_getarea( facetlist )
    -    set area of all facets in facetlist
    -    collect statistics
    -    nop if hasAreaVolume
    -
    -  returns:
    -    sets qh totarea/totvol to total area and volume of convex hull
    -    for Delaunay triangulation, computes projected area of the lower or upper hull
    -      ignores upper hull if qh ATinfinity
    -
    -  notes:
    -    could compute outer volume by expanding facet area by rays from interior
    -    the following attempt at perpendicular projection underestimated badly:
    -      qh.totoutvol += (-dist + facet->maxoutside + qh DISTround)
    -                            * area/ qh hull_dim;
    -  design:
    -    for each facet on facetlist
    -      compute facet->area
    -      update qh.totarea and qh.totvol
    -*/
    -void qh_getarea(facetT *facetlist) {
    -  realT area;
    -  realT dist;
    -  facetT *facet;
    -
    -  if (qh hasAreaVolume)
    -    return;
    -  if (qh REPORTfreq)
    -    qh_fprintf(qh ferr, 8020, "computing area of each facet and volume of the convex hull\n");
    -  else
    -    trace1((qh ferr, 1001, "qh_getarea: computing volume and area for each facet\n"));
    -  qh totarea= qh totvol= 0.0;
    -  FORALLfacet_(facetlist) {
    -    if (!facet->normal)
    -      continue;
    -    if (facet->upperdelaunay && qh ATinfinity)
    -      continue;
    -    if (!facet->isarea) {
    -      facet->f.area= qh_facetarea(facet);
    -      facet->isarea= True;
    -    }
    -    area= facet->f.area;
    -    if (qh DELAUNAY) {
    -      if (facet->upperdelaunay == qh UPPERdelaunay)
    -        qh totarea += area;
    -    }else {
    -      qh totarea += area;
    -      qh_distplane(qh interior_point, facet, &dist);
    -      qh totvol += -dist * area/ qh hull_dim;
    -    }
    -    if (qh PRINTstatistics) {
    -      wadd_(Wareatot, area);
    -      wmax_(Wareamax, area);
    -      wmin_(Wareamin, area);
    -    }
    -  }
    -  qh hasAreaVolume= True;
    -} /* getarea */
    -
    -/*---------------------------------
    -
    -  qh_gram_schmidt( dim, row )
    -    implements Gram-Schmidt orthogonalization by rows
    -
    -  returns:
    -    false if zero norm
    -    overwrites rows[dim][dim]
    -
    -  notes:
    -    see Golub & van Loan Algorithm 6.2-2
    -    overflow due to small divisors not handled
    -
    -  design:
    -    for each row
    -      compute norm for row
    -      if non-zero, normalize row
    -      for each remaining rowA
    -        compute inner product of row and rowA
    -        reduce rowA by row * inner product
    -*/
    -boolT qh_gram_schmidt(int dim, realT **row) {
    -  realT *rowi, *rowj, norm;
    -  int i, j, k;
    -
    -  for (i=0; i < dim; i++) {
    -    rowi= row[i];
    -    for (norm= 0.0, k= dim; k--; rowi++)
    -      norm += *rowi * *rowi;
    -    norm= sqrt(norm);
    -    wmin_(Wmindenom, norm);
    -    if (norm == 0.0)  /* either 0 or overflow due to sqrt */
    -      return False;
    -    for (k=dim; k--; )
    -      *(--rowi) /= norm;
    -    for (j=i+1; j < dim; j++) {
    -      rowj= row[j];
    -      for (norm= 0.0, k=dim; k--; )
    -        norm += *rowi++ * *rowj++;
    -      for (k=dim; k--; )
    -        *(--rowj) -= *(--rowi) * norm;
    -    }
    -  }
    -  return True;
    -} /* gram_schmidt */
    -
    -
    -/*---------------------------------
    -
    -  qh_inthresholds( normal, angle )
    -    return True if normal within qh.lower_/upper_threshold
    -
    -  returns:
    -    estimate of angle by summing of threshold diffs
    -      angle may be NULL
    -      smaller "angle" is better
    -
    -  notes:
    -    invalid if qh.SPLITthresholds
    -
    -  see:
    -    qh.lower_threshold in qh_initbuild()
    -    qh_initthresholds()
    -
    -  design:
    -    for each dimension
    -      test threshold
    -*/
    -boolT qh_inthresholds(coordT *normal, realT *angle) {
    -  boolT within= True;
    -  int k;
    -  realT threshold;
    -
    -  if (angle)
    -    *angle= 0.0;
    -  for (k=0; k < qh hull_dim; k++) {
    -    threshold= qh lower_threshold[k];
    -    if (threshold > -REALmax/2) {
    -      if (normal[k] < threshold)
    -        within= False;
    -      if (angle) {
    -        threshold -= normal[k];
    -        *angle += fabs_(threshold);
    -      }
    -    }
    -    if (qh upper_threshold[k] < REALmax/2) {
    -      threshold= qh upper_threshold[k];
    -      if (normal[k] > threshold)
    -        within= False;
    -      if (angle) {
    -        threshold -= normal[k];
    -        *angle += fabs_(threshold);
    -      }
    -    }
    -  }
    -  return within;
    -} /* inthresholds */
    -
    -
    -/*---------------------------------
    -
    -  qh_joggleinput()
    -    randomly joggle input to Qhull by qh.JOGGLEmax
    -    initial input is qh.first_point/qh.num_points of qh.hull_dim
    -      repeated calls use qh.input_points/qh.num_points
    -
    -  returns:
    -    joggles points at qh.first_point/qh.num_points
    -    copies data to qh.input_points/qh.input_malloc if first time
    -    determines qh.JOGGLEmax if it was zero
    -    if qh.DELAUNAY
    -      computes the Delaunay projection of the joggled points
    -
    -  notes:
    -    if qh.DELAUNAY, unnecessarily joggles the last coordinate
    -    the initial 'QJn' may be set larger than qh_JOGGLEmaxincrease
    -
    -  design:
    -    if qh.DELAUNAY
    -      set qh.SCALElast for reduced precision errors
    -    if first call
    -      initialize qh.input_points to the original input points
    -      if qh.JOGGLEmax == 0
    -        determine default qh.JOGGLEmax
    -    else
    -      increase qh.JOGGLEmax according to qh.build_cnt
    -    joggle the input by adding a random number in [-qh.JOGGLEmax,qh.JOGGLEmax]
    -    if qh.DELAUNAY
    -      sets the Delaunay projection
    -*/
    -void qh_joggleinput(void) {
    -  int i, seed, size;
    -  coordT *coordp, *inputp;
    -  realT randr, randa, randb;
    -
    -  if (!qh input_points) { /* first call */
    -    qh input_points= qh first_point;
    -    qh input_malloc= qh POINTSmalloc;
    -    size= qh num_points * qh hull_dim * sizeof(coordT);
    -    if (!(qh first_point=(coordT*)qh_malloc((size_t)size))) {
    -      qh_fprintf(qh ferr, 6009, "qhull error: insufficient memory to joggle %d points\n",
    -          qh num_points);
    -      qh_errexit(qh_ERRmem, NULL, NULL);
    -    }
    -    qh POINTSmalloc= True;
    -    if (qh JOGGLEmax == 0.0) {
    -      qh JOGGLEmax= qh_detjoggle(qh input_points, qh num_points, qh hull_dim);
    -      qh_option("QJoggle", NULL, &qh JOGGLEmax);
    -    }
    -  }else {                 /* repeated call */
    -    if (!qh RERUN && qh build_cnt > qh_JOGGLEretry) {
    -      if (((qh build_cnt-qh_JOGGLEretry-1) % qh_JOGGLEagain) == 0) {
    -        realT maxjoggle= qh MAXwidth * qh_JOGGLEmaxincrease;
    -        if (qh JOGGLEmax < maxjoggle) {
    -          qh JOGGLEmax *= qh_JOGGLEincrease;
    -          minimize_(qh JOGGLEmax, maxjoggle);
    -        }
    -      }
    -    }
    -    qh_option("QJoggle", NULL, &qh JOGGLEmax);
    -  }
    -  if (qh build_cnt > 1 && qh JOGGLEmax > fmax_(qh MAXwidth/4, 0.1)) {
    -      qh_fprintf(qh ferr, 6010, "qhull error: the current joggle for 'QJn', %.2g, is too large for the width\nof the input.  If possible, recompile Qhull with higher-precision reals.\n",
    -                qh JOGGLEmax);
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  /* for some reason, using qh ROTATErandom and qh_RANDOMseed does not repeat the run. Use 'TRn' instead */
    -  seed= qh_RANDOMint;
    -  qh_option("_joggle-seed", &seed, NULL);
    -  trace0((qh ferr, 6, "qh_joggleinput: joggle input by %2.2g with seed %d\n",
    -    qh JOGGLEmax, seed));
    -  inputp= qh input_points;
    -  coordp= qh first_point;
    -  randa= 2.0 * qh JOGGLEmax/qh_RANDOMmax;
    -  randb= -qh JOGGLEmax;
    -  size= qh num_points * qh hull_dim;
    -  for (i=size; i--; ) {
    -    randr= qh_RANDOMint;
    -    *(coordp++)= *(inputp++) + (randr * randa + randb);
    -  }
    -  if (qh DELAUNAY) {
    -    qh last_low= qh last_high= qh last_newhigh= REALmax;
    -    qh_setdelaunay(qh hull_dim, qh num_points, qh first_point);
    -  }
    -} /* joggleinput */
    -
    -/*---------------------------------
    -
    -  qh_maxabsval( normal, dim )
    -    return pointer to maximum absolute value of a dim vector
    -    returns NULL if dim=0
    -*/
    -realT *qh_maxabsval(realT *normal, int dim) {
    -  realT maxval= -REALmax;
    -  realT *maxp= NULL, *colp, absval;
    -  int k;
    -
    -  for (k=dim, colp= normal; k--; colp++) {
    -    absval= fabs_(*colp);
    -    if (absval > maxval) {
    -      maxval= absval;
    -      maxp= colp;
    -    }
    -  }
    -  return maxp;
    -} /* maxabsval */
    -
    -
    -/*---------------------------------
    -
    -  qh_maxmin( points, numpoints, dimension )
    -    return max/min points for each dimension
    -    determine max and min coordinates
    -
    -  returns:
    -    returns a temporary set of max and min points
    -      may include duplicate points. Does not include qh.GOODpoint
    -    sets qh.NEARzero, qh.MAXabs_coord, qh.MAXsumcoord, qh.MAXwidth
    -         qh.MAXlastcoord, qh.MINlastcoord
    -    initializes qh.max_outside, qh.min_vertex, qh.WAScoplanar, qh.ZEROall_ok
    -
    -  notes:
    -    loop duplicated in qh_detjoggle()
    -
    -  design:
    -    initialize global precision variables
    -    checks definition of REAL...
    -    for each dimension
    -      for each point
    -        collect maximum and minimum point
    -      collect maximum of maximums and minimum of minimums
    -      determine qh.NEARzero for Gaussian Elimination
    -*/
    -setT *qh_maxmin(pointT *points, int numpoints, int dimension) {
    -  int k;
    -  realT maxcoord, temp;
    -  pointT *minimum, *maximum, *point, *pointtemp;
    -  setT *set;
    -
    -  qh max_outside= 0.0;
    -  qh MAXabs_coord= 0.0;
    -  qh MAXwidth= -REALmax;
    -  qh MAXsumcoord= 0.0;
    -  qh min_vertex= 0.0;
    -  qh WAScoplanar= False;
    -  if (qh ZEROcentrum)
    -    qh ZEROall_ok= True;
    -  if (REALmin < REALepsilon && REALmin < REALmax && REALmin > -REALmax
    -  && REALmax > 0.0 && -REALmax < 0.0)
    -    ; /* all ok */
    -  else {
    -    qh_fprintf(qh ferr, 6011, "qhull error: floating point constants in user.h are wrong\n\
    -REALepsilon %g REALmin %g REALmax %g -REALmax %g\n",
    -             REALepsilon, REALmin, REALmax, -REALmax);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  set= qh_settemp(2*dimension);
    -  for (k=0; k < dimension; k++) {
    -    if (points == qh GOODpointp)
    -      minimum= maximum= points + dimension;
    -    else
    -      minimum= maximum= points;
    -    FORALLpoint_(points, numpoints) {
    -      if (point == qh GOODpointp)
    -        continue;
    -      if (maximum[k] < point[k])
    -        maximum= point;
    -      else if (minimum[k] > point[k])
    -        minimum= point;
    -    }
    -    if (k == dimension-1) {
    -      qh MINlastcoord= minimum[k];
    -      qh MAXlastcoord= maximum[k];
    -    }
    -    if (qh SCALElast && k == dimension-1)
    -      maxcoord= qh MAXwidth;
    -    else {
    -      maxcoord= fmax_(maximum[k], -minimum[k]);
    -      if (qh GOODpointp) {
    -        temp= fmax_(qh GOODpointp[k], -qh GOODpointp[k]);
    -        maximize_(maxcoord, temp);
    -      }
    -      temp= maximum[k] - minimum[k];
    -      maximize_(qh MAXwidth, temp);
    -    }
    -    maximize_(qh MAXabs_coord, maxcoord);
    -    qh MAXsumcoord += maxcoord;
    -    qh_setappend(&set, maximum);
    -    qh_setappend(&set, minimum);
    -    /* calculation of qh NEARzero is based on error formula 4.4-13 of
    -       Golub & van Loan, authors say n^3 can be ignored and 10 be used in
    -       place of rho */
    -    qh NEARzero[k]= 80 * qh MAXsumcoord * REALepsilon;
    -  }
    -  if (qh IStracing >=1)
    -    qh_printpoints(qh ferr, "qh_maxmin: found the max and min points(by dim):", set);
    -  return(set);
    -} /* maxmin */
    -
    -/*---------------------------------
    -
    -  qh_maxouter()
    -    return maximum distance from facet to outer plane
    -    normally this is qh.max_outside+qh.DISTround
    -    does not include qh.JOGGLEmax
    -
    -  see:
    -    qh_outerinner()
    -
    -  notes:
    -    need to add another qh.DISTround if testing actual point with computation
    -
    -  for joggle:
    -    qh_setfacetplane() updated qh.max_outer for Wnewvertexmax (max distance to vertex)
    -    need to use Wnewvertexmax since could have a coplanar point for a high
    -      facet that is replaced by a low facet
    -    need to add qh.JOGGLEmax if testing input points
    -*/
    -realT qh_maxouter(void) {
    -  realT dist;
    -
    -  dist= fmax_(qh max_outside, qh DISTround);
    -  dist += qh DISTround;
    -  trace4((qh ferr, 4012, "qh_maxouter: max distance from facet to outer plane is %2.2g max_outside is %2.2g\n", dist, qh max_outside));
    -  return dist;
    -} /* maxouter */
    -
    -/*---------------------------------
    -
    -  qh_maxsimplex( dim, maxpoints, points, numpoints, simplex )
    -    determines maximum simplex for a set of points
    -    starts from points already in simplex
    -    skips qh.GOODpointp (assumes that it isn't in maxpoints)
    -
    -  returns:
    -    simplex with dim+1 points
    -
    -  notes:
    -    assumes at least pointsneeded points in points
    -    maximizes determinate for x,y,z,w, etc.
    -    uses maxpoints as long as determinate is clearly non-zero
    -
    -  design:
    -    initialize simplex with at least two points
    -      (find points with max or min x coordinate)
    -    for each remaining dimension
    -      add point that maximizes the determinate
    -        (use points from maxpoints first)
    -*/
    -void qh_maxsimplex(int dim, setT *maxpoints, pointT *points, int numpoints, setT **simplex) {
    -  pointT *point, **pointp, *pointtemp, *maxpoint, *minx=NULL, *maxx=NULL;
    -  boolT nearzero, maxnearzero= False;
    -  int k, sizinit;
    -  realT maxdet= -REALmax, det, mincoord= REALmax, maxcoord= -REALmax;
    -
    -  sizinit= qh_setsize(*simplex);
    -  if (sizinit < 2) {
    -    if (qh_setsize(maxpoints) >= 2) {
    -      FOREACHpoint_(maxpoints) {
    -        if (maxcoord < point[0]) {
    -          maxcoord= point[0];
    -          maxx= point;
    -        }
    -        if (mincoord > point[0]) {
    -          mincoord= point[0];
    -          minx= point;
    -        }
    -      }
    -    }else {
    -      FORALLpoint_(points, numpoints) {
    -        if (point == qh GOODpointp)
    -          continue;
    -        if (maxcoord < point[0]) {
    -          maxcoord= point[0];
    -          maxx= point;
    -        }
    -        if (mincoord > point[0]) {
    -          mincoord= point[0];
    -          minx= point;
    -        }
    -      }
    -    }
    -    qh_setunique(simplex, minx);
    -    if (qh_setsize(*simplex) < 2)
    -      qh_setunique(simplex, maxx);
    -    sizinit= qh_setsize(*simplex);
    -    if (sizinit < 2) {
    -      qh_precision("input has same x coordinate");
    -      if (zzval_(Zsetplane) > qh hull_dim+1) {
    -        qh_fprintf(qh ferr, 6012, "qhull precision error (qh_maxsimplex for voronoi_center):\n%d points with the same x coordinate.\n",
    -                 qh_setsize(maxpoints)+numpoints);
    -        qh_errexit(qh_ERRprec, NULL, NULL);
    -      }else {
    -        qh_fprintf(qh ferr, 6013, "qhull input error: input is less than %d-dimensional since it has the same x coordinate\n", qh hull_dim);
    -        qh_errexit(qh_ERRinput, NULL, NULL);
    -      }
    -    }
    -  }
    -  for (k=sizinit; k < dim+1; k++) {
    -    maxpoint= NULL;
    -    maxdet= -REALmax;
    -    FOREACHpoint_(maxpoints) {
    -      if (!qh_setin(*simplex, point)) {
    -        det= qh_detsimplex(point, *simplex, k, &nearzero);
    -        if ((det= fabs_(det)) > maxdet) {
    -          maxdet= det;
    -          maxpoint= point;
    -          maxnearzero= nearzero;
    -        }
    -      }
    -    }
    -    if (!maxpoint || maxnearzero) {
    -      zinc_(Zsearchpoints);
    -      if (!maxpoint) {
    -        trace0((qh ferr, 7, "qh_maxsimplex: searching all points for %d-th initial vertex.\n", k+1));
    -      }else {
    -        trace0((qh ferr, 8, "qh_maxsimplex: searching all points for %d-th initial vertex, better than p%d det %2.2g\n",
    -                k+1, qh_pointid(maxpoint), maxdet));
    -      }
    -      FORALLpoint_(points, numpoints) {
    -        if (point == qh GOODpointp)
    -          continue;
    -        if (!qh_setin(*simplex, point)) {
    -          det= qh_detsimplex(point, *simplex, k, &nearzero);
    -          if ((det= fabs_(det)) > maxdet) {
    -            maxdet= det;
    -            maxpoint= point;
    -            maxnearzero= nearzero;
    -          }
    -        }
    -      }
    -    } /* !maxpoint */
    -    if (!maxpoint) {
    -      qh_fprintf(qh ferr, 6014, "qhull internal error (qh_maxsimplex): not enough points available\n");
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -    qh_setappend(simplex, maxpoint);
    -    trace1((qh ferr, 1002, "qh_maxsimplex: selected point p%d for %d`th initial vertex, det=%2.2g\n",
    -            qh_pointid(maxpoint), k+1, maxdet));
    -  } /* k */
    -} /* maxsimplex */
    -
    -/*---------------------------------
    -
    -  qh_minabsval( normal, dim )
    -    return minimum absolute value of a dim vector
    -*/
    -realT qh_minabsval(realT *normal, int dim) {
    -  realT minval= 0;
    -  realT maxval= 0;
    -  realT *colp;
    -  int k;
    -
    -  for (k=dim, colp=normal; k--; colp++) {
    -    maximize_(maxval, *colp);
    -    minimize_(minval, *colp);
    -  }
    -  return fmax_(maxval, -minval);
    -} /* minabsval */
    -
    -
    -/*---------------------------------
    -
    -  qh_mindif ( vecA, vecB, dim )
    -    return index of min abs. difference of two vectors
    -*/
    -int qh_mindiff(realT *vecA, realT *vecB, int dim) {
    -  realT mindiff= REALmax, diff;
    -  realT *vecAp= vecA, *vecBp= vecB;
    -  int k, mink= 0;
    -
    -  for (k=0; k < dim; k++) {
    -    diff= *vecAp++ - *vecBp++;
    -    diff= fabs_(diff);
    -    if (diff < mindiff) {
    -      mindiff= diff;
    -      mink= k;
    -    }
    -  }
    -  return mink;
    -} /* mindiff */
    -
    -
    -
    -/*---------------------------------
    -
    -  qh_orientoutside( facet  )
    -    make facet outside oriented via qh.interior_point
    -
    -  returns:
    -    True if facet reversed orientation.
    -*/
    -boolT qh_orientoutside(facetT *facet) {
    -  int k;
    -  realT dist;
    -
    -  qh_distplane(qh interior_point, facet, &dist);
    -  if (dist > 0) {
    -    for (k=qh hull_dim; k--; )
    -      facet->normal[k]= -facet->normal[k];
    -    facet->offset= -facet->offset;
    -    return True;
    -  }
    -  return False;
    -} /* orientoutside */
    -
    -/*---------------------------------
    -
    -  qh_outerinner( facet, outerplane, innerplane  )
    -    if facet and qh.maxoutdone (i.e., qh_check_maxout)
    -      returns outer and inner plane for facet
    -    else
    -      returns maximum outer and inner plane
    -    accounts for qh.JOGGLEmax
    -
    -  see:
    -    qh_maxouter(), qh_check_bestdist(), qh_check_points()
    -
    -  notes:
    -    outerplaner or innerplane may be NULL
    -    facet is const
    -    Does not error (QhullFacet)
    -
    -    includes qh.DISTround for actual points
    -    adds another qh.DISTround if testing with floating point arithmetic
    -*/
    -void qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane) {
    -  realT dist, mindist;
    -  vertexT *vertex, **vertexp;
    -
    -  if (outerplane) {
    -    if (!qh_MAXoutside || !facet || !qh maxoutdone) {
    -      *outerplane= qh_maxouter();       /* includes qh.DISTround */
    -    }else { /* qh_MAXoutside ... */
    -#if qh_MAXoutside
    -      *outerplane= facet->maxoutside + qh DISTround;
    -#endif
    -
    -    }
    -    if (qh JOGGLEmax < REALmax/2)
    -      *outerplane += qh JOGGLEmax * sqrt((realT)qh hull_dim);
    -  }
    -  if (innerplane) {
    -    if (facet) {
    -      mindist= REALmax;
    -      FOREACHvertex_(facet->vertices) {
    -        zinc_(Zdistio);
    -        qh_distplane(vertex->point, facet, &dist);
    -        minimize_(mindist, dist);
    -      }
    -      *innerplane= mindist - qh DISTround;
    -    }else
    -      *innerplane= qh min_vertex - qh DISTround;
    -    if (qh JOGGLEmax < REALmax/2)
    -      *innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
    -  }
    -} /* outerinner */
    -
    -/*---------------------------------
    -
    -  qh_pointdist( point1, point2, dim )
    -    return distance between two points
    -
    -  notes:
    -    returns distance squared if 'dim' is negative
    -*/
    -coordT qh_pointdist(pointT *point1, pointT *point2, int dim) {
    -  coordT dist, diff;
    -  int k;
    -
    -  dist= 0.0;
    -  for (k= (dim > 0 ? dim : -dim); k--; ) {
    -    diff= *point1++ - *point2++;
    -    dist += diff * diff;
    -  }
    -  if (dim > 0)
    -    return(sqrt(dist));
    -  return dist;
    -} /* pointdist */
    -
    -
    -/*---------------------------------
    -
    -  qh_printmatrix( fp, string, rows, numrow, numcol )
    -    print matrix to fp given by row vectors
    -    print string as header
    -
    -  notes:
    -    print a vector by qh_printmatrix(fp, "", &vect, 1, len)
    -*/
    -void qh_printmatrix(FILE *fp, const char *string, realT **rows, int numrow, int numcol) {
    -  realT *rowp;
    -  realT r; /*bug fix*/
    -  int i,k;
    -
    -  qh_fprintf(fp, 9001, "%s\n", string);
    -  for (i=0; i < numrow; i++) {
    -    rowp= rows[i];
    -    for (k=0; k < numcol; k++) {
    -      r= *rowp++;
    -      qh_fprintf(fp, 9002, "%6.3g ", r);
    -    }
    -    qh_fprintf(fp, 9003, "\n");
    -  }
    -} /* printmatrix */
    -
    -
    -/*---------------------------------
    -
    -  qh_printpoints( fp, string, points )
    -    print pointids to fp for a set of points
    -    if string, prints string and 'p' point ids
    -*/
    -void qh_printpoints(FILE *fp, const char *string, setT *points) {
    -  pointT *point, **pointp;
    -
    -  if (string) {
    -    qh_fprintf(fp, 9004, "%s", string);
    -    FOREACHpoint_(points)
    -      qh_fprintf(fp, 9005, " p%d", qh_pointid(point));
    -    qh_fprintf(fp, 9006, "\n");
    -  }else {
    -    FOREACHpoint_(points)
    -      qh_fprintf(fp, 9007, " %d", qh_pointid(point));
    -    qh_fprintf(fp, 9008, "\n");
    -  }
    -} /* printpoints */
    -
    -
    -/*---------------------------------
    -
    -  qh_projectinput()
    -    project input points using qh.lower_bound/upper_bound and qh DELAUNAY
    -    if qh.lower_bound[k]=qh.upper_bound[k]= 0,
    -      removes dimension k
    -    if halfspace intersection
    -      removes dimension k from qh.feasible_point
    -    input points in qh first_point, num_points, input_dim
    -
    -  returns:
    -    new point array in qh first_point of qh hull_dim coordinates
    -    sets qh POINTSmalloc
    -    if qh DELAUNAY
    -      projects points to paraboloid
    -      lowbound/highbound is also projected
    -    if qh ATinfinity
    -      adds point "at-infinity"
    -    if qh POINTSmalloc
    -      frees old point array
    -
    -  notes:
    -    checks that qh.hull_dim agrees with qh.input_dim, PROJECTinput, and DELAUNAY
    -
    -
    -  design:
    -    sets project[k] to -1 (delete), 0 (keep), 1 (add for Delaunay)
    -    determines newdim and newnum for qh hull_dim and qh num_points
    -    projects points to newpoints
    -    projects qh.lower_bound to itself
    -    projects qh.upper_bound to itself
    -    if qh DELAUNAY
    -      if qh ATINFINITY
    -        projects points to paraboloid
    -        computes "infinity" point as vertex average and 10% above all points
    -      else
    -        uses qh_setdelaunay to project points to paraboloid
    -*/
    -void qh_projectinput(void) {
    -  int k,i;
    -  int newdim= qh input_dim, newnum= qh num_points;
    -  signed char *project;
    -  int size= (qh input_dim+1)*sizeof(*project);
    -  pointT *newpoints, *coord, *infinity;
    -  realT paraboloid, maxboloid= 0;
    -
    -  project= (signed char*)qh_memalloc(size);
    -  memset((char*)project, 0, (size_t)size);
    -  for (k=0; k < qh input_dim; k++) {   /* skip Delaunay bound */
    -    if (qh lower_bound[k] == 0 && qh upper_bound[k] == 0) {
    -      project[k]= -1;
    -      newdim--;
    -    }
    -  }
    -  if (qh DELAUNAY) {
    -    project[k]= 1;
    -    newdim++;
    -    if (qh ATinfinity)
    -      newnum++;
    -  }
    -  if (newdim != qh hull_dim) {
    -    qh_fprintf(qh ferr, 6015, "qhull internal error (qh_projectinput): dimension after projection %d != hull_dim %d\n", newdim, qh hull_dim);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (!(newpoints=(coordT*)qh_malloc(newnum*newdim*sizeof(coordT)))){
    -    qh_fprintf(qh ferr, 6016, "qhull error: insufficient memory to project %d points\n",
    -           qh num_points);
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  qh_projectpoints(project, qh input_dim+1, qh first_point,
    -                    qh num_points, qh input_dim, newpoints, newdim);
    -  trace1((qh ferr, 1003, "qh_projectinput: updating lower and upper_bound\n"));
    -  qh_projectpoints(project, qh input_dim+1, qh lower_bound,
    -                    1, qh input_dim+1, qh lower_bound, newdim+1);
    -  qh_projectpoints(project, qh input_dim+1, qh upper_bound,
    -                    1, qh input_dim+1, qh upper_bound, newdim+1);
    -  if (qh HALFspace) {
    -    if (!qh feasible_point) {
    -      qh_fprintf(qh ferr, 6017, "qhull internal error (qh_projectinput): HALFspace defined without qh.feasible_point\n");
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -    qh_projectpoints(project, qh input_dim, qh feasible_point,
    -                      1, qh input_dim, qh feasible_point, newdim);
    -  }
    -  qh_memfree(project, (qh input_dim+1)*sizeof(*project));
    -  if (qh POINTSmalloc)
    -    qh_free(qh first_point);
    -  qh first_point= newpoints;
    -  qh POINTSmalloc= True;
    -  if (qh DELAUNAY && qh ATinfinity) {
    -    coord= qh first_point;
    -    infinity= qh first_point + qh hull_dim * qh num_points;
    -    for (k=qh hull_dim-1; k--; )
    -      infinity[k]= 0.0;
    -    for (i=qh num_points; i--; ) {
    -      paraboloid= 0.0;
    -      for (k=0; k < qh hull_dim-1; k++) {
    -        paraboloid += *coord * *coord;
    -        infinity[k] += *coord;
    -        coord++;
    -      }
    -      *(coord++)= paraboloid;
    -      maximize_(maxboloid, paraboloid);
    -    }
    -    /* coord == infinity */
    -    for (k=qh hull_dim-1; k--; )
    -      *(coord++) /= qh num_points;
    -    *(coord++)= maxboloid * 1.1;
    -    qh num_points++;
    -    trace0((qh ferr, 9, "qh_projectinput: projected points to paraboloid for Delaunay\n"));
    -  }else if (qh DELAUNAY)  /* !qh ATinfinity */
    -    qh_setdelaunay( qh hull_dim, qh num_points, qh first_point);
    -} /* projectinput */
    -
    -
    -/*---------------------------------
    -
    -  qh_projectpoints( project, n, points, numpoints, dim, newpoints, newdim )
    -    project points/numpoints/dim to newpoints/newdim
    -    if project[k] == -1
    -      delete dimension k
    -    if project[k] == 1
    -      add dimension k by duplicating previous column
    -    n is size of project
    -
    -  notes:
    -    newpoints may be points if only adding dimension at end
    -
    -  design:
    -    check that 'project' and 'newdim' agree
    -    for each dimension
    -      if project == -1
    -        skip dimension
    -      else
    -        determine start of column in newpoints
    -        determine start of column in points
    -          if project == +1, duplicate previous column
    -        copy dimension (column) from points to newpoints
    -*/
    -void qh_projectpoints(signed char *project, int n, realT *points,
    -        int numpoints, int dim, realT *newpoints, int newdim) {
    -  int testdim= dim, oldk=0, newk=0, i,j=0,k;
    -  realT *newp, *oldp;
    -
    -  for (k=0; k < n; k++)
    -    testdim += project[k];
    -  if (testdim != newdim) {
    -    qh_fprintf(qh ferr, 6018, "qhull internal error (qh_projectpoints): newdim %d should be %d after projection\n",
    -      newdim, testdim);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  for (j=0; j= dim)
    -          continue;
    -        oldp= points+oldk;
    -      }else
    -        oldp= points+oldk++;
    -      for (i=numpoints; i--; ) {
    -        *newp= *oldp;
    -        newp += newdim;
    -        oldp += dim;
    -      }
    -    }
    -    if (oldk >= dim)
    -      break;
    -  }
    -  trace1((qh ferr, 1004, "qh_projectpoints: projected %d points from dim %d to dim %d\n",
    -    numpoints, dim, newdim));
    -} /* projectpoints */
    -
    -
    -/*---------------------------------
    -
    -  qh_rotateinput( rows )
    -    rotate input using row matrix
    -    input points given by qh first_point, num_points, hull_dim
    -    assumes rows[dim] is a scratch buffer
    -    if qh POINTSmalloc, overwrites input points, else mallocs a new array
    -
    -  returns:
    -    rotated input
    -    sets qh POINTSmalloc
    -
    -  design:
    -    see qh_rotatepoints
    -*/
    -void qh_rotateinput(realT **rows) {
    -
    -  if (!qh POINTSmalloc) {
    -    qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
    -    qh POINTSmalloc= True;
    -  }
    -  qh_rotatepoints(qh first_point, qh num_points, qh hull_dim, rows);
    -}  /* rotateinput */
    -
    -/*---------------------------------
    -
    -  qh_rotatepoints( points, numpoints, dim, row )
    -    rotate numpoints points by a d-dim row matrix
    -    assumes rows[dim] is a scratch buffer
    -
    -  returns:
    -    rotated points in place
    -
    -  design:
    -    for each point
    -      for each coordinate
    -        use row[dim] to compute partial inner product
    -      for each coordinate
    -        rotate by partial inner product
    -*/
    -void qh_rotatepoints(realT *points, int numpoints, int dim, realT **row) {
    -  realT *point, *rowi, *coord= NULL, sum, *newval;
    -  int i,j,k;
    -
    -  if (qh IStracing >= 1)
    -    qh_printmatrix(qh ferr, "qh_rotatepoints: rotate points by", row, dim, dim);
    -  for (point= points, j= numpoints; j--; point += dim) {
    -    newval= row[dim];
    -    for (i=0; i < dim; i++) {
    -      rowi= row[i];
    -      coord= point;
    -      for (sum= 0.0, k= dim; k--; )
    -        sum += *rowi++ * *coord++;
    -      *(newval++)= sum;
    -    }
    -    for (k=dim; k--; )
    -      *(--coord)= *(--newval);
    -  }
    -} /* rotatepoints */
    -
    -
    -/*---------------------------------
    -
    -  qh_scaleinput()
    -    scale input points using qh low_bound/high_bound
    -    input points given by qh first_point, num_points, hull_dim
    -    if qh POINTSmalloc, overwrites input points, else mallocs a new array
    -
    -  returns:
    -    scales coordinates of points to low_bound[k], high_bound[k]
    -    sets qh POINTSmalloc
    -
    -  design:
    -    see qh_scalepoints
    -*/
    -void qh_scaleinput(void) {
    -
    -  if (!qh POINTSmalloc) {
    -    qh first_point= qh_copypoints(qh first_point, qh num_points, qh hull_dim);
    -    qh POINTSmalloc= True;
    -  }
    -  qh_scalepoints(qh first_point, qh num_points, qh hull_dim,
    -       qh lower_bound, qh upper_bound);
    -}  /* scaleinput */
    -
    -/*---------------------------------
    -
    -  qh_scalelast( points, numpoints, dim, low, high, newhigh )
    -    scale last coordinate to [0,m] for Delaunay triangulations
    -    input points given by points, numpoints, dim
    -
    -  returns:
    -    changes scale of last coordinate from [low, high] to [0, newhigh]
    -    overwrites last coordinate of each point
    -    saves low/high/newhigh in qh.last_low, etc. for qh_setdelaunay()
    -
    -  notes:
    -    when called by qh_setdelaunay, low/high may not match actual data
    -
    -  design:
    -    compute scale and shift factors
    -    apply to last coordinate of each point
    -*/
    -void qh_scalelast(coordT *points, int numpoints, int dim, coordT low,
    -                   coordT high, coordT newhigh) {
    -  realT scale, shift;
    -  coordT *coord;
    -  int i;
    -  boolT nearzero= False;
    -
    -  trace4((qh ferr, 4013, "qh_scalelast: scale last coordinate from [%2.2g, %2.2g] to [0,%2.2g]\n",
    -    low, high, newhigh));
    -  qh last_low= low;
    -  qh last_high= high;
    -  qh last_newhigh= newhigh;
    -  scale= qh_divzero(newhigh, high - low,
    -                  qh MINdenom_1, &nearzero);
    -  if (nearzero) {
    -    if (qh DELAUNAY)
    -      qh_fprintf(qh ferr, 6019, "qhull input error: can not scale last coordinate.  Input is cocircular\n   or cospherical.   Use option 'Qz' to add a point at infinity.\n");
    -    else
    -      qh_fprintf(qh ferr, 6020, "qhull input error: can not scale last coordinate.  New bounds [0, %2.2g] are too wide for\nexisting bounds [%2.2g, %2.2g] (width %2.2g)\n",
    -                newhigh, low, high, high-low);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  shift= - low * newhigh / (high-low);
    -  coord= points + dim - 1;
    -  for (i=numpoints; i--; coord += dim)
    -    *coord= *coord * scale + shift;
    -} /* scalelast */
    -
    -/*---------------------------------
    -
    -  qh_scalepoints( points, numpoints, dim, newlows, newhighs )
    -    scale points to new lowbound and highbound
    -    retains old bound when newlow= -REALmax or newhigh= +REALmax
    -
    -  returns:
    -    scaled points
    -    overwrites old points
    -
    -  design:
    -    for each coordinate
    -      compute current low and high bound
    -      compute scale and shift factors
    -      scale all points
    -      enforce new low and high bound for all points
    -*/
    -void qh_scalepoints(pointT *points, int numpoints, int dim,
    -        realT *newlows, realT *newhighs) {
    -  int i,k;
    -  realT shift, scale, *coord, low, high, newlow, newhigh, mincoord, maxcoord;
    -  boolT nearzero= False;
    -
    -  for (k=0; k < dim; k++) {
    -    newhigh= newhighs[k];
    -    newlow= newlows[k];
    -    if (newhigh > REALmax/2 && newlow < -REALmax/2)
    -      continue;
    -    low= REALmax;
    -    high= -REALmax;
    -    for (i=numpoints, coord=points+k; i--; coord += dim) {
    -      minimize_(low, *coord);
    -      maximize_(high, *coord);
    -    }
    -    if (newhigh > REALmax/2)
    -      newhigh= high;
    -    if (newlow < -REALmax/2)
    -      newlow= low;
    -    if (qh DELAUNAY && k == dim-1 && newhigh < newlow) {
    -      qh_fprintf(qh ferr, 6021, "qhull input error: 'Qb%d' or 'QB%d' inverts paraboloid since high bound %.2g < low bound %.2g\n",
    -               k, k, newhigh, newlow);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    scale= qh_divzero(newhigh - newlow, high - low,
    -                  qh MINdenom_1, &nearzero);
    -    if (nearzero) {
    -      qh_fprintf(qh ferr, 6022, "qhull input error: %d'th dimension's new bounds [%2.2g, %2.2g] too wide for\nexisting bounds [%2.2g, %2.2g]\n",
    -              k, newlow, newhigh, low, high);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    shift= (newlow * high - low * newhigh)/(high-low);
    -    coord= points+k;
    -    for (i=numpoints; i--; coord += dim)
    -      *coord= *coord * scale + shift;
    -    coord= points+k;
    -    if (newlow < newhigh) {
    -      mincoord= newlow;
    -      maxcoord= newhigh;
    -    }else {
    -      mincoord= newhigh;
    -      maxcoord= newlow;
    -    }
    -    for (i=numpoints; i--; coord += dim) {
    -      minimize_(*coord, maxcoord);  /* because of roundoff error */
    -      maximize_(*coord, mincoord);
    -    }
    -    trace0((qh ferr, 10, "qh_scalepoints: scaled %d'th coordinate [%2.2g, %2.2g] to [%.2g, %.2g] for %d points by %2.2g and shifted %2.2g\n",
    -      k, low, high, newlow, newhigh, numpoints, scale, shift));
    -  }
    -} /* scalepoints */
    -
    -
    -/*---------------------------------
    -
    -  qh_setdelaunay( dim, count, points )
    -    project count points to dim-d paraboloid for Delaunay triangulation
    -
    -    dim is one more than the dimension of the input set
    -    assumes dim is at least 3 (i.e., at least a 2-d Delaunay triangulation)
    -
    -    points is a dim*count realT array.  The first dim-1 coordinates
    -    are the coordinates of the first input point.  array[dim] is
    -    the first coordinate of the second input point.  array[2*dim] is
    -    the first coordinate of the third input point.
    -
    -    if qh.last_low defined (i.e., 'Qbb' called qh_scalelast)
    -      calls qh_scalelast to scale the last coordinate the same as the other points
    -
    -  returns:
    -    for each point
    -      sets point[dim-1] to sum of squares of coordinates
    -    scale points to 'Qbb' if needed
    -
    -  notes:
    -    to project one point, use
    -      qh_setdelaunay(qh hull_dim, 1, point)
    -
    -    Do not use options 'Qbk', 'QBk', or 'QbB' since they scale
    -    the coordinates after the original projection.
    -
    -*/
    -void qh_setdelaunay(int dim, int count, pointT *points) {
    -  int i, k;
    -  coordT *coordp, coord;
    -  realT paraboloid;
    -
    -  trace0((qh ferr, 11, "qh_setdelaunay: project %d points to paraboloid for Delaunay triangulation\n", count));
    -  coordp= points;
    -  for (i=0; i < count; i++) {
    -    coord= *coordp++;
    -    paraboloid= coord*coord;
    -    for (k=dim-2; k--; ) {
    -      coord= *coordp++;
    -      paraboloid += coord*coord;
    -    }
    -    *coordp++ = paraboloid;
    -  }
    -  if (qh last_low < REALmax/2)
    -    qh_scalelast(points, count, dim, qh last_low, qh last_high, qh last_newhigh);
    -} /* setdelaunay */
    -
    -
    -/*---------------------------------
    -
    -  qh_sethalfspace( dim, coords, nextp, normal, offset, feasible )
    -    set point to dual of halfspace relative to feasible point
    -    halfspace is normal coefficients and offset.
    -
    -  returns:
    -    false if feasible point is outside of hull (error message already reported)
    -    overwrites coordinates for point at dim coords
    -    nextp= next point (coords)
    -
    -  design:
    -    compute distance from feasible point to halfspace
    -    divide each normal coefficient by -dist
    -*/
    -boolT qh_sethalfspace(int dim, coordT *coords, coordT **nextp,
    -         coordT *normal, coordT *offset, coordT *feasible) {
    -  coordT *normp= normal, *feasiblep= feasible, *coordp= coords;
    -  realT dist;
    -  realT r; /*bug fix*/
    -  int k;
    -  boolT zerodiv;
    -
    -  dist= *offset;
    -  for (k=dim; k--; )
    -    dist += *(normp++) * *(feasiblep++);
    -  if (dist > 0)
    -    goto LABELerroroutside;
    -  normp= normal;
    -  if (dist < -qh MINdenom) {
    -    for (k=dim; k--; )
    -      *(coordp++)= *(normp++) / -dist;
    -  }else {
    -    for (k=dim; k--; ) {
    -      *(coordp++)= qh_divzero(*(normp++), -dist, qh MINdenom_1, &zerodiv);
    -      if (zerodiv)
    -        goto LABELerroroutside;
    -    }
    -  }
    -  *nextp= coordp;
    -  if (qh IStracing >= 4) {
    -    qh_fprintf(qh ferr, 8021, "qh_sethalfspace: halfspace at offset %6.2g to point: ", *offset);
    -    for (k=dim, coordp=coords; k--; ) {
    -      r= *coordp++;
    -      qh_fprintf(qh ferr, 8022, " %6.2g", r);
    -    }
    -    qh_fprintf(qh ferr, 8023, "\n");
    -  }
    -  return True;
    -LABELerroroutside:
    -  feasiblep= feasible;
    -  normp= normal;
    -  qh_fprintf(qh ferr, 6023, "qhull input error: feasible point is not clearly inside halfspace\nfeasible point: ");
    -  for (k=dim; k--; )
    -    qh_fprintf(qh ferr, 8024, qh_REAL_1, r=*(feasiblep++));
    -  qh_fprintf(qh ferr, 8025, "\n     halfspace: ");
    -  for (k=dim; k--; )
    -    qh_fprintf(qh ferr, 8026, qh_REAL_1, r=*(normp++));
    -  qh_fprintf(qh ferr, 8027, "\n     at offset: ");
    -  qh_fprintf(qh ferr, 8028, qh_REAL_1, *offset);
    -  qh_fprintf(qh ferr, 8029, " and distance: ");
    -  qh_fprintf(qh ferr, 8030, qh_REAL_1, dist);
    -  qh_fprintf(qh ferr, 8031, "\n");
    -  return False;
    -} /* sethalfspace */
    -
    -/*---------------------------------
    -
    -  qh_sethalfspace_all( dim, count, halfspaces, feasible )
    -    generate dual for halfspace intersection with feasible point
    -    array of count halfspaces
    -      each halfspace is normal coefficients followed by offset
    -      the origin is inside the halfspace if the offset is negative
    -
    -  returns:
    -    malloc'd array of count X dim-1 points
    -
    -  notes:
    -    call before qh_init_B or qh_initqhull_globals
    -    unused/untested code: please email bradb@shore.net if this works ok for you
    -    If using option 'Fp', also set qh feasible_point. It is a malloc'd array
    -      that is freed by qh_freebuffers.
    -
    -  design:
    -    see qh_sethalfspace
    -*/
    -coordT *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible) {
    -  int i, newdim;
    -  pointT *newpoints;
    -  coordT *coordp, *normalp, *offsetp;
    -
    -  trace0((qh ferr, 12, "qh_sethalfspace_all: compute dual for halfspace intersection\n"));
    -  newdim= dim - 1;
    -  if (!(newpoints=(coordT*)qh_malloc(count*newdim*sizeof(coordT)))){
    -    qh_fprintf(qh ferr, 6024, "qhull error: insufficient memory to compute dual of %d halfspaces\n",
    -          count);
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  coordp= newpoints;
    -  normalp= halfspaces;
    -  for (i=0; i < count; i++) {
    -    offsetp= normalp + newdim;
    -    if (!qh_sethalfspace(newdim, coordp, &coordp, normalp, offsetp, feasible)) {
    -      qh_fprintf(qh ferr, 8032, "The halfspace was at index %d\n", i);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    normalp= offsetp + 1;
    -  }
    -  return newpoints;
    -} /* sethalfspace_all */
    -
    -
    -/*---------------------------------
    -
    -  qh_sharpnewfacets()
    -
    -  returns:
    -    true if could be an acute angle (facets in different quadrants)
    -
    -  notes:
    -    for qh_findbest
    -
    -  design:
    -    for all facets on qh.newfacet_list
    -      if two facets are in different quadrants
    -        set issharp
    -*/
    -boolT qh_sharpnewfacets() {
    -  facetT *facet;
    -  boolT issharp = False;
    -  int *quadrant, k;
    -
    -  quadrant= (int*)qh_memalloc(qh hull_dim * sizeof(int));
    -  FORALLfacet_(qh newfacet_list) {
    -    if (facet == qh newfacet_list) {
    -      for (k=qh hull_dim; k--; )
    -        quadrant[ k]= (facet->normal[ k] > 0);
    -    }else {
    -      for (k=qh hull_dim; k--; ) {
    -        if (quadrant[ k] != (facet->normal[ k] > 0)) {
    -          issharp= True;
    -          break;
    -        }
    -      }
    -    }
    -    if (issharp)
    -      break;
    -  }
    -  qh_memfree( quadrant, qh hull_dim * sizeof(int));
    -  trace3((qh ferr, 3001, "qh_sharpnewfacets: %d\n", issharp));
    -  return issharp;
    -} /* sharpnewfacets */
    -
    -/*---------------------------------
    -
    -  qh_voronoi_center( dim, points )
    -    return Voronoi center for a set of points
    -    dim is the orginal dimension of the points
    -    gh.gm_matrix/qh.gm_row are scratch buffers
    -
    -  returns:
    -    center as a temporary point
    -    if non-simplicial,
    -      returns center for max simplex of points
    -
    -  notes:
    -    from Bowyer & Woodwark, A Programmer's Geometry, 1983, p. 65
    -
    -  design:
    -    if non-simplicial
    -      determine max simplex for points
    -    translate point0 of simplex to origin
    -    compute sum of squares of diagonal
    -    compute determinate
    -    compute Voronoi center (see Bowyer & Woodwark)
    -*/
    -pointT *qh_voronoi_center(int dim, setT *points) {
    -  pointT *point, **pointp, *point0;
    -  pointT *center= (pointT*)qh_memalloc(qh center_size);
    -  setT *simplex;
    -  int i, j, k, size= qh_setsize(points);
    -  coordT *gmcoord;
    -  realT *diffp, sum2, *sum2row, *sum2p, det, factor;
    -  boolT nearzero, infinite;
    -
    -  if (size == dim+1)
    -    simplex= points;
    -  else if (size < dim+1) {
    -    qh_fprintf(qh ferr, 6025, "qhull internal error (qh_voronoi_center):\n  need at least %d points to construct a Voronoi center\n",
    -             dim+1);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -    simplex= points;  /* never executed -- avoids warning */
    -  }else {
    -    simplex= qh_settemp(dim+1);
    -    qh_maxsimplex(dim, points, NULL, 0, &simplex);
    -  }
    -  point0= SETfirstt_(simplex, pointT);
    -  gmcoord= qh gm_matrix;
    -  for (k=0; k < dim; k++) {
    -    qh gm_row[k]= gmcoord;
    -    FOREACHpoint_(simplex) {
    -      if (point != point0)
    -        *(gmcoord++)= point[k] - point0[k];
    -    }
    -  }
    -  sum2row= gmcoord;
    -  for (i=0; i < dim; i++) {
    -    sum2= 0.0;
    -    for (k=0; k < dim; k++) {
    -      diffp= qh gm_row[k] + i;
    -      sum2 += *diffp * *diffp;
    -    }
    -    *(gmcoord++)= sum2;
    -  }
    -  det= qh_determinant(qh gm_row, dim, &nearzero);
    -  factor= qh_divzero(0.5, det, qh MINdenom, &infinite);
    -  if (infinite) {
    -    for (k=dim; k--; )
    -      center[k]= qh_INFINITE;
    -    if (qh IStracing)
    -      qh_printpoints(qh ferr, "qh_voronoi_center: at infinity for ", simplex);
    -  }else {
    -    for (i=0; i < dim; i++) {
    -      gmcoord= qh gm_matrix;
    -      sum2p= sum2row;
    -      for (k=0; k < dim; k++) {
    -        qh gm_row[k]= gmcoord;
    -        if (k == i) {
    -          for (j=dim; j--; )
    -            *(gmcoord++)= *sum2p++;
    -        }else {
    -          FOREACHpoint_(simplex) {
    -            if (point != point0)
    -              *(gmcoord++)= point[k] - point0[k];
    -          }
    -        }
    -      }
    -      center[i]= qh_determinant(qh gm_row, dim, &nearzero)*factor + point0[i];
    -    }
    -#ifndef qh_NOtrace
    -    if (qh IStracing >= 3) {
    -      qh_fprintf(qh ferr, 8033, "qh_voronoi_center: det %2.2g factor %2.2g ", det, factor);
    -      qh_printmatrix(qh ferr, "center:", ¢er, 1, dim);
    -      if (qh IStracing >= 5) {
    -        qh_printpoints(qh ferr, "points", simplex);
    -        FOREACHpoint_(simplex)
    -          qh_fprintf(qh ferr, 8034, "p%d dist %.2g, ", qh_pointid(point),
    -                   qh_pointdist(point, center, dim));
    -        qh_fprintf(qh ferr, 8035, "\n");
    -      }
    -    }
    -#endif
    -  }
    -  if (simplex != points)
    -    qh_settempfree(&simplex);
    -  return center;
    -} /* voronoi_center */
    diff --git a/extern/qhull/global.c b/extern/qhull/global.c
    deleted file mode 100644
    index 90f956d7e885..000000000000
    --- a/extern/qhull/global.c
    +++ /dev/null
    @@ -1,2126 +0,0 @@
    -
    -/*
      ---------------------------------
    -
    -   global.c
    -   initializes all the globals of the qhull application
    -
    -   see README
    -
    -   see libqhull.h for qh.globals and function prototypes
    -
    -   see qhull_a.h for internal functions
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/global.c#15 $$Change: 1490 $
    -   $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $
    - */
    -
    -#include "qhull_a.h"
    -
    -/*========= qh definition -- globals defined in libqhull.h =======================*/
    -
    -int qhull_inuse= 0; /* not used */
    -
    -#if qh_QHpointer
    -qhT *qh_qh= NULL;       /* pointer to all global variables */
    -#else
    -qhT qh_qh;              /* all global variables.
    -                           Add "= {0}" if this causes a compiler error.
    -                           Also qh_qhstat in stat.c and qhmem in mem.c.  */
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_version
    -    version string by year and date
    -
    -    the revision increases on code changes only
    -
    -  notes:
    -    change date:    Changes.txt, Announce.txt, index.htm, README.txt,
    -                    qhull-news.html, Eudora signatures, CMakeLists.txt
    -    change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt
    -    change year:    Copying.txt
    -    check download size
    -    recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c
    -*/
    -
    -const char *qh_version = "2012.1 2012/02/18";
    -
    -/*---------------------------------
    -
    -  qh_appendprint( printFormat )
    -    append printFormat to qh.PRINTout unless already defined
    -*/
    -void qh_appendprint(qh_PRINT format) {
    -  int i;
    -
    -  for (i=0; i < qh_PRINTEND; i++) {
    -    if (qh PRINTout[i] == format && format != qh_PRINTqhull)
    -      break;
    -    if (!qh PRINTout[i]) {
    -      qh PRINTout[i]= format;
    -      break;
    -    }
    -  }
    -} /* appendprint */
    -
    -/*---------------------------------
    -
    -  qh_checkflags( commandStr, hiddenFlags )
    -    errors if commandStr contains hiddenFlags
    -    hiddenFlags starts and ends with a space and is space deliminated (checked)
    -
    -  notes:
    -    ignores first word (e.g., "qconvex i")
    -    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
    -
    -  see:
    -    qh_initflags() initializes Qhull according to commandStr
    -*/
    -void qh_checkflags(char *command, char *hiddenflags) {
    -  char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
    -  char key, opt, prevopt;
    -  char chkkey[]= "   ";
    -  char chkopt[]=  "    ";
    -  char chkopt2[]= "     ";
    -  boolT waserr= False;
    -
    -  if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
    -    qh_fprintf(qh ferr, 6026, "qhull error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"", hiddenflags);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (strpbrk(hiddenflags, ",\n\r\t")) {
    -    qh_fprintf(qh ferr, 6027, "qhull error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"", hiddenflags);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  while (*s && !isspace(*s))  /* skip program name */
    -    s++;
    -  while (*s) {
    -    while (*s && isspace(*s))
    -      s++;
    -    if (*s == '-')
    -      s++;
    -    if (!*s)
    -      break;
    -    key = *s++;
    -    chkerr = NULL;
    -    if (key == 'T' && (*s == 'I' || *s == 'O')) {  /* TI or TO 'file name' */
    -      s= qh_skipfilename(++s);
    -      continue;
    -    }
    -    chkkey[1]= key;
    -    if (strstr(hiddenflags, chkkey)) {
    -      chkerr= chkkey;
    -    }else if (isupper(key)) {
    -      opt= ' ';
    -      prevopt= ' ';
    -      chkopt[1]= key;
    -      chkopt2[1]= key;
    -      while (!chkerr && *s && !isspace(*s)) {
    -        opt= *s++;
    -        if (isalpha(opt)) {
    -          chkopt[2]= opt;
    -          if (strstr(hiddenflags, chkopt))
    -            chkerr= chkopt;
    -          if (prevopt != ' ') {
    -            chkopt2[2]= prevopt;
    -            chkopt2[3]= opt;
    -            if (strstr(hiddenflags, chkopt2))
    -              chkerr= chkopt2;
    -          }
    -        }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
    -              && (prevopt == ' ' || islower(prevopt))) {
    -            chkopt[2]= opt;
    -            if (strstr(hiddenflags, chkopt))
    -              chkerr= chkopt;
    -        }else {
    -          qh_strtod(s-1, &t);
    -          if (s < t)
    -            s= t;
    -        }
    -        prevopt= opt;
    -      }
    -    }
    -    if (chkerr) {
    -      *chkerr= '\'';
    -      chkerr[strlen(chkerr)-1]=  '\'';
    -      qh_fprintf(qh ferr, 6029, "qhull error: option %s is not used with this program.\n             It may be used with qhull.\n", chkerr);
    -      waserr= True;
    -    }
    -  }
    -  if (waserr)
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -} /* checkflags */
    -
    -/*---------------------------------
    -
    -  qh_clear_outputflags()
    -    Clear output flags for QhullPoints
    -*/
    -void qh_clear_outputflags(void) {
    -  int i,k;
    -
    -  qh ANNOTATEoutput= False;
    -  qh DOintersections= False;
    -  qh DROPdim= -1;
    -  qh FORCEoutput= False;
    -  qh GETarea= False;
    -  qh GOODpoint= 0;
    -  qh GOODpointp= NULL;
    -  qh GOODthreshold= False;
    -  qh GOODvertex= 0;
    -  qh GOODvertexp= NULL;
    -  qh IStracing= 0;
    -  qh KEEParea= False;
    -  qh KEEPmerge= False;
    -  qh KEEPminArea= REALmax;
    -  qh PRINTcentrums= False;
    -  qh PRINTcoplanar= False;
    -  qh PRINTdots= False;
    -  qh PRINTgood= False;
    -  qh PRINTinner= False;
    -  qh PRINTneighbors= False;
    -  qh PRINTnoplanes= False;
    -  qh PRINToptions1st= False;
    -  qh PRINTouter= False;
    -  qh PRINTprecision= True;
    -  qh PRINTridges= False;
    -  qh PRINTspheres= False;
    -  qh PRINTstatistics= False;
    -  qh PRINTsummary= False;
    -  qh PRINTtransparent= False;
    -  qh SPLITthresholds= False;
    -  qh TRACElevel= 0;
    -  qh TRInormals= False;
    -  qh USEstdout= False;
    -  qh VERIFYoutput= False;
    -  for (k=qh input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */
    -    qh lower_threshold[k]= -REALmax;
    -    qh upper_threshold[k]= REALmax;
    -    qh lower_bound[k]= -REALmax;
    -    qh upper_bound[k]= REALmax;
    -  }
    -
    -  for (i=0; i < qh_PRINTEND; i++) {
    -    qh PRINTout[i]= qh_PRINTnone;
    -  }
    -
    -  if (!qh qhull_commandsiz2)
    -      qh qhull_commandsiz2= (int)strlen(qh qhull_command); /* WARN64 */
    -  else {
    -      qh qhull_command[qh qhull_commandsiz2]= '\0';
    -  }
    -  if (!qh qhull_optionsiz2)
    -    qh qhull_optionsiz2= (int)strlen(qh qhull_options);  /* WARN64 */
    -  else {
    -    qh qhull_options[qh qhull_optionsiz2]= '\0';
    -    qh qhull_optionlen= qh_OPTIONline;  /* start a new line */
    -  }
    -} /* clear_outputflags */
    -
    -/*---------------------------------
    -
    -  qh_clock()
    -    return user CPU time in 100ths (qh_SECtick)
    -    only defined for qh_CLOCKtype == 2
    -
    -  notes:
    -    use first value to determine time 0
    -    from Stevens '92 8.15
    -*/
    -unsigned long qh_clock(void) {
    -
    -#if (qh_CLOCKtype == 2)
    -  struct tms time;
    -  static long clktck;  /* initialized first call */
    -  double ratio, cpu;
    -  unsigned long ticks;
    -
    -  if (!clktck) {
    -    if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
    -      qh_fprintf(qh ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user.h\n");
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -  }
    -  if (times(&time) == -1) {
    -    qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed.  Use qh_CLOCKtype 1 in user.h\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  ratio= qh_SECticks / (double)clktck;
    -  ticks= time.tms_utime * ratio;
    -  return ticks;
    -#else
    -  qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
    -  qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */
    -  return 0;
    -#endif
    -} /* clock */
    -
    -/*---------------------------------
    -
    -  qh_freebuffers()
    -    free up global memory buffers
    -
    -  notes:
    -    must match qh_initbuffers()
    -*/
    -void qh_freebuffers(void) {
    -
    -  trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
    -  /* allocated by qh_initqhull_buffers */
    -  qh_memfree(qh NEARzero, qh hull_dim * sizeof(realT));
    -  qh_memfree(qh lower_threshold, (qh input_dim+1) * sizeof(realT));
    -  qh_memfree(qh upper_threshold, (qh input_dim+1) * sizeof(realT));
    -  qh_memfree(qh lower_bound, (qh input_dim+1) * sizeof(realT));
    -  qh_memfree(qh upper_bound, (qh input_dim+1) * sizeof(realT));
    -  qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * sizeof(coordT));
    -  qh_memfree(qh gm_row, (qh hull_dim+1) * sizeof(coordT *));
    -  qh NEARzero= qh lower_threshold= qh upper_threshold= NULL;
    -  qh lower_bound= qh upper_bound= NULL;
    -  qh gm_matrix= NULL;
    -  qh gm_row= NULL;
    -  qh_setfree(&qh other_points);
    -  qh_setfree(&qh del_vertices);
    -  qh_setfree(&qh coplanarfacetset);
    -  if (qh line)                /* allocated by qh_readinput, freed if no error */
    -    qh_free(qh line);
    -  if (qh half_space)
    -    qh_free(qh half_space);
    -  if (qh temp_malloc)
    -    qh_free(qh temp_malloc);
    -  if (qh feasible_point)      /* allocated by qh_readfeasible */
    -    qh_free(qh feasible_point);
    -  if (qh feasible_string)     /* allocated by qh_initflags */
    -    qh_free(qh feasible_string);
    -  qh line= qh feasible_string= NULL;
    -  qh half_space= qh feasible_point= qh temp_malloc= NULL;
    -  /* usually allocated by qh_readinput */
    -  if (qh first_point && qh POINTSmalloc) {
    -    qh_free(qh first_point);
    -    qh first_point= NULL;
    -  }
    -  if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */
    -    qh_free(qh input_points);
    -    qh input_points= NULL;
    -  }
    -  trace5((qh ferr, 5002, "qh_freebuffers: finished\n"));
    -} /* freebuffers */
    -
    -
    -/*---------------------------------
    -
    -  qh_freebuild( allmem )
    -    free global memory used by qh_initbuild and qh_buildhull
    -    if !allmem,
    -      does not free short memory (e.g., facetT, freed by qh_memfreeshort)
    -
    -  design:
    -    free centrums
    -    free each vertex
    -    mark unattached ridges
    -    for each facet
    -      free ridges
    -      free outside set, coplanar set, neighbor set, ridge set, vertex set
    -      free facet
    -    free hash table
    -    free interior point
    -    free merge set
    -    free temporary sets
    -*/
    -void qh_freebuild(boolT allmem) {
    -  facetT *facet;
    -  vertexT *vertex;
    -  ridgeT *ridge, **ridgep;
    -  mergeT *merge, **mergep;
    -
    -  trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
    -  if (qh del_vertices)
    -    qh_settruncate(qh del_vertices, 0);
    -  if (allmem) {
    -    while ((vertex= qh vertex_list)) {
    -      if (vertex->next)
    -        qh_delvertex(vertex);
    -      else {
    -        qh_memfree(vertex, (int)sizeof(vertexT));
    -        qh newvertex_list= qh vertex_list= NULL;
    -      }
    -    }
    -  }else if (qh VERTEXneighbors) {
    -    FORALLvertices
    -      qh_setfreelong(&(vertex->neighbors));
    -  }
    -  qh VERTEXneighbors= False;
    -  qh GOODclosest= NULL;
    -  if (allmem) {
    -    FORALLfacets {
    -      FOREACHridge_(facet->ridges)
    -        ridge->seen= False;
    -    }
    -    FORALLfacets {
    -      if (facet->visible) {
    -        FOREACHridge_(facet->ridges) {
    -          if (!otherfacet_(ridge, facet)->visible)
    -            ridge->seen= True;  /* an unattached ridge */
    -        }
    -      }
    -    }
    -    while ((facet= qh facet_list)) {
    -      FOREACHridge_(facet->ridges) {
    -        if (ridge->seen) {
    -          qh_setfree(&(ridge->vertices));
    -          qh_memfree(ridge, (int)sizeof(ridgeT));
    -        }else
    -          ridge->seen= True;
    -      }
    -      qh_setfree(&(facet->outsideset));
    -      qh_setfree(&(facet->coplanarset));
    -      qh_setfree(&(facet->neighbors));
    -      qh_setfree(&(facet->ridges));
    -      qh_setfree(&(facet->vertices));
    -      if (facet->next)
    -        qh_delfacet(facet);
    -      else {
    -        qh_memfree(facet, (int)sizeof(facetT));
    -        qh visible_list= qh newfacet_list= qh facet_list= NULL;
    -      }
    -    }
    -  }else {
    -    FORALLfacets {
    -      qh_setfreelong(&(facet->outsideset));
    -      qh_setfreelong(&(facet->coplanarset));
    -      if (!facet->simplicial) {
    -        qh_setfreelong(&(facet->neighbors));
    -        qh_setfreelong(&(facet->ridges));
    -        qh_setfreelong(&(facet->vertices));
    -      }
    -    }
    -  }
    -  qh_setfree(&(qh hash_table));
    -  qh_memfree(qh interior_point, qh normal_size);
    -  qh interior_point= NULL;
    -  FOREACHmerge_(qh facet_mergeset)  /* usually empty */
    -    qh_memfree(merge, (int)sizeof(mergeT));
    -  qh facet_mergeset= NULL;  /* temp set */
    -  qh degen_mergeset= NULL;  /* temp set */
    -  qh_settempfree_all();
    -} /* freebuild */
    -
    -/*---------------------------------
    -
    -  qh_freeqhull( allmem )
    -    see qh_freeqhull2
    -    if qh_QHpointer, frees qh_qh
    -*/
    -void qh_freeqhull(boolT allmem) {
    -    qh_freeqhull2(allmem);
    -#if qh_QHpointer
    -    qh_free(qh_qh);
    -    qh_qh= NULL;
    -#endif
    -}
    -
    -/*---------------------------------
    -
    -qh_freeqhull2( allmem )
    -  free global memory
    -  if !allmem,
    -    does not free short memory (freed by qh_memfreeshort)
    -
    -notes:
    -  sets qh.NOerrexit in case caller forgets to
    -
    -see:
    -  see qh_initqhull_start2()
    -
    -design:
    -  free global and temporary memory from qh_initbuild and qh_buildhull
    -  free buffers
    -  free statistics
    -*/
    -void qh_freeqhull2(boolT allmem) {
    -
    -  trace1((qh ferr, 1006, "qh_freeqhull2: free global memory\n"));
    -  qh NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
    -  qh_freebuild(allmem);
    -  qh_freebuffers();
    -  qh_freestatistics();
    -#if qh_QHpointer
    -  memset((char *)qh_qh, 0, sizeof(qhT));
    -  /* qh_qh freed by caller, qh_freeqhull() */
    -#else
    -  memset((char *)&qh_qh, 0, sizeof(qhT));
    -#endif
    -  qh NOerrexit= True;
    -} /* freeqhull2 */
    -
    -/*---------------------------------
    -
    -  qh_init_A( infile, outfile, errfile, argc, argv )
    -    initialize memory and stdio files
    -    convert input options to option string (qh.qhull_command)
    -
    -  notes:
    -    infile may be NULL if qh_readpoints() is not called
    -
    -    errfile should always be defined.  It is used for reporting
    -    errors.  outfile is used for output and format options.
    -
    -    argc/argv may be 0/NULL
    -
    -    called before error handling initialized
    -    qh_errexit() may not be used
    -*/
    -void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
    -  qh_meminit(errfile);
    -  qh_initqhull_start(infile, outfile, errfile);
    -  qh_init_qhull_command(argc, argv);
    -} /* init_A */
    -
    -/*---------------------------------
    -
    -  qh_init_B( points, numpoints, dim, ismalloc )
    -    initialize globals for points array
    -
    -    points has numpoints dim-dimensional points
    -      points[0] is the first coordinate of the first point
    -      points[1] is the second coordinate of the first point
    -      points[dim] is the first coordinate of the second point
    -
    -    ismalloc=True
    -      Qhull will call qh_free(points) on exit or input transformation
    -    ismalloc=False
    -      Qhull will allocate a new point array if needed for input transformation
    -
    -    qh.qhull_command
    -      is the option string.
    -      It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags
    -
    -  returns:
    -    if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
    -      projects the input to a new point array
    -
    -        if qh.DELAUNAY,
    -          qh.hull_dim is increased by one
    -        if qh.ATinfinity,
    -          qh_projectinput adds point-at-infinity for Delaunay tri.
    -
    -    if qh.SCALEinput
    -      changes the upper and lower bounds of the input, see qh_scaleinput()
    -
    -    if qh.ROTATEinput
    -      rotates the input by a random rotation, see qh_rotateinput()
    -      if qh.DELAUNAY
    -        rotates about the last coordinate
    -
    -  notes:
    -    called after points are defined
    -    qh_errexit() may be used
    -*/
    -void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) {
    -  qh_initqhull_globals(points, numpoints, dim, ismalloc);
    -  if (qhmem.LASTsize == 0)
    -    qh_initqhull_mem();
    -  /* mem.c and qset.c are initialized */
    -  qh_initqhull_buffers();
    -  qh_initthresholds(qh qhull_command);
    -  if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay))
    -    qh_projectinput();
    -  if (qh SCALEinput)
    -    qh_scaleinput();
    -  if (qh ROTATErandom >= 0) {
    -    qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row);
    -    if (qh DELAUNAY) {
    -      int k, lastk= qh hull_dim-1;
    -      for (k=0; k < lastk; k++) {
    -        qh gm_row[k][lastk]= 0.0;
    -        qh gm_row[lastk][k]= 0.0;
    -      }
    -      qh gm_row[lastk][lastk]= 1.0;
    -    }
    -    qh_gram_schmidt(qh hull_dim, qh gm_row);
    -    qh_rotateinput(qh gm_row);
    -  }
    -} /* init_B */
    -
    -/*---------------------------------
    -
    -  qh_init_qhull_command( argc, argv )
    -    build qh.qhull_command from argc/argv
    -
    -  returns:
    -    a space-delimited string of options (just as typed)
    -
    -  notes:
    -    makes option string easy to input and output
    -
    -    argc/argv may be 0/NULL
    -*/
    -void qh_init_qhull_command(int argc, char *argv[]) {
    -
    -  if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){
    -    /* Assumes qh.ferr is defined. */
    -    qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line\n",
    -          (int)sizeof(qh qhull_command));
    -    qh_exit(qh_ERRinput);  /* error reported, can not use qh_errexit */
    -  }
    -} /* init_qhull_command */
    -
    -/*---------------------------------
    -
    -  qh_initflags( commandStr )
    -    set flags and initialized constants from commandStr
    -
    -  returns:
    -    sets qh.qhull_command to command if needed
    -
    -  notes:
    -    ignores first word (e.g., "qhull d")
    -    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces
    -
    -  see:
    -    qh_initthresholds() continues processing of 'Pdn' and 'PDn'
    -    'prompt' in unix.c for documentation
    -
    -  design:
    -    for each space-deliminated option group
    -      if top-level option
    -        check syntax
    -        append approriate option to option string
    -        set appropriate global variable or append printFormat to print options
    -      else
    -        for each sub-option
    -          check syntax
    -          append approriate option to option string
    -          set appropriate global variable or append printFormat to print options
    -*/
    -void qh_initflags(char *command) {
    -  int k, i, lastproject;
    -  char *s= command, *t, *prev_s, *start, key;
    -  boolT isgeom= False, wasproject;
    -  realT r;
    -
    -  if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) {
    -    if (command != &qh qhull_command[0]) {
    -      *qh qhull_command= '\0';
    -      strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1);
    -    }
    -    while (*s && !isspace(*s))  /* skip program name */
    -      s++;
    -  }
    -  while (*s) {
    -    while (*s && isspace(*s))
    -      s++;
    -    if (*s == '-')
    -      s++;
    -    if (!*s)
    -      break;
    -    prev_s= s;
    -    switch (*s++) {
    -    case 'd':
    -      qh_option("delaunay", NULL, NULL);
    -      qh DELAUNAY= True;
    -      break;
    -    case 'f':
    -      qh_option("facets", NULL, NULL);
    -      qh_appendprint(qh_PRINTfacets);
    -      break;
    -    case 'i':
    -      qh_option("incidence", NULL, NULL);
    -      qh_appendprint(qh_PRINTincidences);
    -      break;
    -    case 'm':
    -      qh_option("mathematica", NULL, NULL);
    -      qh_appendprint(qh_PRINTmathematica);
    -      break;
    -    case 'n':
    -      qh_option("normals", NULL, NULL);
    -      qh_appendprint(qh_PRINTnormals);
    -      break;
    -    case 'o':
    -      qh_option("offFile", NULL, NULL);
    -      qh_appendprint(qh_PRINToff);
    -      break;
    -    case 'p':
    -      qh_option("points", NULL, NULL);
    -      qh_appendprint(qh_PRINTpoints);
    -      break;
    -    case 's':
    -      qh_option("summary", NULL, NULL);
    -      qh PRINTsummary= True;
    -      break;
    -    case 'v':
    -      qh_option("voronoi", NULL, NULL);
    -      qh VORONOI= True;
    -      qh DELAUNAY= True;
    -      break;
    -    case 'A':
    -      if (!isdigit(*s) && *s != '.' && *s != '-')
    -        qh_fprintf(qh ferr, 7002, "qhull warning: no maximum cosine angle given for option 'An'.  Ignored.\n");
    -      else {
    -        if (*s == '-') {
    -          qh premerge_cos= -qh_strtod(s, &s);
    -          qh_option("Angle-premerge-", NULL, &qh premerge_cos);
    -          qh PREmerge= True;
    -        }else {
    -          qh postmerge_cos= qh_strtod(s, &s);
    -          qh_option("Angle-postmerge", NULL, &qh postmerge_cos);
    -          qh POSTmerge= True;
    -        }
    -        qh MERGING= True;
    -      }
    -      break;
    -    case 'C':
    -      if (!isdigit(*s) && *s != '.' && *s != '-')
    -        qh_fprintf(qh ferr, 7003, "qhull warning: no centrum radius given for option 'Cn'.  Ignored.\n");
    -      else {
    -        if (*s == '-') {
    -          qh premerge_centrum= -qh_strtod(s, &s);
    -          qh_option("Centrum-premerge-", NULL, &qh premerge_centrum);
    -          qh PREmerge= True;
    -        }else {
    -          qh postmerge_centrum= qh_strtod(s, &s);
    -          qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum);
    -          qh POSTmerge= True;
    -        }
    -        qh MERGING= True;
    -      }
    -      break;
    -    case 'E':
    -      if (*s == '-')
    -        qh_fprintf(qh ferr, 7004, "qhull warning: negative maximum roundoff given for option 'An'.  Ignored.\n");
    -      else if (!isdigit(*s))
    -        qh_fprintf(qh ferr, 7005, "qhull warning: no maximum roundoff given for option 'En'.  Ignored.\n");
    -      else {
    -        qh DISTround= qh_strtod(s, &s);
    -        qh_option("Distance-roundoff", NULL, &qh DISTround);
    -        qh SETroundoff= True;
    -      }
    -      break;
    -    case 'H':
    -      start= s;
    -      qh HALFspace= True;
    -      qh_strtod(s, &t);
    -      while (t > s)  {
    -        if (*t && !isspace(*t)) {
    -          if (*t == ',')
    -            t++;
    -          else
    -            qh_fprintf(qh ferr, 7006, "qhull warning: origin for Halfspace intersection should be 'Hn,n,n,...'\n");
    -        }
    -        s= t;
    -        qh_strtod(s, &t);
    -      }
    -      if (start < t) {
    -        if (!(qh feasible_string= (char*)calloc((size_t)(t-start+1), (size_t)1))) {
    -          qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
    -          qh_errexit(qh_ERRmem, NULL, NULL);
    -        }
    -        strncpy(qh feasible_string, start, (size_t)(t-start));
    -        qh_option("Halfspace-about", NULL, NULL);
    -        qh_option(qh feasible_string, NULL, NULL);
    -      }else
    -        qh_option("Halfspace", NULL, NULL);
    -      break;
    -    case 'R':
    -      if (!isdigit(*s))
    -        qh_fprintf(qh ferr, 7007, "qhull warning: missing random perturbation for option 'Rn'.  Ignored\n");
    -      else {
    -        qh RANDOMfactor= qh_strtod(s, &s);
    -        qh_option("Random_perturb", NULL, &qh RANDOMfactor);
    -        qh RANDOMdist= True;
    -      }
    -      break;
    -    case 'V':
    -      if (!isdigit(*s) && *s != '-')
    -        qh_fprintf(qh ferr, 7008, "qhull warning: missing visible distance for option 'Vn'.  Ignored\n");
    -      else {
    -        qh MINvisible= qh_strtod(s, &s);
    -        qh_option("Visible", NULL, &qh MINvisible);
    -      }
    -      break;
    -    case 'U':
    -      if (!isdigit(*s) && *s != '-')
    -        qh_fprintf(qh ferr, 7009, "qhull warning: missing coplanar distance for option 'Un'.  Ignored\n");
    -      else {
    -        qh MAXcoplanar= qh_strtod(s, &s);
    -        qh_option("U-coplanar", NULL, &qh MAXcoplanar);
    -      }
    -      break;
    -    case 'W':
    -      if (*s == '-')
    -        qh_fprintf(qh ferr, 7010, "qhull warning: negative outside width for option 'Wn'.  Ignored.\n");
    -      else if (!isdigit(*s))
    -        qh_fprintf(qh ferr, 7011, "qhull warning: missing outside width for option 'Wn'.  Ignored\n");
    -      else {
    -        qh MINoutside= qh_strtod(s, &s);
    -        qh_option("W-outside", NULL, &qh MINoutside);
    -        qh APPROXhull= True;
    -      }
    -      break;
    -    /************  sub menus ***************/
    -    case 'F':
    -      while (*s && !isspace(*s)) {
    -        switch (*s++) {
    -        case 'a':
    -          qh_option("Farea", NULL, NULL);
    -          qh_appendprint(qh_PRINTarea);
    -          qh GETarea= True;
    -          break;
    -        case 'A':
    -          qh_option("FArea-total", NULL, NULL);
    -          qh GETarea= True;
    -          break;
    -        case 'c':
    -          qh_option("Fcoplanars", NULL, NULL);
    -          qh_appendprint(qh_PRINTcoplanars);
    -          break;
    -        case 'C':
    -          qh_option("FCentrums", NULL, NULL);
    -          qh_appendprint(qh_PRINTcentrums);
    -          break;
    -        case 'd':
    -          qh_option("Fd-cdd-in", NULL, NULL);
    -          qh CDDinput= True;
    -          break;
    -        case 'D':
    -          qh_option("FD-cdd-out", NULL, NULL);
    -          qh CDDoutput= True;
    -          break;
    -        case 'F':
    -          qh_option("FFacets-xridge", NULL, NULL);
    -          qh_appendprint(qh_PRINTfacets_xridge);
    -          break;
    -        case 'i':
    -          qh_option("Finner", NULL, NULL);
    -          qh_appendprint(qh_PRINTinner);
    -          break;
    -        case 'I':
    -          qh_option("FIDs", NULL, NULL);
    -          qh_appendprint(qh_PRINTids);
    -          break;
    -        case 'm':
    -          qh_option("Fmerges", NULL, NULL);
    -          qh_appendprint(qh_PRINTmerges);
    -          break;
    -        case 'M':
    -          qh_option("FMaple", NULL, NULL);
    -          qh_appendprint(qh_PRINTmaple);
    -          break;
    -        case 'n':
    -          qh_option("Fneighbors", NULL, NULL);
    -          qh_appendprint(qh_PRINTneighbors);
    -          break;
    -        case 'N':
    -          qh_option("FNeighbors-vertex", NULL, NULL);
    -          qh_appendprint(qh_PRINTvneighbors);
    -          break;
    -        case 'o':
    -          qh_option("Fouter", NULL, NULL);
    -          qh_appendprint(qh_PRINTouter);
    -          break;
    -        case 'O':
    -          if (qh PRINToptions1st) {
    -            qh_option("FOptions", NULL, NULL);
    -            qh_appendprint(qh_PRINToptions);
    -          }else
    -            qh PRINToptions1st= True;
    -          break;
    -        case 'p':
    -          qh_option("Fpoint-intersect", NULL, NULL);
    -          qh_appendprint(qh_PRINTpointintersect);
    -          break;
    -        case 'P':
    -          qh_option("FPoint-nearest", NULL, NULL);
    -          qh_appendprint(qh_PRINTpointnearest);
    -          break;
    -        case 'Q':
    -          qh_option("FQhull", NULL, NULL);
    -          qh_appendprint(qh_PRINTqhull);
    -          break;
    -        case 's':
    -          qh_option("Fsummary", NULL, NULL);
    -          qh_appendprint(qh_PRINTsummary);
    -          break;
    -        case 'S':
    -          qh_option("FSize", NULL, NULL);
    -          qh_appendprint(qh_PRINTsize);
    -          qh GETarea= True;
    -          break;
    -        case 't':
    -          qh_option("Ftriangles", NULL, NULL);
    -          qh_appendprint(qh_PRINTtriangles);
    -          break;
    -        case 'v':
    -          /* option set in qh_initqhull_globals */
    -          qh_appendprint(qh_PRINTvertices);
    -          break;
    -        case 'V':
    -          qh_option("FVertex-average", NULL, NULL);
    -          qh_appendprint(qh_PRINTaverage);
    -          break;
    -        case 'x':
    -          qh_option("Fxtremes", NULL, NULL);
    -          qh_appendprint(qh_PRINTextremes);
    -          break;
    -        default:
    -          s--;
    -          qh_fprintf(qh ferr, 7012, "qhull warning: unknown 'F' output option %c, rest ignored\n", (int)s[0]);
    -          while (*++s && !isspace(*s));
    -          break;
    -        }
    -      }
    -      break;
    -    case 'G':
    -      isgeom= True;
    -      qh_appendprint(qh_PRINTgeom);
    -      while (*s && !isspace(*s)) {
    -        switch (*s++) {
    -        case 'a':
    -          qh_option("Gall-points", NULL, NULL);
    -          qh PRINTdots= True;
    -          break;
    -        case 'c':
    -          qh_option("Gcentrums", NULL, NULL);
    -          qh PRINTcentrums= True;
    -          break;
    -        case 'h':
    -          qh_option("Gintersections", NULL, NULL);
    -          qh DOintersections= True;
    -          break;
    -        case 'i':
    -          qh_option("Ginner", NULL, NULL);
    -          qh PRINTinner= True;
    -          break;
    -        case 'n':
    -          qh_option("Gno-planes", NULL, NULL);
    -          qh PRINTnoplanes= True;
    -          break;
    -        case 'o':
    -          qh_option("Gouter", NULL, NULL);
    -          qh PRINTouter= True;
    -          break;
    -        case 'p':
    -          qh_option("Gpoints", NULL, NULL);
    -          qh PRINTcoplanar= True;
    -          break;
    -        case 'r':
    -          qh_option("Gridges", NULL, NULL);
    -          qh PRINTridges= True;
    -          break;
    -        case 't':
    -          qh_option("Gtransparent", NULL, NULL);
    -          qh PRINTtransparent= True;
    -          break;
    -        case 'v':
    -          qh_option("Gvertices", NULL, NULL);
    -          qh PRINTspheres= True;
    -          break;
    -        case 'D':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 6035, "qhull input error: missing dimension for option 'GDn'\n");
    -          else {
    -            if (qh DROPdim >= 0)
    -              qh_fprintf(qh ferr, 7013, "qhull warning: can only drop one dimension.  Previous 'GD%d' ignored\n",
    -                   qh DROPdim);
    -            qh DROPdim= qh_strtol(s, &s);
    -            qh_option("GDrop-dim", &qh DROPdim, NULL);
    -          }
    -          break;
    -        default:
    -          s--;
    -          qh_fprintf(qh ferr, 7014, "qhull warning: unknown 'G' print option %c, rest ignored\n", (int)s[0]);
    -          while (*++s && !isspace(*s));
    -          break;
    -        }
    -      }
    -      break;
    -    case 'P':
    -      while (*s && !isspace(*s)) {
    -        switch (*s++) {
    -        case 'd': case 'D':  /* see qh_initthresholds() */
    -          key= s[-1];
    -          i= qh_strtol(s, &s);
    -          r= 0;
    -          if (*s == ':') {
    -            s++;
    -            r= qh_strtod(s, &s);
    -          }
    -          if (key == 'd')
    -            qh_option("Pdrop-facets-dim-less", &i, &r);
    -          else
    -            qh_option("PDrop-facets-dim-more", &i, &r);
    -          break;
    -        case 'g':
    -          qh_option("Pgood-facets", NULL, NULL);
    -          qh PRINTgood= True;
    -          break;
    -        case 'G':
    -          qh_option("PGood-facet-neighbors", NULL, NULL);
    -          qh PRINTneighbors= True;
    -          break;
    -        case 'o':
    -          qh_option("Poutput-forced", NULL, NULL);
    -          qh FORCEoutput= True;
    -          break;
    -        case 'p':
    -          qh_option("Pprecision-ignore", NULL, NULL);
    -          qh PRINTprecision= False;
    -          break;
    -        case 'A':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 6036, "qhull input error: missing facet count for keep area option 'PAn'\n");
    -          else {
    -            qh KEEParea= qh_strtol(s, &s);
    -            qh_option("PArea-keep", &qh KEEParea, NULL);
    -            qh GETarea= True;
    -          }
    -          break;
    -        case 'F':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 6037, "qhull input error: missing facet area for option 'PFn'\n");
    -          else {
    -            qh KEEPminArea= qh_strtod(s, &s);
    -            qh_option("PFacet-area-keep", NULL, &qh KEEPminArea);
    -            qh GETarea= True;
    -          }
    -          break;
    -        case 'M':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 6038, "qhull input error: missing merge count for option 'PMn'\n");
    -          else {
    -            qh KEEPmerge= qh_strtol(s, &s);
    -            qh_option("PMerge-keep", &qh KEEPmerge, NULL);
    -          }
    -          break;
    -        default:
    -          s--;
    -          qh_fprintf(qh ferr, 7015, "qhull warning: unknown 'P' print option %c, rest ignored\n", (int)s[0]);
    -          while (*++s && !isspace(*s));
    -          break;
    -        }
    -      }
    -      break;
    -    case 'Q':
    -      lastproject= -1;
    -      while (*s && !isspace(*s)) {
    -        switch (*s++) {
    -        case 'b': case 'B':  /* handled by qh_initthresholds */
    -          key= s[-1];
    -          if (key == 'b' && *s == 'B') {
    -            s++;
    -            r= qh_DEFAULTbox;
    -            qh SCALEinput= True;
    -            qh_option("QbBound-unit-box", NULL, &r);
    -            break;
    -          }
    -          if (key == 'b' && *s == 'b') {
    -            s++;
    -            qh SCALElast= True;
    -            qh_option("Qbbound-last", NULL, NULL);
    -            break;
    -          }
    -          k= qh_strtol(s, &s);
    -          r= 0.0;
    -          wasproject= False;
    -          if (*s == ':') {
    -            s++;
    -            if ((r= qh_strtod(s, &s)) == 0.0) {
    -              t= s;            /* need true dimension for memory allocation */
    -              while (*t && !isspace(*t)) {
    -                if (toupper(*t++) == 'B'
    -                 && k == qh_strtol(t, &t)
    -                 && *t++ == ':'
    -                 && qh_strtod(t, &t) == 0.0) {
    -                  qh PROJECTinput++;
    -                  trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k));
    -                  qh_option("Qb-project-dim", &k, NULL);
    -                  wasproject= True;
    -                  lastproject= k;
    -                  break;
    -                }
    -              }
    -            }
    -          }
    -          if (!wasproject) {
    -            if (lastproject == k && r == 0.0)
    -              lastproject= -1;  /* doesn't catch all possible sequences */
    -            else if (key == 'b') {
    -              qh SCALEinput= True;
    -              if (r == 0.0)
    -                r= -qh_DEFAULTbox;
    -              qh_option("Qbound-dim-low", &k, &r);
    -            }else {
    -              qh SCALEinput= True;
    -              if (r == 0.0)
    -                r= qh_DEFAULTbox;
    -              qh_option("QBound-dim-high", &k, &r);
    -            }
    -          }
    -          break;
    -        case 'c':
    -          qh_option("Qcoplanar-keep", NULL, NULL);
    -          qh KEEPcoplanar= True;
    -          break;
    -        case 'f':
    -          qh_option("Qfurthest-outside", NULL, NULL);
    -          qh BESToutside= True;
    -          break;
    -        case 'g':
    -          qh_option("Qgood-facets-only", NULL, NULL);
    -          qh ONLYgood= True;
    -          break;
    -        case 'i':
    -          qh_option("Qinterior-keep", NULL, NULL);
    -          qh KEEPinside= True;
    -          break;
    -        case 'm':
    -          qh_option("Qmax-outside-only", NULL, NULL);
    -          qh ONLYmax= True;
    -          break;
    -        case 'r':
    -          qh_option("Qrandom-outside", NULL, NULL);
    -          qh RANDOMoutside= True;
    -          break;
    -        case 's':
    -          qh_option("Qsearch-initial-simplex", NULL, NULL);
    -          qh ALLpoints= True;
    -          break;
    -        case 't':
    -          qh_option("Qtriangulate", NULL, NULL);
    -          qh TRIangulate= True;
    -          break;
    -        case 'T':
    -          qh_option("QTestPoints", NULL, NULL);
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 6039, "qhull input error: missing number of test points for option 'QTn'\n");
    -          else {
    -            qh TESTpoints= qh_strtol(s, &s);
    -            qh_option("QTestPoints", &qh TESTpoints, NULL);
    -          }
    -          break;
    -        case 'u':
    -          qh_option("QupperDelaunay", NULL, NULL);
    -          qh UPPERdelaunay= True;
    -          break;
    -        case 'v':
    -          qh_option("Qvertex-neighbors-convex", NULL, NULL);
    -          qh TESTvneighbors= True;
    -          break;
    -        case 'x':
    -          qh_option("Qxact-merge", NULL, NULL);
    -          qh MERGEexact= True;
    -          break;
    -        case 'z':
    -          qh_option("Qz-infinity-point", NULL, NULL);
    -          qh ATinfinity= True;
    -          break;
    -        case '0':
    -          qh_option("Q0-no-premerge", NULL, NULL);
    -          qh NOpremerge= True;
    -          break;
    -        case '1':
    -          if (!isdigit(*s)) {
    -            qh_option("Q1-no-angle-sort", NULL, NULL);
    -            qh ANGLEmerge= False;
    -            break;
    -          }
    -          switch (*s++) {
    -          case '0':
    -            qh_option("Q10-no-narrow", NULL, NULL);
    -            qh NOnarrow= True;
    -            break;
    -          case '1':
    -            qh_option("Q11-trinormals Qtriangulate", NULL, NULL);
    -            qh TRInormals= True;
    -            qh TRIangulate= True;
    -            break;
    -          default:
    -            s--;
    -            qh_fprintf(qh ferr, 7016, "qhull warning: unknown 'Q' qhull option 1%c, rest ignored\n", (int)s[0]);
    -            while (*++s && !isspace(*s));
    -            break;
    -          }
    -          break;
    -        case '2':
    -          qh_option("Q2-no-merge-independent", NULL, NULL);
    -          qh MERGEindependent= False;
    -          goto LABELcheckdigit;
    -          break; /* no warnings */
    -        case '3':
    -          qh_option("Q3-no-merge-vertices", NULL, NULL);
    -          qh MERGEvertices= False;
    -        LABELcheckdigit:
    -          if (isdigit(*s))
    -            qh_fprintf(qh ferr, 7017, "qhull warning: can not follow '1', '2', or '3' with a digit.  '%c' skipped.\n",
    -                     *s++);
    -          break;
    -        case '4':
    -          qh_option("Q4-avoid-old-into-new", NULL, NULL);
    -          qh AVOIDold= True;
    -          break;
    -        case '5':
    -          qh_option("Q5-no-check-outer", NULL, NULL);
    -          qh SKIPcheckmax= True;
    -          break;
    -        case '6':
    -          qh_option("Q6-no-concave-merge", NULL, NULL);
    -          qh SKIPconvex= True;
    -          break;
    -        case '7':
    -          qh_option("Q7-no-breadth-first", NULL, NULL);
    -          qh VIRTUALmemory= True;
    -          break;
    -        case '8':
    -          qh_option("Q8-no-near-inside", NULL, NULL);
    -          qh NOnearinside= True;
    -          break;
    -        case '9':
    -          qh_option("Q9-pick-furthest", NULL, NULL);
    -          qh PICKfurthest= True;
    -          break;
    -        case 'G':
    -          i= qh_strtol(s, &t);
    -          if (qh GOODpoint)
    -            qh_fprintf(qh ferr, 7018, "qhull warning: good point already defined for option 'QGn'.  Ignored\n");
    -          else if (s == t)
    -            qh_fprintf(qh ferr, 7019, "qhull warning: missing good point id for option 'QGn'.  Ignored\n");
    -          else if (i < 0 || *s == '-') {
    -            qh GOODpoint= i-1;
    -            qh_option("QGood-if-dont-see-point", &i, NULL);
    -          }else {
    -            qh GOODpoint= i+1;
    -            qh_option("QGood-if-see-point", &i, NULL);
    -          }
    -          s= t;
    -          break;
    -        case 'J':
    -          if (!isdigit(*s) && *s != '-')
    -            qh JOGGLEmax= 0.0;
    -          else {
    -            qh JOGGLEmax= (realT) qh_strtod(s, &s);
    -            qh_option("QJoggle", NULL, &qh JOGGLEmax);
    -          }
    -          break;
    -        case 'R':
    -          if (!isdigit(*s) && *s != '-')
    -            qh_fprintf(qh ferr, 7020, "qhull warning: missing random seed for option 'QRn'.  Ignored\n");
    -          else {
    -            qh ROTATErandom= i= qh_strtol(s, &s);
    -            if (i > 0)
    -              qh_option("QRotate-id", &i, NULL );
    -            else if (i < -1)
    -              qh_option("QRandom-seed", &i, NULL );
    -          }
    -          break;
    -        case 'V':
    -          i= qh_strtol(s, &t);
    -          if (qh GOODvertex)
    -            qh_fprintf(qh ferr, 7021, "qhull warning: good vertex already defined for option 'QVn'.  Ignored\n");
    -          else if (s == t)
    -            qh_fprintf(qh ferr, 7022, "qhull warning: no good point id given for option 'QVn'.  Ignored\n");
    -          else if (i < 0) {
    -            qh GOODvertex= i - 1;
    -            qh_option("QV-good-facets-not-point", &i, NULL);
    -          }else {
    -            qh_option("QV-good-facets-point", &i, NULL);
    -            qh GOODvertex= i + 1;
    -          }
    -          s= t;
    -          break;
    -        default:
    -          s--;
    -          qh_fprintf(qh ferr, 7023, "qhull warning: unknown 'Q' qhull option %c, rest ignored\n", (int)s[0]);
    -          while (*++s && !isspace(*s));
    -          break;
    -        }
    -      }
    -      break;
    -    case 'T':
    -      while (*s && !isspace(*s)) {
    -        if (isdigit(*s) || *s == '-')
    -          qh IStracing= qh_strtol(s, &s);
    -        else switch (*s++) {
    -        case 'a':
    -          qh_option("Tannotate-output", NULL, NULL);
    -          qh ANNOTATEoutput= True;
    -          break;
    -        case 'c':
    -          qh_option("Tcheck-frequently", NULL, NULL);
    -          qh CHECKfrequently= True;
    -          break;
    -        case 's':
    -          qh_option("Tstatistics", NULL, NULL);
    -          qh PRINTstatistics= True;
    -          break;
    -        case 'v':
    -          qh_option("Tverify", NULL, NULL);
    -          qh VERIFYoutput= True;
    -          break;
    -        case 'z':
    -          if (qh ferr == qh_FILEstderr) {
    -            /* The C++ interface captures the output in qh_fprint_qhull() */
    -            qh_option("Tz-stdout", NULL, NULL);
    -            qh USEstdout= True;
    -          }else if (!qh fout)
    -            qh_fprintf(qh ferr, 7024, "qhull warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
    -          else {
    -            qh_option("Tz-stdout", NULL, NULL);
    -            qh USEstdout= True;
    -            qh ferr= qh fout;
    -            qhmem.ferr= qh fout;
    -          }
    -          break;
    -        case 'C':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7025, "qhull warning: missing point id for cone for trace option 'TCn'.  Ignored\n");
    -          else {
    -            i= qh_strtol(s, &s);
    -            qh_option("TCone-stop", &i, NULL);
    -            qh STOPcone= i + 1;
    -          }
    -          break;
    -        case 'F':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7026, "qhull warning: missing frequency count for trace option 'TFn'.  Ignored\n");
    -          else {
    -            qh REPORTfreq= qh_strtol(s, &s);
    -            qh_option("TFacet-log", &qh REPORTfreq, NULL);
    -            qh REPORTfreq2= qh REPORTfreq/2;  /* for tracemerging() */
    -          }
    -          break;
    -        case 'I':
    -          if (!isspace(*s))
    -            qh_fprintf(qh ferr, 7027, "qhull warning: missing space between 'TI' and filename, %s\n", s);
    -          while (isspace(*s))
    -            s++;
    -          t= qh_skipfilename(s);
    -          {
    -            char filename[qh_FILENAMElen];
    -
    -            qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));   /* WARN64 */
    -            s= t;
    -            if (!freopen(filename, "r", stdin)) {
    -              qh_fprintf(qh ferr, 6041, "qhull error: could not open file \"%s\".", filename);
    -              qh_errexit(qh_ERRinput, NULL, NULL);
    -            }else {
    -              qh_option("TInput-file", NULL, NULL);
    -              qh_option(filename, NULL, NULL);
    -            }
    -          }
    -          break;
    -        case 'O':
    -            if (!isspace(*s))
    -                qh_fprintf(qh ferr, 7028, "qhull warning: missing space between 'TO' and filename, %s\n", s);
    -            while (isspace(*s))
    -                s++;
    -            t= qh_skipfilename(s);
    -            {
    -              char filename[qh_FILENAMElen];
    -
    -              qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));  /* WARN64 */
    -              s= t;
    -              if (!freopen(filename, "w", stdout)) {
    -                qh_fprintf(qh ferr, 6044, "qhull error: could not open file \"%s\".", filename);
    -                qh_errexit(qh_ERRinput, NULL, NULL);
    -              }else {
    -                qh_option("TOutput-file", NULL, NULL);
    -              qh_option(filename, NULL, NULL);
    -            }
    -          }
    -          break;
    -        case 'P':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7029, "qhull warning: missing point id for trace option 'TPn'.  Ignored\n");
    -          else {
    -            qh TRACEpoint= qh_strtol(s, &s);
    -            qh_option("Trace-point", &qh TRACEpoint, NULL);
    -          }
    -          break;
    -        case 'M':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7030, "qhull warning: missing merge id for trace option 'TMn'.  Ignored\n");
    -          else {
    -            qh TRACEmerge= qh_strtol(s, &s);
    -            qh_option("Trace-merge", &qh TRACEmerge, NULL);
    -          }
    -          break;
    -        case 'R':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7031, "qhull warning: missing rerun count for trace option 'TRn'.  Ignored\n");
    -          else {
    -            qh RERUN= qh_strtol(s, &s);
    -            qh_option("TRerun", &qh RERUN, NULL);
    -          }
    -          break;
    -        case 'V':
    -          i= qh_strtol(s, &t);
    -          if (s == t)
    -            qh_fprintf(qh ferr, 7032, "qhull warning: missing furthest point id for trace option 'TVn'.  Ignored\n");
    -          else if (i < 0) {
    -            qh STOPpoint= i - 1;
    -            qh_option("TV-stop-before-point", &i, NULL);
    -          }else {
    -            qh STOPpoint= i + 1;
    -            qh_option("TV-stop-after-point", &i, NULL);
    -          }
    -          s= t;
    -          break;
    -        case 'W':
    -          if (!isdigit(*s))
    -            qh_fprintf(qh ferr, 7033, "qhull warning: missing max width for trace option 'TWn'.  Ignored\n");
    -          else {
    -            qh TRACEdist= (realT) qh_strtod(s, &s);
    -            qh_option("TWide-trace", NULL, &qh TRACEdist);
    -          }
    -          break;
    -        default:
    -          s--;
    -          qh_fprintf(qh ferr, 7034, "qhull warning: unknown 'T' trace option %c, rest ignored\n", (int)s[0]);
    -          while (*++s && !isspace(*s));
    -          break;
    -        }
    -      }
    -      break;
    -    default:
    -      qh_fprintf(qh ferr, 7035, "qhull warning: unknown flag %c(%x)\n", (int)s[-1],
    -               (int)s[-1]);
    -      break;
    -    }
    -    if (s-1 == prev_s && *s && !isspace(*s)) {
    -      qh_fprintf(qh ferr, 7036, "qhull warning: missing space after flag %c(%x); reserved for menu. Skipped.\n",
    -               (int)*prev_s, (int)*prev_s);
    -      while (*s && !isspace(*s))
    -        s++;
    -    }
    -  }
    -  if (qh STOPcone && qh JOGGLEmax < REALmax/2)
    -    qh_fprintf(qh ferr, 7078, "qhull warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
    -  if (isgeom && !qh FORCEoutput && qh PRINTout[1])
    -    qh_fprintf(qh ferr, 7037, "qhull warning: additional output formats are not compatible with Geomview\n");
    -  /* set derived values in qh_initqhull_globals */
    -} /* initflags */
    -
    -
    -/*---------------------------------
    -
    -  qh_initqhull_buffers()
    -    initialize global memory buffers
    -
    -  notes:
    -    must match qh_freebuffers()
    -*/
    -void qh_initqhull_buffers(void) {
    -  int k;
    -
    -  qh TEMPsize= (qhmem.LASTsize - sizeof(setT))/SETelemsize;
    -  if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize)
    -    qh TEMPsize= 8;  /* e.g., if qh_NOmem */
    -  qh other_points= qh_setnew(qh TEMPsize);
    -  qh del_vertices= qh_setnew(qh TEMPsize);
    -  qh coplanarfacetset= qh_setnew(qh TEMPsize);
    -  qh NEARzero= (realT *)qh_memalloc(qh hull_dim * sizeof(realT));
    -  qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    -  qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    -  qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    -  qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * sizeof(realT));
    -  for (k=qh input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_ouputflags */
    -    qh lower_threshold[k]= -REALmax;
    -    qh upper_threshold[k]= REALmax;
    -    qh lower_bound[k]= -REALmax;
    -    qh upper_bound[k]= REALmax;
    -  }
    -  qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * sizeof(coordT));
    -  qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * sizeof(coordT *));
    -} /* initqhull_buffers */
    -
    -/*---------------------------------
    -
    -  qh_initqhull_globals( points, numpoints, dim, ismalloc )
    -    initialize globals
    -    if ismalloc
    -      points were malloc'd and qhull should free at end
    -
    -  returns:
    -    sets qh.first_point, num_points, input_dim, hull_dim and others
    -    seeds random number generator (seed=1 if tracing)
    -    modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
    -    adjust user flags as needed
    -    also checks DIM3 dependencies and constants
    -
    -  notes:
    -    do not use qh_point() since an input transformation may move them elsewhere
    -
    -  see:
    -    qh_initqhull_start() sets default values for non-zero globals
    -
    -  design:
    -    initialize points array from input arguments
    -    test for qh.ZEROcentrum
    -      (i.e., use opposite vertex instead of cetrum for convexity testing)
    -    initialize qh.CENTERtype, qh.normal_size,
    -      qh.center_size, qh.TRACEpoint/level,
    -    initialize and test random numbers
    -    qh_initqhull_outputflags() -- adjust and test output flags
    -*/
    -void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) {
    -  int seed, pointsneeded, extra= 0, i, randi, k;
    -  realT randr;
    -  realT factorial;
    -
    -  time_t timedata;
    -
    -  trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command,
    -      qh qhull_command));
    -  qh POINTSmalloc= ismalloc;
    -  qh first_point= points;
    -  qh num_points= numpoints;
    -  qh hull_dim= qh input_dim= dim;
    -  if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) {
    -    qh MERGING= True;
    -    if (qh hull_dim <= 4) {
    -      qh PREmerge= True;
    -      qh_option("_pre-merge", NULL, NULL);
    -    }else {
    -      qh MERGEexact= True;
    -      qh_option("Qxact_merge", NULL, NULL);
    -    }
    -  }else if (qh MERGEexact)
    -    qh MERGING= True;
    -  if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) {
    -#ifdef qh_NOmerge
    -    qh JOGGLEmax= 0.0;
    -#endif
    -  }
    -  if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && qh PRINTprecision)
    -    qh_fprintf(qh ferr, 7038, "qhull warning: joggle('QJ') always produces simplicial output.  Triangulated output('Qt') does nothing.\n");
    -  if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) {
    -    qh SCALElast= True;
    -    qh_option("Qbbound-last-qj", NULL, NULL);
    -  }
    -  if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2
    -  && qh premerge_centrum == 0) {
    -    qh ZEROcentrum= True;
    -    qh ZEROall_ok= True;
    -    qh_option("_zero-centrum", NULL, NULL);
    -  }
    -  if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision)
    -    qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
    -          REALepsilon);
    -#ifdef qh_NOmerge
    -  if (qh MERGING) {
    -    qh_fprintf(qh ferr, 6045, "qhull input error: merging not installed(qh_NOmerge + 'Qx', 'Cn' or 'An')\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -#endif
    -  if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) {
    -    qh KEEPinside= True;
    -    qh_option("Qinterior-keep", NULL, NULL);
    -  }
    -  if (qh DELAUNAY && qh HALFspace) {
    -    qh_fprintf(qh ferr, 6046, "qhull input error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) {
    -    qh_fprintf(qh ferr, 6047, "qhull input error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (qh UPPERdelaunay && qh ATinfinity) {
    -    qh_fprintf(qh ferr, 6048, "qhull input error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision)
    -    qh_fprintf(qh ferr, 7040, "qhull input warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
    -  qh DOcheckmax= (!qh SKIPcheckmax && qh MERGING );
    -  qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar)
    -                          && !qh NOnearinside);
    -  if (qh MERGING)
    -    qh CENTERtype= qh_AScentrum;
    -  else if (qh VORONOI)
    -    qh CENTERtype= qh_ASvoronoi;
    -  if (qh TESTvneighbors && !qh MERGING) {
    -    qh_fprintf(qh ferr, 6049, "qhull input error: test vertex neighbors('Qv') needs a merge option\n");
    -    qh_errexit(qh_ERRinput, NULL ,NULL);
    -  }
    -  if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) {
    -    qh hull_dim -= qh PROJECTinput;
    -    if (qh DELAUNAY) {
    -      qh hull_dim++;
    -      if (qh ATinfinity)
    -        extra= 1;
    -    }
    -  }
    -  if (qh hull_dim <= 1) {
    -    qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  for (k=2, factorial=1.0; k < qh hull_dim; k++)
    -    factorial *= k;
    -  qh AREAfactor= 1.0 / factorial;
    -  trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals.  dim %d numpoints %d malloc? %d projected %d to hull_dim %d\n",
    -        dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim));
    -  qh normal_size= qh hull_dim * sizeof(coordT);
    -  qh center_size= qh normal_size - sizeof(coordT);
    -  pointsneeded= qh hull_dim+1;
    -  if (qh hull_dim > qh_DIMmergeVertex) {
    -    qh MERGEvertices= False;
    -    qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL);
    -  }
    -  if (qh GOODpoint)
    -    pointsneeded++;
    -#ifdef qh_NOtrace
    -  if (qh IStracing) {
    -    qh_fprintf(qh ferr, 6051, "qhull input error: tracing is not installed(qh_NOtrace in user.h)");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -#endif
    -  if (qh RERUN > 1) {
    -    qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */
    -    if (qh IStracing != -1)
    -      qh IStracing= 0;
    -  }else if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
    -    qh TRACElevel= (qh IStracing? qh IStracing : 3);
    -    qh IStracing= 0;
    -  }
    -  if (qh ROTATErandom == 0 || qh ROTATErandom == -1) {
    -    seed= (int)time(&timedata);
    -    if (qh ROTATErandom  == -1) {
    -      seed= -seed;
    -      qh_option("QRandom-seed", &seed, NULL );
    -    }else
    -      qh_option("QRotate-random", &seed, NULL);
    -    qh ROTATErandom= seed;
    -  }
    -  seed= qh ROTATErandom;
    -  if (seed == INT_MIN)    /* default value */
    -    seed= 1;
    -  else if (seed < 0)
    -    seed= -seed;
    -  qh_RANDOMseed_(seed);
    -  randr= 0.0;
    -  for (i=1000; i--; ) {
    -    randi= qh_RANDOMint;
    -    randr += randi;
    -    if (randi > qh_RANDOMmax) {
    -      qh_fprintf(qh ferr, 8036, "\
    -qhull configuration error (qh_RANDOMmax in user.h):\n\
    -   random integer %d > qh_RANDOMmax(%.8g)\n",
    -               randi, qh_RANDOMmax);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -  }
    -  qh_RANDOMseed_(seed);
    -  randr = randr/1000;
    -  if (randr < qh_RANDOMmax * 0.1
    -  || randr > qh_RANDOMmax * 0.9)
    -    qh_fprintf(qh ferr, 8037, "\
    -qhull configuration warning (qh_RANDOMmax in user.h):\n\
    -   average of 1000 random integers (%.2g) is much different than expected (%.2g).\n\
    -   Is qh_RANDOMmax (%.2g) wrong?\n",
    -             randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
    -  qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax;
    -  qh RANDOMb= 1.0 - qh RANDOMfactor;
    -  if (qh_HASHfactor < 1.1) {
    -    qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1.  Qhull uses linear hash probing\n",
    -      qh_HASHfactor);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (numpoints+extra < pointsneeded) {
    -    qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
    -            numpoints, pointsneeded);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  qh_initqhull_outputflags();
    -} /* initqhull_globals */
    -
    -/*---------------------------------
    -
    -  qh_initqhull_mem(  )
    -    initialize mem.c for qhull
    -    qh.hull_dim and qh.normal_size determine some of the allocation sizes
    -    if qh.MERGING,
    -      includes ridgeT
    -    calls qh_user_memsizes() to add up to 10 additional sizes for quick allocation
    -      (see numsizes below)
    -
    -  returns:
    -    mem.c already for qh_memalloc/qh_memfree (errors if called beforehand)
    -
    -  notes:
    -    qh_produceoutput() prints memsizes
    -
    -*/
    -void qh_initqhull_mem(void) {
    -  int numsizes;
    -  int i;
    -
    -  numsizes= 8+10;
    -  qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes,
    -                     qh_MEMbufsize,qh_MEMinitbuf);
    -  qh_memsize((int)sizeof(vertexT));
    -  if (qh MERGING) {
    -    qh_memsize((int)sizeof(ridgeT));
    -    qh_memsize((int)sizeof(mergeT));
    -  }
    -  qh_memsize((int)sizeof(facetT));
    -  i= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;  /* ridge.vertices */
    -  qh_memsize(i);
    -  qh_memsize(qh normal_size);        /* normal */
    -  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
    -  qh_memsize(i);
    -  qh_user_memsizes();
    -  qh_memsetup();
    -} /* initqhull_mem */
    -
    -/*---------------------------------
    -
    -  qh_initqhull_outputflags
    -    initialize flags concerned with output
    -
    -  returns:
    -    adjust user flags as needed
    -
    -  see:
    -    qh_clear_outputflags() resets the flags
    -
    -  design:
    -    test for qh.PRINTgood (i.e., only print 'good' facets)
    -    check for conflicting print output options
    -*/
    -void qh_initqhull_outputflags(void) {
    -  boolT printgeom= False, printmath= False, printcoplanar= False;
    -  int i;
    -
    -  trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command));
    -  if (!(qh PRINTgood || qh PRINTneighbors)) {
    -    if (qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge || qh DELAUNAY
    -        || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) {
    -      qh PRINTgood= True;
    -      qh_option("Pgood", NULL, NULL);
    -    }
    -  }
    -  if (qh PRINTtransparent) {
    -    if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) {
    -      qh_fprintf(qh ferr, 6215, "qhull input error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    qh DROPdim = 3;
    -    qh PRINTridges = True;
    -  }
    -  for (i=qh_PRINTEND; i--; ) {
    -    if (qh PRINTout[i] == qh_PRINTgeom)
    -      printgeom= True;
    -    else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple)
    -      printmath= True;
    -    else if (qh PRINTout[i] == qh_PRINTcoplanars)
    -      printcoplanar= True;
    -    else if (qh PRINTout[i] == qh_PRINTpointnearest)
    -      printcoplanar= True;
    -    else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) {
    -      qh_fprintf(qh ferr, 6053, "qhull input error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) {
    -      qh_fprintf(qh ferr, 6054, "qhull input error: option 'Ft' is not available for Voronoi vertices or halfspace intersection\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) {
    -      qh_fprintf(qh ferr, 6055, "qhull input error: option 'FC' is not available for Voronoi vertices('v')\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }else if (qh PRINTout[i] == qh_PRINTvertices) {
    -      if (qh VORONOI)
    -        qh_option("Fvoronoi", NULL, NULL);
    -      else
    -        qh_option("Fvertices", NULL, NULL);
    -    }
    -  }
    -  if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) {
    -    if (qh PRINTprecision)
    -      qh_fprintf(qh ferr, 7041, "qhull input warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
    -  }
    -  if (printmath && (qh hull_dim > 3 || qh VORONOI)) {
    -    qh_fprintf(qh ferr, 6056, "qhull input error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (printgeom) {
    -    if (qh hull_dim > 4) {
    -      qh_fprintf(qh ferr, 6057, "qhull input error: Geomview output is only available for 2-d, 3-d and 4-d\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums
    -     + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) {
    -      qh_fprintf(qh ferr, 6058, "qhull input error: no output specified for Geomview\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) {
    -      qh_fprintf(qh ferr, 6059, "qhull input error: Geomview output for Voronoi diagrams only for 2-d\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    /* can not warn about furthest-site Geomview output: no lower_threshold */
    -    if (qh hull_dim == 4 && qh DROPdim == -1 &&
    -        (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
    -      qh_fprintf(qh ferr, 7042, "qhull input warning: coplanars, vertices, and centrums output not\n\
    -available for 4-d output(ignored).  Could use 'GDn' instead.\n");
    -      qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False;
    -    }
    -  }
    -  if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) {
    -    if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) {
    -      if (qh QHULLfinished) {
    -        qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
    -      }else {
    -        qh KEEPcoplanar = True;
    -        qh_option("Qcoplanar", NULL, NULL);
    -      }
    -    }
    -  }
    -  qh PRINTdim= qh hull_dim;
    -  if (qh DROPdim >=0) {    /* after Geomview checks */
    -    if (qh DROPdim < qh hull_dim) {
    -      qh PRINTdim--;
    -      if (!printgeom || qh hull_dim < 3)
    -        qh_fprintf(qh ferr, 7043, "qhull input warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim);
    -    }else
    -      qh DROPdim= -1;
    -  }else if (qh VORONOI) {
    -    qh DROPdim= qh hull_dim-1;
    -    qh PRINTdim= qh hull_dim-1;
    -  }
    -} /* qh_initqhull_outputflags */
    -
    -/*---------------------------------
    -
    -  qh_initqhull_start( infile, outfile, errfile )
    -    allocate memory if needed and call qh_initqhull_start2()
    -*/
    -void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) {
    -
    -#if qh_QHpointer
    -  if (qh_qh) {
    -    qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined.  Call qh_save_qhull() first\n");
    -    qh_exit(qh_ERRqhull);  /* no error handler */
    -  }
    -  if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) {
    -    qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n");
    -    qh_exit(qh_ERRmem);  /* no error handler */
    -  }
    -#endif
    -  qh_initstatistics();
    -  qh_initqhull_start2(infile, outfile, errfile);
    -} /* initqhull_start */
    -
    -/*---------------------------------
    -
    -  qh_initqhull_start2( infile, outfile, errfile )
    -    start initialization of qhull
    -    initialize statistics, stdio, default values for global variables
    -    assumes qh_qh is defined
    -  notes:
    -    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
    -  see:
    -    qh_maxmin() determines the precision constants
    -    qh_freeqhull2()
    -*/
    -void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) {
    -  time_t timedata;
    -  int seed;
    -
    -  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
    -#if qh_QHpointer
    -  memset((char *)qh_qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
    -#else
    -  memset((char *)&qh_qh, 0, sizeof(qhT));
    -#endif
    -  qh ANGLEmerge= True;
    -  qh DROPdim= -1;
    -  qh ferr= errfile;
    -  qh fin= infile;
    -  qh fout= outfile;
    -  qh furthest_id= -1;
    -  qh JOGGLEmax= REALmax;
    -  qh KEEPminArea = REALmax;
    -  qh last_low= REALmax;
    -  qh last_high= REALmax;
    -  qh last_newhigh= REALmax;
    -  qh max_outside= 0.0;
    -  qh max_vertex= 0.0;
    -  qh MAXabs_coord= 0.0;
    -  qh MAXsumcoord= 0.0;
    -  qh MAXwidth= -REALmax;
    -  qh MERGEindependent= True;
    -  qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
    -  qh MINoutside= 0.0;
    -  qh MINvisible= REALmax;
    -  qh MAXcoplanar= REALmax;
    -  qh outside_err= REALmax;
    -  qh premerge_centrum= 0.0;
    -  qh premerge_cos= REALmax;
    -  qh PRINTprecision= True;
    -  qh PRINTradius= 0.0;
    -  qh postmerge_cos= REALmax;
    -  qh postmerge_centrum= 0.0;
    -  qh ROTATErandom= INT_MIN;
    -  qh MERGEvertices= True;
    -  qh totarea= 0.0;
    -  qh totvol= 0.0;
    -  qh TRACEdist= REALmax;
    -  qh TRACEpoint= -1; /* recompile or use 'TPn' */
    -  qh tracefacet_id= UINT_MAX;  /* recompile to trace a facet */
    -  qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex */
    -  seed= (int)time(&timedata);
    -  qh_RANDOMseed_(seed);
    -  qh run_id= qh_RANDOMint+1; /* disallow 0 [UsingLibQhull::NOqhRunId] */
    -  qh_option("run-id", &qh run_id, NULL);
    -  strcat(qh qhull, "qhull");
    -} /* initqhull_start2 */
    -
    -/*---------------------------------
    -
    -  qh_initthresholds( commandString )
    -    set thresholds for printing and scaling from commandString
    -
    -  returns:
    -    sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used
    -
    -  see:
    -    qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
    -    qh_inthresholds()
    -
    -  design:
    -    for each 'Pdn' or 'PDn' option
    -      check syntax
    -      set qh.lower_threshold or qh.upper_threshold
    -    set qh.GOODthreshold if an unbounded threshold is used
    -    set qh.SPLITthreshold if a bounded threshold is used
    -*/
    -void qh_initthresholds(char *command) {
    -  realT value;
    -  int idx, maxdim, k;
    -  char *s= command; /* non-const due to strtol */
    -  char key;
    -
    -  maxdim= qh input_dim;
    -  if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput))
    -    maxdim++;
    -  while (*s) {
    -    if (*s == '-')
    -      s++;
    -    if (*s == 'P') {
    -      s++;
    -      while (*s && !isspace(key= *s++)) {
    -        if (key == 'd' || key == 'D') {
    -          if (!isdigit(*s)) {
    -            qh_fprintf(qh ferr, 7044, "qhull warning: no dimension given for Print option '%c' at: %s.  Ignored\n",
    -                    key, s-1);
    -            continue;
    -          }
    -          idx= qh_strtol(s, &s);
    -          if (idx >= qh hull_dim) {
    -            qh_fprintf(qh ferr, 7045, "qhull warning: dimension %d for Print option '%c' is >= %d.  Ignored\n",
    -                idx, key, qh hull_dim);
    -            continue;
    -          }
    -          if (*s == ':') {
    -            s++;
    -            value= qh_strtod(s, &s);
    -            if (fabs((double)value) > 1.0) {
    -              qh_fprintf(qh ferr, 7046, "qhull warning: value %2.4g for Print option %c is > +1 or < -1.  Ignored\n",
    -                      value, key);
    -              continue;
    -            }
    -          }else
    -            value= 0.0;
    -          if (key == 'd')
    -            qh lower_threshold[idx]= value;
    -          else
    -            qh upper_threshold[idx]= value;
    -        }
    -      }
    -    }else if (*s == 'Q') {
    -      s++;
    -      while (*s && !isspace(key= *s++)) {
    -        if (key == 'b' && *s == 'B') {
    -          s++;
    -          for (k=maxdim; k--; ) {
    -            qh lower_bound[k]= -qh_DEFAULTbox;
    -            qh upper_bound[k]= qh_DEFAULTbox;
    -          }
    -        }else if (key == 'b' && *s == 'b')
    -          s++;
    -        else if (key == 'b' || key == 'B') {
    -          if (!isdigit(*s)) {
    -            qh_fprintf(qh ferr, 7047, "qhull warning: no dimension given for Qhull option %c.  Ignored\n",
    -                    key);
    -            continue;
    -          }
    -          idx= qh_strtol(s, &s);
    -          if (idx >= maxdim) {
    -            qh_fprintf(qh ferr, 7048, "qhull warning: dimension %d for Qhull option %c is >= %d.  Ignored\n",
    -                idx, key, maxdim);
    -            continue;
    -          }
    -          if (*s == ':') {
    -            s++;
    -            value= qh_strtod(s, &s);
    -          }else if (key == 'b')
    -            value= -qh_DEFAULTbox;
    -          else
    -            value= qh_DEFAULTbox;
    -          if (key == 'b')
    -            qh lower_bound[idx]= value;
    -          else
    -            qh upper_bound[idx]= value;
    -        }
    -      }
    -    }else {
    -      while (*s && !isspace(*s))
    -        s++;
    -    }
    -    while (isspace(*s))
    -      s++;
    -  }
    -  for (k=qh hull_dim; k--; ) {
    -    if (qh lower_threshold[k] > -REALmax/2) {
    -      qh GOODthreshold= True;
    -      if (qh upper_threshold[k] < REALmax/2) {
    -        qh SPLITthresholds= True;
    -        qh GOODthreshold= False;
    -        break;
    -      }
    -    }else if (qh upper_threshold[k] < REALmax/2)
    -      qh GOODthreshold= True;
    -  }
    -} /* initthresholds */
    -
    -/*---------------------------------
    -
    -  qh_option( option, intVal, realVal )
    -    add an option description to qh.qhull_options
    -
    -  notes:
    -    NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
    -    will be printed with statistics ('Ts') and errors
    -    strlen(option) < 40
    -*/
    -void qh_option(const char *option, int *i, realT *r) {
    -  char buf[200];
    -  int len, maxlen;
    -
    -  sprintf(buf, "  %s", option);
    -  if (i)
    -    sprintf(buf+strlen(buf), " %d", *i);
    -  if (r)
    -    sprintf(buf+strlen(buf), " %2.2g", *r);
    -  len= (int)strlen(buf);  /* WARN64 */
    -  qh qhull_optionlen += len;
    -  maxlen= sizeof(qh qhull_options) - len -1;
    -  maximize_(maxlen, 0);
    -  if (qh qhull_optionlen >= qh_OPTIONline && maxlen > 0) {
    -    qh qhull_optionlen= len;
    -    strncat(qh qhull_options, "\n", (size_t)(maxlen--));
    -  }
    -  strncat(qh qhull_options, buf, (size_t)maxlen);
    -} /* option */
    -
    -#if qh_QHpointer
    -/*---------------------------------
    -
    -  qh_restore_qhull( oldqh )
    -    restores a previously saved qhull
    -    also restores qh_qhstat and qhmem.tempstack
    -    Sets *oldqh to NULL
    -  notes:
    -    errors if current qhull hasn't been saved or freed
    -    uses qhmem for error reporting
    -
    -  NOTE 1998/5/11:
    -    Freeing memory after qh_save_qhull and qh_restore_qhull
    -    is complicated.  The procedures will be redesigned.
    -
    -  see:
    -    qh_save_qhull(), UsingLibQhull
    -*/
    -void qh_restore_qhull(qhT **oldqh) {
    -
    -  if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) {
    -    qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n",
    -                  *oldqh);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (qh_qh) {
    -    qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (!*oldqh || !(*oldqh)->old_qhstat) {
    -    qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n",
    -                  *oldqh);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  qh_qh= *oldqh;
    -  *oldqh= NULL;
    -  qh_qhstat= qh old_qhstat;
    -  qhmem.tempstack= qh old_tempstack;
    -  qh old_qhstat= 0;
    -  qh old_tempstack= 0;
    -  trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh));
    -} /* restore_qhull */
    -
    -/*---------------------------------
    -
    -  qh_save_qhull(  )
    -    saves qhull for a later qh_restore_qhull
    -    also saves qh_qhstat and qhmem.tempstack
    -
    -  returns:
    -    qh_qh=NULL
    -
    -  notes:
    -    need to initialize qhull or call qh_restore_qhull before continuing
    -
    -  NOTE 1998/5/11:
    -    Freeing memory after qh_save_qhull and qh_restore_qhull
    -    is complicated.  The procedures will be redesigned.
    -
    -  see:
    -    qh_restore_qhull()
    -*/
    -qhT *qh_save_qhull(void) {
    -  qhT *oldqh;
    -
    -  trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh));
    -  if (!qh_qh) {
    -    qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  qh old_qhstat= qh_qhstat;
    -  qh_qhstat= NULL;
    -  qh old_tempstack= qhmem.tempstack;
    -  qhmem.tempstack= NULL;
    -  oldqh= qh_qh;
    -  qh_qh= NULL;
    -  return oldqh;
    -} /* save_qhull */
    -
    -#endif
    diff --git a/extern/qhull/io.c b/extern/qhull/io.c
    deleted file mode 100644
    index 092e3ef79a53..000000000000
    --- a/extern/qhull/io.c
    +++ /dev/null
    @@ -1,4059 +0,0 @@
    -/*
      ---------------------------------
    -
    -   io.c
    -   Input/Output routines of qhull application
    -
    -   see qh-io.htm and io.h
    -
    -   see user.c for qh_errprint and qh_printfacetlist
    -
    -   unix.c calls qh_readpoints and qh_produce_output
    -
    -   unix.c and user.c are the only callers of io.c functions
    -   This allows the user to avoid loading io.o from qhull.a
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/io.c#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*========= -functions in alphabetical order after qh_produce_output()  =====*/
    -
    -/*---------------------------------
    -
    -  qh_produce_output()
    -  qh_produce_output2()
    -    prints out the result of qhull in desired format
    -    qh_produce_output2() does not call qh_prepare_output()
    -    if qh.GETarea
    -      computes and prints area and volume
    -    qh.PRINTout[] is an array of output formats
    -
    -  notes:
    -    prints output in qh.PRINTout order
    -*/
    -void qh_produce_output(void) {
    -    int tempsize= qh_setsize(qhmem.tempstack);
    -
    -    qh_prepare_output();
    -    qh_produce_output2();
    -    if (qh_setsize(qhmem.tempstack) != tempsize) {
    -        qh_fprintf(qh ferr, 6206, "qhull internal error (qh_produce_output): temporary sets not empty(%d)\n",
    -            qh_setsize(qhmem.tempstack));
    -        qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -} /* produce_output */
    -
    -
    -void qh_produce_output2(void) {
    -  int i, tempsize= qh_setsize(qhmem.tempstack), d_1;
    -
    -  if (qh PRINTsummary)
    -    qh_printsummary(qh ferr);
    -  else if (qh PRINTout[0] == qh_PRINTnone)
    -    qh_printsummary(qh fout);
    -  for (i=0; i < qh_PRINTEND; i++)
    -    qh_printfacets(qh fout, qh PRINTout[i], qh facet_list, NULL, !qh_ALL);
    -  qh_allstatistics();
    -  if (qh PRINTprecision && !qh MERGING && (qh JOGGLEmax > REALmax/2 || qh RERUN))
    -    qh_printstats(qh ferr, qhstat precision, NULL);
    -  if (qh VERIFYoutput && (zzval_(Zridge) > 0 || zzval_(Zridgemid) > 0))
    -    qh_printstats(qh ferr, qhstat vridges, NULL);
    -  if (qh PRINTstatistics) {
    -    qh_printstatistics(qh ferr, "");
    -    qh_memstatistics(qh ferr);
    -    d_1= sizeof(setT) + (qh hull_dim - 1) * SETelemsize;
    -    qh_fprintf(qh ferr, 8040, "\
    -    size in bytes: merge %d ridge %d vertex %d facet %d\n\
    -         normal %d ridge vertices %d facet vertices or neighbors %d\n",
    -            (int)sizeof(mergeT), (int)sizeof(ridgeT),
    -            (int)sizeof(vertexT), (int)sizeof(facetT),
    -            qh normal_size, d_1, d_1 + SETelemsize);
    -  }
    -  if (qh_setsize(qhmem.tempstack) != tempsize) {
    -    qh_fprintf(qh ferr, 6065, "qhull internal error (qh_produce_output2): temporary sets not empty(%d)\n",
    -             qh_setsize(qhmem.tempstack));
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -} /* produce_output2 */
    -
    -/*---------------------------------
    -
    -  dfacet( id )
    -    print facet by id, for debugging
    -
    -*/
    -void dfacet(unsigned id) {
    -  facetT *facet;
    -
    -  FORALLfacets {
    -    if (facet->id == id) {
    -      qh_printfacet(qh fout, facet);
    -      break;
    -    }
    -  }
    -} /* dfacet */
    -
    -
    -/*---------------------------------
    -
    -  dvertex( id )
    -    print vertex by id, for debugging
    -*/
    -void dvertex(unsigned id) {
    -  vertexT *vertex;
    -
    -  FORALLvertices {
    -    if (vertex->id == id) {
    -      qh_printvertex(qh fout, vertex);
    -      break;
    -    }
    -  }
    -} /* dvertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_compare_vertexpoint( p1, p2 )
    -    used by qsort() to order vertices by point id
    -*/
    -int qh_compare_vertexpoint(const void *p1, const void *p2) {
    -  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
    -
    -  return((qh_pointid(a->point) > qh_pointid(b->point)?1:-1));
    -} /* compare_vertexpoint */
    -
    -/*---------------------------------
    -
    -  qh_compare_facetarea( p1, p2 )
    -    used by qsort() to order facets by area
    -*/
    -int qh_compare_facetarea(const void *p1, const void *p2) {
    -  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
    -
    -  if (!a->isarea)
    -    return -1;
    -  if (!b->isarea)
    -    return 1;
    -  if (a->f.area > b->f.area)
    -    return 1;
    -  else if (a->f.area == b->f.area)
    -    return 0;
    -  return -1;
    -} /* compare_facetarea */
    -
    -/*---------------------------------
    -
    -  qh_compare_facetmerge( p1, p2 )
    -    used by qsort() to order facets by number of merges
    -*/
    -int qh_compare_facetmerge(const void *p1, const void *p2) {
    -  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
    -
    -  return(a->nummerge - b->nummerge);
    -} /* compare_facetvisit */
    -
    -/*---------------------------------
    -
    -  qh_compare_facetvisit( p1, p2 )
    -    used by qsort() to order facets by visit id or id
    -*/
    -int qh_compare_facetvisit(const void *p1, const void *p2) {
    -  const facetT *a= *((facetT *const*)p1), *b= *((facetT *const*)p2);
    -  int i,j;
    -
    -  if (!(i= a->visitid))
    -    i= 0 - a->id; /* do not convert to int, sign distinguishes id from visitid */
    -  if (!(j= b->visitid))
    -    j= 0 - b->id;
    -  return(i - j);
    -} /* compare_facetvisit */
    -
    -/*---------------------------------
    -
    -  qh_copyfilename( dest, size, source, length )
    -    copy filename identified by qh_skipfilename()
    -
    -  notes:
    -    see qh_skipfilename() for syntax
    -*/
    -void qh_copyfilename(char *filename, int size, const char* source, int length) {
    -  char c= *source;
    -
    -  if (length > size + 1) {
    -      qh_fprintf(qh ferr, 6040, "qhull error: filename is more than %d characters, %s\n",  size-1, source);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  strncpy(filename, source, length);
    -  filename[length]= '\0';
    -  if (c == '\'' || c == '"') {
    -    char *s= filename + 1;
    -    char *t= filename;
    -    while (*s) {
    -      if (*s == c) {
    -          if (s[-1] == '\\')
    -              t[-1]= c;
    -      }else
    -          *t++= *s;
    -      s++;
    -    }
    -    *t= '\0';
    -  }
    -} /* copyfilename */
    -
    -/*---------------------------------
    -
    -  qh_countfacets( facetlist, facets, printall,
    -          numfacets, numsimplicial, totneighbors, numridges, numcoplanar, numtricoplanars  )
    -    count good facets for printing and set visitid
    -    if allfacets, ignores qh_skipfacet()
    -
    -  notes:
    -    qh_printsummary and qh_countfacets must match counts
    -
    -  returns:
    -    numfacets, numsimplicial, total neighbors, numridges, coplanars
    -    each facet with ->visitid indicating 1-relative position
    -      ->visitid==0 indicates not good
    -
    -  notes
    -    numfacets >= numsimplicial
    -    if qh.NEWfacets,
    -      does not count visible facets (matches qh_printafacet)
    -
    -  design:
    -    for all facets on facetlist and in facets set
    -      unless facet is skipped or visible (i.e., will be deleted)
    -        mark facet->visitid
    -        update counts
    -*/
    -void qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
    -    int *numfacetsp, int *numsimplicialp, int *totneighborsp, int *numridgesp, int *numcoplanarsp, int *numtricoplanarsp) {
    -  facetT *facet, **facetp;
    -  int numfacets= 0, numsimplicial= 0, numridges= 0, totneighbors= 0, numcoplanars= 0, numtricoplanars= 0;
    -
    -  FORALLfacet_(facetlist) {
    -    if ((facet->visible && qh NEWfacets)
    -    || (!printall && qh_skipfacet(facet)))
    -      facet->visitid= 0;
    -    else {
    -      facet->visitid= ++numfacets;
    -      totneighbors += qh_setsize(facet->neighbors);
    -      if (facet->simplicial) {
    -        numsimplicial++;
    -        if (facet->keepcentrum && facet->tricoplanar)
    -          numtricoplanars++;
    -      }else
    -        numridges += qh_setsize(facet->ridges);
    -      if (facet->coplanarset)
    -        numcoplanars += qh_setsize(facet->coplanarset);
    -    }
    -  }
    -
    -  FOREACHfacet_(facets) {
    -    if ((facet->visible && qh NEWfacets)
    -    || (!printall && qh_skipfacet(facet)))
    -      facet->visitid= 0;
    -    else {
    -      facet->visitid= ++numfacets;
    -      totneighbors += qh_setsize(facet->neighbors);
    -      if (facet->simplicial){
    -        numsimplicial++;
    -        if (facet->keepcentrum && facet->tricoplanar)
    -          numtricoplanars++;
    -      }else
    -        numridges += qh_setsize(facet->ridges);
    -      if (facet->coplanarset)
    -        numcoplanars += qh_setsize(facet->coplanarset);
    -    }
    -  }
    -  qh visit_id += numfacets+1;
    -  *numfacetsp= numfacets;
    -  *numsimplicialp= numsimplicial;
    -  *totneighborsp= totneighbors;
    -  *numridgesp= numridges;
    -  *numcoplanarsp= numcoplanars;
    -  *numtricoplanarsp= numtricoplanars;
    -} /* countfacets */
    -
    -/*---------------------------------
    -
    -  qh_detvnorm( vertex, vertexA, centers, offset )
    -    compute separating plane of the Voronoi diagram for a pair of input sites
    -    centers= set of facets (i.e., Voronoi vertices)
    -      facet->visitid= 0 iff vertex-at-infinity (i.e., unbounded)
    -
    -  assumes:
    -    qh_ASvoronoi and qh_vertexneighbors() already set
    -
    -  returns:
    -    norm
    -      a pointer into qh.gm_matrix to qh.hull_dim-1 reals
    -      copy the data before reusing qh.gm_matrix
    -    offset
    -      if 'QVn'
    -        sign adjusted so that qh.GOODvertexp is inside
    -      else
    -        sign adjusted so that vertex is inside
    -
    -    qh.gm_matrix= simplex of points from centers relative to first center
    -
    -  notes:
    -    in io.c so that code for 'v Tv' can be removed by removing io.c
    -    returns pointer into qh.gm_matrix to avoid tracking of temporary memory
    -
    -  design:
    -    determine midpoint of input sites
    -    build points as the set of Voronoi vertices
    -    select a simplex from points (if necessary)
    -      include midpoint if the Voronoi region is unbounded
    -    relocate the first vertex of the simplex to the origin
    -    compute the normalized hyperplane through the simplex
    -    orient the hyperplane toward 'QVn' or 'vertex'
    -    if 'Tv' or 'Ts'
    -      if bounded
    -        test that hyperplane is the perpendicular bisector of the input sites
    -      test that Voronoi vertices not in the simplex are still on the hyperplane
    -    free up temporary memory
    -*/
    -pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp) {
    -  facetT *facet, **facetp;
    -  int  i, k, pointid, pointidA, point_i, point_n;
    -  setT *simplex= NULL;
    -  pointT *point, **pointp, *point0, *midpoint, *normal, *inpoint;
    -  coordT *coord, *gmcoord, *normalp;
    -  setT *points= qh_settemp(qh TEMPsize);
    -  boolT nearzero= False;
    -  boolT unbounded= False;
    -  int numcenters= 0;
    -  int dim= qh hull_dim - 1;
    -  realT dist, offset, angle, zero= 0.0;
    -
    -  midpoint= qh gm_matrix + qh hull_dim * qh hull_dim;  /* last row */
    -  for (k=0; k < dim; k++)
    -    midpoint[k]= (vertex->point[k] + vertexA->point[k])/2;
    -  FOREACHfacet_(centers) {
    -    numcenters++;
    -    if (!facet->visitid)
    -      unbounded= True;
    -    else {
    -      if (!facet->center)
    -        facet->center= qh_facetcenter(facet->vertices);
    -      qh_setappend(&points, facet->center);
    -    }
    -  }
    -  if (numcenters > dim) {
    -    simplex= qh_settemp(qh TEMPsize);
    -    qh_setappend(&simplex, vertex->point);
    -    if (unbounded)
    -      qh_setappend(&simplex, midpoint);
    -    qh_maxsimplex(dim, points, NULL, 0, &simplex);
    -    qh_setdelnth(simplex, 0);
    -  }else if (numcenters == dim) {
    -    if (unbounded)
    -      qh_setappend(&points, midpoint);
    -    simplex= points;
    -  }else {
    -    qh_fprintf(qh ferr, 6216, "qhull internal error (qh_detvnorm): too few points(%d) to compute separating plane\n", numcenters);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  i= 0;
    -  gmcoord= qh gm_matrix;
    -  point0= SETfirstt_(simplex, pointT);
    -  FOREACHpoint_(simplex) {
    -    if (qh IStracing >= 4)
    -      qh_printmatrix(qh ferr, "qh_detvnorm: Voronoi vertex or midpoint",
    -                              &point, 1, dim);
    -    if (point != point0) {
    -      qh gm_row[i++]= gmcoord;
    -      coord= point0;
    -      for (k=dim; k--; )
    -        *(gmcoord++)= *point++ - *coord++;
    -    }
    -  }
    -  qh gm_row[i]= gmcoord;  /* does not overlap midpoint, may be used later for qh_areasimplex */
    -  normal= gmcoord;
    -  qh_sethyperplane_gauss(dim, qh gm_row, point0, True,
    -                normal, &offset, &nearzero);
    -  if (qh GOODvertexp == vertexA->point)
    -    inpoint= vertexA->point;
    -  else
    -    inpoint= vertex->point;
    -  zinc_(Zdistio);
    -  dist= qh_distnorm(dim, inpoint, normal, &offset);
    -  if (dist > 0) {
    -    offset= -offset;
    -    normalp= normal;
    -    for (k=dim; k--; ) {
    -      *normalp= -(*normalp);
    -      normalp++;
    -    }
    -  }
    -  if (qh VERIFYoutput || qh PRINTstatistics) {
    -    pointid= qh_pointid(vertex->point);
    -    pointidA= qh_pointid(vertexA->point);
    -    if (!unbounded) {
    -      zinc_(Zdiststat);
    -      dist= qh_distnorm(dim, midpoint, normal, &offset);
    -      if (dist < 0)
    -        dist= -dist;
    -      zzinc_(Zridgemid);
    -      wwmax_(Wridgemidmax, dist);
    -      wwadd_(Wridgemid, dist);
    -      trace4((qh ferr, 4014, "qh_detvnorm: points %d %d midpoint dist %2.2g\n",
    -                 pointid, pointidA, dist));
    -      for (k=0; k < dim; k++)
    -        midpoint[k]= vertexA->point[k] - vertex->point[k];  /* overwrites midpoint! */
    -      qh_normalize(midpoint, dim, False);
    -      angle= qh_distnorm(dim, midpoint, normal, &zero); /* qh_detangle uses dim+1 */
    -      if (angle < 0.0)
    -        angle= angle + 1.0;
    -      else
    -        angle= angle - 1.0;
    -      if (angle < 0.0)
    -        angle -= angle;
    -      trace4((qh ferr, 4015, "qh_detvnorm: points %d %d angle %2.2g nearzero %d\n",
    -                 pointid, pointidA, angle, nearzero));
    -      if (nearzero) {
    -        zzinc_(Zridge0);
    -        wwmax_(Wridge0max, angle);
    -        wwadd_(Wridge0, angle);
    -      }else {
    -        zzinc_(Zridgeok)
    -        wwmax_(Wridgeokmax, angle);
    -        wwadd_(Wridgeok, angle);
    -      }
    -    }
    -    if (simplex != points) {
    -      FOREACHpoint_i_(points) {
    -        if (!qh_setin(simplex, point)) {
    -          facet= SETelemt_(centers, point_i, facetT);
    -          zinc_(Zdiststat);
    -          dist= qh_distnorm(dim, point, normal, &offset);
    -          if (dist < 0)
    -            dist= -dist;
    -          zzinc_(Zridge);
    -          wwmax_(Wridgemax, dist);
    -          wwadd_(Wridge, dist);
    -          trace4((qh ferr, 4016, "qh_detvnorm: points %d %d Voronoi vertex %d dist %2.2g\n",
    -                             pointid, pointidA, facet->visitid, dist));
    -        }
    -      }
    -    }
    -  }
    -  *offsetp= offset;
    -  if (simplex != points)
    -    qh_settempfree(&simplex);
    -  qh_settempfree(&points);
    -  return normal;
    -} /* detvnorm */
    -
    -/*---------------------------------
    -
    -  qh_detvridge( vertexA )
    -    determine Voronoi ridge from 'seen' neighbors of vertexA
    -    include one vertex-at-infinite if an !neighbor->visitid
    -
    -  returns:
    -    temporary set of centers (facets, i.e., Voronoi vertices)
    -    sorted by center id
    -*/
    -setT *qh_detvridge(vertexT *vertex) {
    -  setT *centers= qh_settemp(qh TEMPsize);
    -  setT *tricenters= qh_settemp(qh TEMPsize);
    -  facetT *neighbor, **neighborp;
    -  boolT firstinf= True;
    -
    -  FOREACHneighbor_(vertex) {
    -    if (neighbor->seen) {
    -      if (neighbor->visitid) {
    -        if (!neighbor->tricoplanar || qh_setunique(&tricenters, neighbor->center))
    -          qh_setappend(¢ers, neighbor);
    -      }else if (firstinf) {
    -        firstinf= False;
    -        qh_setappend(¢ers, neighbor);
    -      }
    -    }
    -  }
    -  qsort(SETaddr_(centers, facetT), (size_t)qh_setsize(centers),
    -             sizeof(facetT *), qh_compare_facetvisit);
    -  qh_settempfree(&tricenters);
    -  return centers;
    -} /* detvridge */
    -
    -/*---------------------------------
    -
    -  qh_detvridge3( atvertex, vertex )
    -    determine 3-d Voronoi ridge from 'seen' neighbors of atvertex and vertex
    -    include one vertex-at-infinite for !neighbor->visitid
    -    assumes all facet->seen2= True
    -
    -  returns:
    -    temporary set of centers (facets, i.e., Voronoi vertices)
    -    listed in adjacency order (!oriented)
    -    all facet->seen2= True
    -
    -  design:
    -    mark all neighbors of atvertex
    -    for each adjacent neighbor of both atvertex and vertex
    -      if neighbor selected
    -        add neighbor to set of Voronoi vertices
    -*/
    -setT *qh_detvridge3 (vertexT *atvertex, vertexT *vertex) {
    -  setT *centers= qh_settemp(qh TEMPsize);
    -  setT *tricenters= qh_settemp(qh TEMPsize);
    -  facetT *neighbor, **neighborp, *facet= NULL;
    -  boolT firstinf= True;
    -
    -  FOREACHneighbor_(atvertex)
    -    neighbor->seen2= False;
    -  FOREACHneighbor_(vertex) {
    -    if (!neighbor->seen2) {
    -      facet= neighbor;
    -      break;
    -    }
    -  }
    -  while (facet) {
    -    facet->seen2= True;
    -    if (neighbor->seen) {
    -      if (facet->visitid) {
    -        if (!facet->tricoplanar || qh_setunique(&tricenters, facet->center))
    -          qh_setappend(¢ers, facet);
    -      }else if (firstinf) {
    -        firstinf= False;
    -        qh_setappend(¢ers, facet);
    -      }
    -    }
    -    FOREACHneighbor_(facet) {
    -      if (!neighbor->seen2) {
    -        if (qh_setin(vertex->neighbors, neighbor))
    -          break;
    -        else
    -          neighbor->seen2= True;
    -      }
    -    }
    -    facet= neighbor;
    -  }
    -  if (qh CHECKfrequently) {
    -    FOREACHneighbor_(vertex) {
    -      if (!neighbor->seen2) {
    -          qh_fprintf(qh ferr, 6217, "qhull internal error (qh_detvridge3): neighbors of vertex p%d are not connected at facet %d\n",
    -                 qh_pointid(vertex->point), neighbor->id);
    -        qh_errexit(qh_ERRqhull, neighbor, NULL);
    -      }
    -    }
    -  }
    -  FOREACHneighbor_(atvertex)
    -    neighbor->seen2= True;
    -  qh_settempfree(&tricenters);
    -  return centers;
    -} /* detvridge3 */
    -
    -/*---------------------------------
    -
    -  qh_eachvoronoi( fp, printvridge, vertex, visitall, innerouter, inorder )
    -    if visitall,
    -      visit all Voronoi ridges for vertex (i.e., an input site)
    -    else
    -      visit all unvisited Voronoi ridges for vertex
    -      all vertex->seen= False if unvisited
    -    assumes
    -      all facet->seen= False
    -      all facet->seen2= True (for qh_detvridge3)
    -      all facet->visitid == 0 if vertex_at_infinity
    -                         == index of Voronoi vertex
    -                         >= qh.num_facets if ignored
    -    innerouter:
    -      qh_RIDGEall--  both inner (bounded) and outer(unbounded) ridges
    -      qh_RIDGEinner- only inner
    -      qh_RIDGEouter- only outer
    -
    -    if inorder
    -      orders vertices for 3-d Voronoi diagrams
    -
    -  returns:
    -    number of visited ridges (does not include previously visited ridges)
    -
    -    if printvridge,
    -      calls printvridge( fp, vertex, vertexA, centers)
    -        fp== any pointer (assumes FILE*)
    -        vertex,vertexA= pair of input sites that define a Voronoi ridge
    -        centers= set of facets (i.e., Voronoi vertices)
    -                 ->visitid == index or 0 if vertex_at_infinity
    -                 ordered for 3-d Voronoi diagram
    -  notes:
    -    uses qh.vertex_visit
    -
    -  see:
    -    qh_eachvoronoi_all()
    -
    -  design:
    -    mark selected neighbors of atvertex
    -    for each selected neighbor (either Voronoi vertex or vertex-at-infinity)
    -      for each unvisited vertex
    -        if atvertex and vertex share more than d-1 neighbors
    -          bump totalcount
    -          if printvridge defined
    -            build the set of shared neighbors (i.e., Voronoi vertices)
    -            call printvridge
    -*/
    -int qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder) {
    -  boolT unbounded;
    -  int count;
    -  facetT *neighbor, **neighborp, *neighborA, **neighborAp;
    -  setT *centers;
    -  setT *tricenters= qh_settemp(qh TEMPsize);
    -
    -  vertexT *vertex, **vertexp;
    -  boolT firstinf;
    -  unsigned int numfacets= (unsigned int)qh num_facets;
    -  int totridges= 0;
    -
    -  qh vertex_visit++;
    -  atvertex->seen= True;
    -  if (visitall) {
    -    FORALLvertices
    -      vertex->seen= False;
    -  }
    -  FOREACHneighbor_(atvertex) {
    -    if (neighbor->visitid < numfacets)
    -      neighbor->seen= True;
    -  }
    -  FOREACHneighbor_(atvertex) {
    -    if (neighbor->seen) {
    -      FOREACHvertex_(neighbor->vertices) {
    -        if (vertex->visitid != qh vertex_visit && !vertex->seen) {
    -          vertex->visitid= qh vertex_visit;
    -          count= 0;
    -          firstinf= True;
    -          qh_settruncate(tricenters, 0);
    -          FOREACHneighborA_(vertex) {
    -            if (neighborA->seen) {
    -              if (neighborA->visitid) {
    -                if (!neighborA->tricoplanar || qh_setunique(&tricenters, neighborA->center))
    -                  count++;
    -              }else if (firstinf) {
    -                count++;
    -                firstinf= False;
    -              }
    -            }
    -          }
    -          if (count >= qh hull_dim - 1) {  /* e.g., 3 for 3-d Voronoi */
    -            if (firstinf) {
    -              if (innerouter == qh_RIDGEouter)
    -                continue;
    -              unbounded= False;
    -            }else {
    -              if (innerouter == qh_RIDGEinner)
    -                continue;
    -              unbounded= True;
    -            }
    -            totridges++;
    -            trace4((qh ferr, 4017, "qh_eachvoronoi: Voronoi ridge of %d vertices between sites %d and %d\n",
    -                  count, qh_pointid(atvertex->point), qh_pointid(vertex->point)));
    -            if (printvridge && fp) {
    -              if (inorder && qh hull_dim == 3+1) /* 3-d Voronoi diagram */
    -                centers= qh_detvridge3 (atvertex, vertex);
    -              else
    -                centers= qh_detvridge(vertex);
    -              (*printvridge) (fp, atvertex, vertex, centers, unbounded);
    -              qh_settempfree(¢ers);
    -            }
    -          }
    -        }
    -      }
    -    }
    -  }
    -  FOREACHneighbor_(atvertex)
    -    neighbor->seen= False;
    -  qh_settempfree(&tricenters);
    -  return totridges;
    -} /* eachvoronoi */
    -
    -
    -/*---------------------------------
    -
    -  qh_eachvoronoi_all( fp, printvridge, isUpper, innerouter, inorder )
    -    visit all Voronoi ridges
    -
    -    innerouter:
    -      see qh_eachvoronoi()
    -
    -    if inorder
    -      orders vertices for 3-d Voronoi diagrams
    -
    -  returns
    -    total number of ridges
    -
    -    if isUpper == facet->upperdelaunay  (i.e., a Vornoi vertex)
    -      facet->visitid= Voronoi vertex index(same as 'o' format)
    -    else
    -      facet->visitid= 0
    -
    -    if printvridge,
    -      calls printvridge( fp, vertex, vertexA, centers)
    -      [see qh_eachvoronoi]
    -
    -  notes:
    -    Not used for qhull.exe
    -    same effect as qh_printvdiagram but ridges not sorted by point id
    -*/
    -int qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder) {
    -  facetT *facet;
    -  vertexT *vertex;
    -  int numcenters= 1;  /* vertex 0 is vertex-at-infinity */
    -  int totridges= 0;
    -
    -  qh_clearcenters(qh_ASvoronoi);
    -  qh_vertexneighbors();
    -  maximize_(qh visit_id, (unsigned) qh num_facets);
    -  FORALLfacets {
    -    facet->visitid= 0;
    -    facet->seen= False;
    -    facet->seen2= True;
    -  }
    -  FORALLfacets {
    -    if (facet->upperdelaunay == isUpper)
    -      facet->visitid= numcenters++;
    -  }
    -  FORALLvertices
    -    vertex->seen= False;
    -  FORALLvertices {
    -    if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
    -      continue;
    -    totridges += qh_eachvoronoi(fp, printvridge, vertex,
    -                   !qh_ALL, innerouter, inorder);
    -  }
    -  return totridges;
    -} /* eachvoronoi_all */
    -
    -/*---------------------------------
    -
    -  qh_facet2point( facet, point0, point1, mindist )
    -    return two projected temporary vertices for a 2-d facet
    -    may be non-simplicial
    -
    -  returns:
    -    point0 and point1 oriented and projected to the facet
    -    returns mindist (maximum distance below plane)
    -*/
    -void qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist) {
    -  vertexT *vertex0, *vertex1;
    -  realT dist;
    -
    -  if (facet->toporient ^ qh_ORIENTclock) {
    -    vertex0= SETfirstt_(facet->vertices, vertexT);
    -    vertex1= SETsecondt_(facet->vertices, vertexT);
    -  }else {
    -    vertex1= SETfirstt_(facet->vertices, vertexT);
    -    vertex0= SETsecondt_(facet->vertices, vertexT);
    -  }
    -  zadd_(Zdistio, 2);
    -  qh_distplane(vertex0->point, facet, &dist);
    -  *mindist= dist;
    -  *point0= qh_projectpoint(vertex0->point, facet, dist);
    -  qh_distplane(vertex1->point, facet, &dist);
    -  minimize_(*mindist, dist);
    -  *point1= qh_projectpoint(vertex1->point, facet, dist);
    -} /* facet2point */
    -
    -
    -/*---------------------------------
    -
    -  qh_facetvertices( facetlist, facets, allfacets )
    -    returns temporary set of vertices in a set and/or list of facets
    -    if allfacets, ignores qh_skipfacet()
    -
    -  returns:
    -    vertices with qh.vertex_visit
    -
    -  notes:
    -    optimized for allfacets of facet_list
    -
    -  design:
    -    if allfacets of facet_list
    -      create vertex set from vertex_list
    -    else
    -      for each selected facet in facets or facetlist
    -        append unvisited vertices to vertex set
    -*/
    -setT *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets) {
    -  setT *vertices;
    -  facetT *facet, **facetp;
    -  vertexT *vertex, **vertexp;
    -
    -  qh vertex_visit++;
    -  if (facetlist == qh facet_list && allfacets && !facets) {
    -    vertices= qh_settemp(qh num_vertices);
    -    FORALLvertices {
    -      vertex->visitid= qh vertex_visit;
    -      qh_setappend(&vertices, vertex);
    -    }
    -  }else {
    -    vertices= qh_settemp(qh TEMPsize);
    -    FORALLfacet_(facetlist) {
    -      if (!allfacets && qh_skipfacet(facet))
    -        continue;
    -      FOREACHvertex_(facet->vertices) {
    -        if (vertex->visitid != qh vertex_visit) {
    -          vertex->visitid= qh vertex_visit;
    -          qh_setappend(&vertices, vertex);
    -        }
    -      }
    -    }
    -  }
    -  FOREACHfacet_(facets) {
    -    if (!allfacets && qh_skipfacet(facet))
    -      continue;
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->visitid != qh vertex_visit) {
    -        vertex->visitid= qh vertex_visit;
    -        qh_setappend(&vertices, vertex);
    -      }
    -    }
    -  }
    -  return vertices;
    -} /* facetvertices */
    -
    -/*---------------------------------
    -
    -  qh_geomplanes( facet, outerplane, innerplane )
    -    return outer and inner planes for Geomview
    -    qh.PRINTradius is size of vertices and points (includes qh.JOGGLEmax)
    -
    -  notes:
    -    assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
    -*/
    -void qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane) {
    -  realT radius;
    -
    -  if (qh MERGING || qh JOGGLEmax < REALmax/2) {
    -    qh_outerinner(facet, outerplane, innerplane);
    -    radius= qh PRINTradius;
    -    if (qh JOGGLEmax < REALmax/2)
    -      radius -= qh JOGGLEmax * sqrt((realT)qh hull_dim);  /* already accounted for in qh_outerinner() */
    -    *outerplane += radius;
    -    *innerplane -= radius;
    -    if (qh PRINTcoplanar || qh PRINTspheres) {
    -      *outerplane += qh MAXabs_coord * qh_GEOMepsilon;
    -      *innerplane -= qh MAXabs_coord * qh_GEOMepsilon;
    -    }
    -  }else
    -    *innerplane= *outerplane= 0;
    -} /* geomplanes */
    -
    -
    -/*---------------------------------
    -
    -  qh_markkeep( facetlist )
    -    mark good facets that meet qh.KEEParea, qh.KEEPmerge, and qh.KEEPminArea
    -    ignores visible facets (!part of convex hull)
    -
    -  returns:
    -    may clear facet->good
    -    recomputes qh.num_good
    -
    -  design:
    -    get set of good facets
    -    if qh.KEEParea
    -      sort facets by area
    -      clear facet->good for all but n largest facets
    -    if qh.KEEPmerge
    -      sort facets by merge count
    -      clear facet->good for all but n most merged facets
    -    if qh.KEEPminarea
    -      clear facet->good if area too small
    -    update qh.num_good
    -*/
    -void qh_markkeep(facetT *facetlist) {
    -  facetT *facet, **facetp;
    -  setT *facets= qh_settemp(qh num_facets);
    -  int size, count;
    -
    -  trace2((qh ferr, 2006, "qh_markkeep: only keep %d largest and/or %d most merged facets and/or min area %.2g\n",
    -          qh KEEParea, qh KEEPmerge, qh KEEPminArea));
    -  FORALLfacet_(facetlist) {
    -    if (!facet->visible && facet->good)
    -      qh_setappend(&facets, facet);
    -  }
    -  size= qh_setsize(facets);
    -  if (qh KEEParea) {
    -    qsort(SETaddr_(facets, facetT), (size_t)size,
    -             sizeof(facetT *), qh_compare_facetarea);
    -    if ((count= size - qh KEEParea) > 0) {
    -      FOREACHfacet_(facets) {
    -        facet->good= False;
    -        if (--count == 0)
    -          break;
    -      }
    -    }
    -  }
    -  if (qh KEEPmerge) {
    -    qsort(SETaddr_(facets, facetT), (size_t)size,
    -             sizeof(facetT *), qh_compare_facetmerge);
    -    if ((count= size - qh KEEPmerge) > 0) {
    -      FOREACHfacet_(facets) {
    -        facet->good= False;
    -        if (--count == 0)
    -          break;
    -      }
    -    }
    -  }
    -  if (qh KEEPminArea < REALmax/2) {
    -    FOREACHfacet_(facets) {
    -      if (!facet->isarea || facet->f.area < qh KEEPminArea)
    -        facet->good= False;
    -    }
    -  }
    -  qh_settempfree(&facets);
    -  count= 0;
    -  FORALLfacet_(facetlist) {
    -    if (facet->good)
    -      count++;
    -  }
    -  qh num_good= count;
    -} /* markkeep */
    -
    -
    -/*---------------------------------
    -
    -  qh_markvoronoi( facetlist, facets, printall, isLower, numcenters )
    -    mark voronoi vertices for printing by site pairs
    -
    -  returns:
    -    temporary set of vertices indexed by pointid
    -    isLower set if printing lower hull (i.e., at least one facet is lower hull)
    -    numcenters= total number of Voronoi vertices
    -    bumps qh.printoutnum for vertex-at-infinity
    -    clears all facet->seen and sets facet->seen2
    -
    -    if selected
    -      facet->visitid= Voronoi vertex id
    -    else if upper hull (or 'Qu' and lower hull)
    -      facet->visitid= 0
    -    else
    -      facet->visitid >= qh num_facets
    -
    -  notes:
    -    ignores qh.ATinfinity, if defined
    -*/
    -setT *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp) {
    -  int numcenters=0;
    -  facetT *facet, **facetp;
    -  setT *vertices;
    -  boolT isLower= False;
    -
    -  qh printoutnum++;
    -  qh_clearcenters(qh_ASvoronoi);  /* in case, qh_printvdiagram2 called by user */
    -  qh_vertexneighbors();
    -  vertices= qh_pointvertex();
    -  if (qh ATinfinity)
    -    SETelem_(vertices, qh num_points-1)= NULL;
    -  qh visit_id++;
    -  maximize_(qh visit_id, (unsigned) qh num_facets);
    -  FORALLfacet_(facetlist) {
    -    if (printall || !qh_skipfacet(facet)) {
    -      if (!facet->upperdelaunay) {
    -        isLower= True;
    -        break;
    -      }
    -    }
    -  }
    -  FOREACHfacet_(facets) {
    -    if (printall || !qh_skipfacet(facet)) {
    -      if (!facet->upperdelaunay) {
    -        isLower= True;
    -        break;
    -      }
    -    }
    -  }
    -  FORALLfacets {
    -    if (facet->normal && (facet->upperdelaunay == isLower))
    -      facet->visitid= 0;  /* facetlist or facets may overwrite */
    -    else
    -      facet->visitid= qh visit_id;
    -    facet->seen= False;
    -    facet->seen2= True;
    -  }
    -  numcenters++;  /* qh_INFINITE */
    -  FORALLfacet_(facetlist) {
    -    if (printall || !qh_skipfacet(facet))
    -      facet->visitid= numcenters++;
    -  }
    -  FOREACHfacet_(facets) {
    -    if (printall || !qh_skipfacet(facet))
    -      facet->visitid= numcenters++;
    -  }
    -  *isLowerp= isLower;
    -  *numcentersp= numcenters;
    -  trace2((qh ferr, 2007, "qh_markvoronoi: isLower %d numcenters %d\n", isLower, numcenters));
    -  return vertices;
    -} /* markvoronoi */
    -
    -/*---------------------------------
    -
    -  qh_order_vertexneighbors( vertex )
    -    order facet neighbors of a 2-d or 3-d vertex by adjacency
    -
    -  notes:
    -    does not orient the neighbors
    -
    -  design:
    -    initialize a new neighbor set with the first facet in vertex->neighbors
    -    while vertex->neighbors non-empty
    -      select next neighbor in the previous facet's neighbor set
    -    set vertex->neighbors to the new neighbor set
    -*/
    -void qh_order_vertexneighbors(vertexT *vertex) {
    -  setT *newset;
    -  facetT *facet, *neighbor, **neighborp;
    -
    -  trace4((qh ferr, 4018, "qh_order_vertexneighbors: order neighbors of v%d for 3-d\n", vertex->id));
    -  newset= qh_settemp(qh_setsize(vertex->neighbors));
    -  facet= (facetT*)qh_setdellast(vertex->neighbors);
    -  qh_setappend(&newset, facet);
    -  while (qh_setsize(vertex->neighbors)) {
    -    FOREACHneighbor_(vertex) {
    -      if (qh_setin(facet->neighbors, neighbor)) {
    -        qh_setdel(vertex->neighbors, neighbor);
    -        qh_setappend(&newset, neighbor);
    -        facet= neighbor;
    -        break;
    -      }
    -    }
    -    if (!neighbor) {
    -      qh_fprintf(qh ferr, 6066, "qhull internal error (qh_order_vertexneighbors): no neighbor of v%d for f%d\n",
    -        vertex->id, facet->id);
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -  }
    -  qh_setfree(&vertex->neighbors);
    -  qh_settemppop();
    -  vertex->neighbors= newset;
    -} /* order_vertexneighbors */
    -
    -/*---------------------------------
    -
    -  qh_prepare_output( )
    -    prepare for qh_produce_output2() according to
    -      qh.KEEPminArea, KEEParea, KEEPmerge, GOODvertex, GOODthreshold, GOODpoint, ONLYgood, SPLITthresholds
    -    does not reset facet->good
    -
    -  notes
    -    except for PRINTstatistics, no-op if previously called with same options
    -*/
    -void qh_prepare_output(void) {
    -  if (qh VORONOI) {
    -    qh_clearcenters (qh_ASvoronoi);
    -    qh_vertexneighbors();
    -  }
    -  if (qh TRIangulate && !qh hasTriangulation) {
    -    qh_triangulate();
    -    if (qh VERIFYoutput && !qh CHECKfrequently)
    -      qh_checkpolygon (qh facet_list);
    -  }
    -  qh_findgood_all (qh facet_list);
    -  if (qh GETarea)
    -    qh_getarea(qh facet_list);
    -  if (qh KEEParea || qh KEEPmerge || qh KEEPminArea < REALmax/2)
    -    qh_markkeep (qh facet_list);
    -  if (qh PRINTstatistics)
    -    qh_collectstatistics();
    -}
    -
    -/*---------------------------------
    -
    -  qh_printafacet( fp, format, facet, printall )
    -    print facet to fp in given output format (see qh.PRINTout)
    -
    -  returns:
    -    nop if !printall and qh_skipfacet()
    -    nop if visible facet and NEWfacets and format != PRINTfacets
    -    must match qh_countfacets
    -
    -  notes
    -    preserves qh.visit_id
    -    facet->normal may be null if PREmerge/MERGEexact and STOPcone before merge
    -
    -  see
    -    qh_printbegin() and qh_printend()
    -
    -  design:
    -    test for printing facet
    -    call appropriate routine for format
    -    or output results directly
    -*/
    -void qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall) {
    -  realT color[4], offset, dist, outerplane, innerplane;
    -  boolT zerodiv;
    -  coordT *point, *normp, *coordp, **pointp, *feasiblep;
    -  int k;
    -  vertexT *vertex, **vertexp;
    -  facetT *neighbor, **neighborp;
    -
    -  if (!printall && qh_skipfacet(facet))
    -    return;
    -  if (facet->visible && qh NEWfacets && format != qh_PRINTfacets)
    -    return;
    -  qh printoutnum++;
    -  switch (format) {
    -  case qh_PRINTarea:
    -    if (facet->isarea) {
    -      qh_fprintf(fp, 9009, qh_REAL_1, facet->f.area);
    -      qh_fprintf(fp, 9010, "\n");
    -    }else
    -      qh_fprintf(fp, 9011, "0\n");
    -    break;
    -  case qh_PRINTcoplanars:
    -    qh_fprintf(fp, 9012, "%d", qh_setsize(facet->coplanarset));
    -    FOREACHpoint_(facet->coplanarset)
    -      qh_fprintf(fp, 9013, " %d", qh_pointid(point));
    -    qh_fprintf(fp, 9014, "\n");
    -    break;
    -  case qh_PRINTcentrums:
    -    qh_printcenter(fp, format, NULL, facet);
    -    break;
    -  case qh_PRINTfacets:
    -    qh_printfacet(fp, facet);
    -    break;
    -  case qh_PRINTfacets_xridge:
    -    qh_printfacetheader(fp, facet);
    -    break;
    -  case qh_PRINTgeom:  /* either 2 , 3, or 4-d by qh_printbegin */
    -    if (!facet->normal)
    -      break;
    -    for (k=qh hull_dim; k--; ) {
    -      color[k]= (facet->normal[k]+1.0)/2.0;
    -      maximize_(color[k], -1.0);
    -      minimize_(color[k], +1.0);
    -    }
    -    qh_projectdim3 (color, color);
    -    if (qh PRINTdim != qh hull_dim)
    -      qh_normalize2 (color, 3, True, NULL, NULL);
    -    if (qh hull_dim <= 2)
    -      qh_printfacet2geom(fp, facet, color);
    -    else if (qh hull_dim == 3) {
    -      if (facet->simplicial)
    -        qh_printfacet3geom_simplicial(fp, facet, color);
    -      else
    -        qh_printfacet3geom_nonsimplicial(fp, facet, color);
    -    }else {
    -      if (facet->simplicial)
    -        qh_printfacet4geom_simplicial(fp, facet, color);
    -      else
    -        qh_printfacet4geom_nonsimplicial(fp, facet, color);
    -    }
    -    break;
    -  case qh_PRINTids:
    -    qh_fprintf(fp, 9015, "%d\n", facet->id);
    -    break;
    -  case qh_PRINTincidences:
    -  case qh_PRINToff:
    -  case qh_PRINTtriangles:
    -    if (qh hull_dim == 3 && format != qh_PRINTtriangles)
    -      qh_printfacet3vertex(fp, facet, format);
    -    else if (facet->simplicial || qh hull_dim == 2 || format == qh_PRINToff)
    -      qh_printfacetNvertex_simplicial(fp, facet, format);
    -    else
    -      qh_printfacetNvertex_nonsimplicial(fp, facet, qh printoutvar++, format);
    -    break;
    -  case qh_PRINTinner:
    -    qh_outerinner(facet, NULL, &innerplane);
    -    offset= facet->offset - innerplane;
    -    goto LABELprintnorm;
    -    break; /* prevent warning */
    -  case qh_PRINTmerges:
    -    qh_fprintf(fp, 9016, "%d\n", facet->nummerge);
    -    break;
    -  case qh_PRINTnormals:
    -    offset= facet->offset;
    -    goto LABELprintnorm;
    -    break; /* prevent warning */
    -  case qh_PRINTouter:
    -    qh_outerinner(facet, &outerplane, NULL);
    -    offset= facet->offset - outerplane;
    -  LABELprintnorm:
    -    if (!facet->normal) {
    -      qh_fprintf(fp, 9017, "no normal for facet f%d\n", facet->id);
    -      break;
    -    }
    -    if (qh CDDoutput) {
    -      qh_fprintf(fp, 9018, qh_REAL_1, -offset);
    -      for (k=0; k < qh hull_dim; k++)
    -        qh_fprintf(fp, 9019, qh_REAL_1, -facet->normal[k]);
    -    }else {
    -      for (k=0; k < qh hull_dim; k++)
    -        qh_fprintf(fp, 9020, qh_REAL_1, facet->normal[k]);
    -      qh_fprintf(fp, 9021, qh_REAL_1, offset);
    -    }
    -    qh_fprintf(fp, 9022, "\n");
    -    break;
    -  case qh_PRINTmathematica:  /* either 2 or 3-d by qh_printbegin */
    -  case qh_PRINTmaple:
    -    if (qh hull_dim == 2)
    -      qh_printfacet2math(fp, facet, format, qh printoutvar++);
    -    else
    -      qh_printfacet3math(fp, facet, format, qh printoutvar++);
    -    break;
    -  case qh_PRINTneighbors:
    -    qh_fprintf(fp, 9023, "%d", qh_setsize(facet->neighbors));
    -    FOREACHneighbor_(facet)
    -      qh_fprintf(fp, 9024, " %d",
    -               neighbor->visitid ? neighbor->visitid - 1: 0 - neighbor->id);
    -    qh_fprintf(fp, 9025, "\n");
    -    break;
    -  case qh_PRINTpointintersect:
    -    if (!qh feasible_point) {
    -      qh_fprintf(qh ferr, 6067, "qhull input error (qh_printafacet): option 'Fp' needs qh feasible_point\n");
    -      qh_errexit( qh_ERRinput, NULL, NULL);
    -    }
    -    if (facet->offset > 0)
    -      goto LABELprintinfinite;
    -    point= coordp= (coordT*)qh_memalloc(qh normal_size);
    -    normp= facet->normal;
    -    feasiblep= qh feasible_point;
    -    if (facet->offset < -qh MINdenom) {
    -      for (k=qh hull_dim; k--; )
    -        *(coordp++)= (*(normp++) / - facet->offset) + *(feasiblep++);
    -    }else {
    -      for (k=qh hull_dim; k--; ) {
    -        *(coordp++)= qh_divzero(*(normp++), facet->offset, qh MINdenom_1,
    -                                 &zerodiv) + *(feasiblep++);
    -        if (zerodiv) {
    -          qh_memfree(point, qh normal_size);
    -          goto LABELprintinfinite;
    -        }
    -      }
    -    }
    -    qh_printpoint(fp, NULL, point);
    -    qh_memfree(point, qh normal_size);
    -    break;
    -  LABELprintinfinite:
    -    for (k=qh hull_dim; k--; )
    -      qh_fprintf(fp, 9026, qh_REAL_1, qh_INFINITE);
    -    qh_fprintf(fp, 9027, "\n");
    -    break;
    -  case qh_PRINTpointnearest:
    -    FOREACHpoint_(facet->coplanarset) {
    -      int id, id2;
    -      vertex= qh_nearvertex(facet, point, &dist);
    -      id= qh_pointid(vertex->point);
    -      id2= qh_pointid(point);
    -      qh_fprintf(fp, 9028, "%d %d %d " qh_REAL_1 "\n", id, id2, facet->id, dist);
    -    }
    -    break;
    -  case qh_PRINTpoints:  /* VORONOI only by qh_printbegin */
    -    if (qh CDDoutput)
    -      qh_fprintf(fp, 9029, "1 ");
    -    qh_printcenter(fp, format, NULL, facet);
    -    break;
    -  case qh_PRINTvertices:
    -    qh_fprintf(fp, 9030, "%d", qh_setsize(facet->vertices));
    -    FOREACHvertex_(facet->vertices)
    -      qh_fprintf(fp, 9031, " %d", qh_pointid(vertex->point));
    -    qh_fprintf(fp, 9032, "\n");
    -    break;
    -  default:
    -    break;
    -  }
    -} /* printafacet */
    -
    -/*---------------------------------
    -
    -  qh_printbegin(  )
    -    prints header for all output formats
    -
    -  returns:
    -    checks for valid format
    -
    -  notes:
    -    uses qh.visit_id for 3/4off
    -    changes qh.interior_point if printing centrums
    -    qh_countfacets clears facet->visitid for non-good facets
    -
    -  see
    -    qh_printend() and qh_printafacet()
    -
    -  design:
    -    count facets and related statistics
    -    print header for format
    -*/
    -void qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    -  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
    -  int i, num;
    -  facetT *facet, **facetp;
    -  vertexT *vertex, **vertexp;
    -  setT *vertices;
    -  pointT *point, **pointp, *pointtemp;
    -
    -  qh printoutnum= 0;
    -  qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    -      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
    -  switch (format) {
    -  case qh_PRINTnone:
    -    break;
    -  case qh_PRINTarea:
    -    qh_fprintf(fp, 9033, "%d\n", numfacets);
    -    break;
    -  case qh_PRINTcoplanars:
    -    qh_fprintf(fp, 9034, "%d\n", numfacets);
    -    break;
    -  case qh_PRINTcentrums:
    -    if (qh CENTERtype == qh_ASnone)
    -      qh_clearcenters(qh_AScentrum);
    -    qh_fprintf(fp, 9035, "%d\n%d\n", qh hull_dim, numfacets);
    -    break;
    -  case qh_PRINTfacets:
    -  case qh_PRINTfacets_xridge:
    -    if (facetlist)
    -      qh_printvertexlist(fp, "Vertices and facets:\n", facetlist, facets, printall);
    -    break;
    -  case qh_PRINTgeom:
    -    if (qh hull_dim > 4)  /* qh_initqhull_globals also checks */
    -      goto LABELnoformat;
    -    if (qh VORONOI && qh hull_dim > 3)  /* PRINTdim == DROPdim == hull_dim-1 */
    -      goto LABELnoformat;
    -    if (qh hull_dim == 2 && (qh PRINTridges || qh DOintersections))
    -      qh_fprintf(qh ferr, 7049, "qhull warning: output for ridges and intersections not implemented in 2-d\n");
    -    if (qh hull_dim == 4 && (qh PRINTinner || qh PRINTouter ||
    -                             (qh PRINTdim == 4 && qh PRINTcentrums)))
    -      qh_fprintf(qh ferr, 7050, "qhull warning: output for outer/inner planes and centrums not implemented in 4-d\n");
    -    if (qh PRINTdim == 4 && (qh PRINTspheres))
    -      qh_fprintf(qh ferr, 7051, "qhull warning: output for vertices not implemented in 4-d\n");
    -    if (qh PRINTdim == 4 && qh DOintersections && qh PRINTnoplanes)
    -      qh_fprintf(qh ferr, 7052, "qhull warning: 'Gnh' generates no output in 4-d\n");
    -    if (qh PRINTdim == 2) {
    -      qh_fprintf(fp, 9036, "{appearance {linewidth 3} LIST # %s | %s\n",
    -              qh rbox_command, qh qhull_command);
    -    }else if (qh PRINTdim == 3) {
    -      qh_fprintf(fp, 9037, "{appearance {+edge -evert linewidth 2} LIST # %s | %s\n",
    -              qh rbox_command, qh qhull_command);
    -    }else if (qh PRINTdim == 4) {
    -      qh visit_id++;
    -      num= 0;
    -      FORALLfacet_(facetlist)    /* get number of ridges to be printed */
    -        qh_printend4geom(NULL, facet, &num, printall);
    -      FOREACHfacet_(facets)
    -        qh_printend4geom(NULL, facet, &num, printall);
    -      qh ridgeoutnum= num;
    -      qh printoutvar= 0;  /* counts number of ridges in output */
    -      qh_fprintf(fp, 9038, "LIST # %s | %s\n", qh rbox_command, qh qhull_command);
    -    }
    -
    -    if (qh PRINTdots) {
    -      qh printoutnum++;
    -      num= qh num_points + qh_setsize(qh other_points);
    -      if (qh DELAUNAY && qh ATinfinity)
    -        num--;
    -      if (qh PRINTdim == 4)
    -        qh_fprintf(fp, 9039, "4VECT %d %d 1\n", num, num);
    -      else
    -        qh_fprintf(fp, 9040, "VECT %d %d 1\n", num, num);
    -
    -      for (i=num; i--; ) {
    -        if (i % 20 == 0)
    -          qh_fprintf(fp, 9041, "\n");
    -        qh_fprintf(fp, 9042, "1 ");
    -      }
    -      qh_fprintf(fp, 9043, "# 1 point per line\n1 ");
    -      for (i=num-1; i--; ) { /* num at least 3 for D2 */
    -        if (i % 20 == 0)
    -          qh_fprintf(fp, 9044, "\n");
    -        qh_fprintf(fp, 9045, "0 ");
    -      }
    -      qh_fprintf(fp, 9046, "# 1 color for all\n");
    -      FORALLpoints {
    -        if (!qh DELAUNAY || !qh ATinfinity || qh_pointid(point) != qh num_points-1) {
    -          if (qh PRINTdim == 4)
    -            qh_printpoint(fp, NULL, point);
    -            else
    -              qh_printpoint3 (fp, point);
    -        }
    -      }
    -      FOREACHpoint_(qh other_points) {
    -        if (qh PRINTdim == 4)
    -          qh_printpoint(fp, NULL, point);
    -        else
    -          qh_printpoint3 (fp, point);
    -      }
    -      qh_fprintf(fp, 9047, "0 1 1 1  # color of points\n");
    -    }
    -
    -    if (qh PRINTdim == 4  && !qh PRINTnoplanes)
    -      /* 4dview loads up multiple 4OFF objects slowly */
    -      qh_fprintf(fp, 9048, "4OFF %d %d 1\n", 3*qh ridgeoutnum, qh ridgeoutnum);
    -    qh PRINTcradius= 2 * qh DISTround;  /* include test DISTround */
    -    if (qh PREmerge) {
    -      maximize_(qh PRINTcradius, qh premerge_centrum + qh DISTround);
    -    }else if (qh POSTmerge)
    -      maximize_(qh PRINTcradius, qh postmerge_centrum + qh DISTround);
    -    qh PRINTradius= qh PRINTcradius;
    -    if (qh PRINTspheres + qh PRINTcoplanar)
    -      maximize_(qh PRINTradius, qh MAXabs_coord * qh_MINradius);
    -    if (qh premerge_cos < REALmax/2) {
    -      maximize_(qh PRINTradius, (1- qh premerge_cos) * qh MAXabs_coord);
    -    }else if (!qh PREmerge && qh POSTmerge && qh postmerge_cos < REALmax/2) {
    -      maximize_(qh PRINTradius, (1- qh postmerge_cos) * qh MAXabs_coord);
    -    }
    -    maximize_(qh PRINTradius, qh MINvisible);
    -    if (qh JOGGLEmax < REALmax/2)
    -      qh PRINTradius += qh JOGGLEmax * sqrt((realT)qh hull_dim);
    -    if (qh PRINTdim != 4 &&
    -        (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
    -      vertices= qh_facetvertices(facetlist, facets, printall);
    -      if (qh PRINTspheres && qh PRINTdim <= 3)
    -        qh_printspheres(fp, vertices, qh PRINTradius);
    -      if (qh PRINTcoplanar || qh PRINTcentrums) {
    -        qh firstcentrum= True;
    -        if (qh PRINTcoplanar&& !qh PRINTspheres) {
    -          FOREACHvertex_(vertices)
    -            qh_printpointvect2 (fp, vertex->point, NULL, qh interior_point, qh PRINTradius);
    -        }
    -        FORALLfacet_(facetlist) {
    -          if (!printall && qh_skipfacet(facet))
    -            continue;
    -          if (!facet->normal)
    -            continue;
    -          if (qh PRINTcentrums && qh PRINTdim <= 3)
    -            qh_printcentrum(fp, facet, qh PRINTcradius);
    -          if (!qh PRINTcoplanar)
    -            continue;
    -          FOREACHpoint_(facet->coplanarset)
    -            qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
    -          FOREACHpoint_(facet->outsideset)
    -            qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
    -        }
    -        FOREACHfacet_(facets) {
    -          if (!printall && qh_skipfacet(facet))
    -            continue;
    -          if (!facet->normal)
    -            continue;
    -          if (qh PRINTcentrums && qh PRINTdim <= 3)
    -            qh_printcentrum(fp, facet, qh PRINTcradius);
    -          if (!qh PRINTcoplanar)
    -            continue;
    -          FOREACHpoint_(facet->coplanarset)
    -            qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
    -          FOREACHpoint_(facet->outsideset)
    -            qh_printpointvect2 (fp, point, facet->normal, NULL, qh PRINTradius);
    -        }
    -      }
    -      qh_settempfree(&vertices);
    -    }
    -    qh visit_id++; /* for printing hyperplane intersections */
    -    break;
    -  case qh_PRINTids:
    -    qh_fprintf(fp, 9049, "%d\n", numfacets);
    -    break;
    -  case qh_PRINTincidences:
    -    if (qh VORONOI && qh PRINTprecision)
    -      qh_fprintf(qh ferr, 7053, "qhull warning: writing Delaunay.  Use 'p' or 'o' for Voronoi centers\n");
    -    qh printoutvar= qh vertex_id;  /* centrum id for non-simplicial facets */
    -    if (qh hull_dim <= 3)
    -      qh_fprintf(fp, 9050, "%d\n", numfacets);
    -    else
    -      qh_fprintf(fp, 9051, "%d\n", numsimplicial+numridges);
    -    break;
    -  case qh_PRINTinner:
    -  case qh_PRINTnormals:
    -  case qh_PRINTouter:
    -    if (qh CDDoutput)
    -      qh_fprintf(fp, 9052, "%s | %s\nbegin\n    %d %d real\n", qh rbox_command,
    -            qh qhull_command, numfacets, qh hull_dim+1);
    -    else
    -      qh_fprintf(fp, 9053, "%d\n%d\n", qh hull_dim+1, numfacets);
    -    break;
    -  case qh_PRINTmathematica:
    -  case qh_PRINTmaple:
    -    if (qh hull_dim > 3)  /* qh_initbuffers also checks */
    -      goto LABELnoformat;
    -    if (qh VORONOI)
    -      qh_fprintf(qh ferr, 7054, "qhull warning: output is the Delaunay triangulation\n");
    -    if (format == qh_PRINTmaple) {
    -      if (qh hull_dim == 2)
    -        qh_fprintf(fp, 9054, "PLOT(CURVES(\n");
    -      else
    -        qh_fprintf(fp, 9055, "PLOT3D(POLYGONS(\n");
    -    }else
    -      qh_fprintf(fp, 9056, "{\n");
    -    qh printoutvar= 0;   /* counts number of facets for notfirst */
    -    break;
    -  case qh_PRINTmerges:
    -    qh_fprintf(fp, 9057, "%d\n", numfacets);
    -    break;
    -  case qh_PRINTpointintersect:
    -    qh_fprintf(fp, 9058, "%d\n%d\n", qh hull_dim, numfacets);
    -    break;
    -  case qh_PRINTneighbors:
    -    qh_fprintf(fp, 9059, "%d\n", numfacets);
    -    break;
    -  case qh_PRINToff:
    -  case qh_PRINTtriangles:
    -    if (qh VORONOI)
    -      goto LABELnoformat;
    -    num = qh hull_dim;
    -    if (format == qh_PRINToff || qh hull_dim == 2)
    -      qh_fprintf(fp, 9060, "%d\n%d %d %d\n", num,
    -        qh num_points+qh_setsize(qh other_points), numfacets, totneighbors/2);
    -    else { /* qh_PRINTtriangles */
    -      qh printoutvar= qh num_points+qh_setsize(qh other_points); /* first centrum */
    -      if (qh DELAUNAY)
    -        num--;  /* drop last dimension */
    -      qh_fprintf(fp, 9061, "%d\n%d %d %d\n", num, qh printoutvar
    -        + numfacets - numsimplicial, numsimplicial + numridges, totneighbors/2);
    -    }
    -    FORALLpoints
    -      qh_printpointid(qh fout, NULL, num, point, -1);
    -    FOREACHpoint_(qh other_points)
    -      qh_printpointid(qh fout, NULL, num, point, -1);
    -    if (format == qh_PRINTtriangles && qh hull_dim > 2) {
    -      FORALLfacets {
    -        if (!facet->simplicial && facet->visitid)
    -          qh_printcenter(qh fout, format, NULL, facet);
    -      }
    -    }
    -    break;
    -  case qh_PRINTpointnearest:
    -    qh_fprintf(fp, 9062, "%d\n", numcoplanars);
    -    break;
    -  case qh_PRINTpoints:
    -    if (!qh VORONOI)
    -      goto LABELnoformat;
    -    if (qh CDDoutput)
    -      qh_fprintf(fp, 9063, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
    -           qh qhull_command, numfacets, qh hull_dim);
    -    else
    -      qh_fprintf(fp, 9064, "%d\n%d\n", qh hull_dim-1, numfacets);
    -    break;
    -  case qh_PRINTvertices:
    -    qh_fprintf(fp, 9065, "%d\n", numfacets);
    -    break;
    -  case qh_PRINTsummary:
    -  default:
    -  LABELnoformat:
    -    qh_fprintf(qh ferr, 6068, "qhull internal error (qh_printbegin): can not use this format for dimension %d\n",
    -         qh hull_dim);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -} /* printbegin */
    -
    -/*---------------------------------
    -
    -  qh_printcenter( fp, string, facet )
    -    print facet->center as centrum or Voronoi center
    -    string may be NULL.  Don't include '%' codes.
    -    nop if qh CENTERtype neither CENTERvoronoi nor CENTERcentrum
    -    if upper envelope of Delaunay triangulation and point at-infinity
    -      prints qh_INFINITE instead;
    -
    -  notes:
    -    defines facet->center if needed
    -    if format=PRINTgeom, adds a 0 if would otherwise be 2-d
    -    Same as QhullFacet::printCenter
    -*/
    -void qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet) {
    -  int k, num;
    -
    -  if (qh CENTERtype != qh_ASvoronoi && qh CENTERtype != qh_AScentrum)
    -    return;
    -  if (string)
    -    qh_fprintf(fp, 9066, string);
    -  if (qh CENTERtype == qh_ASvoronoi) {
    -    num= qh hull_dim-1;
    -    if (!facet->normal || !facet->upperdelaunay || !qh ATinfinity) {
    -      if (!facet->center)
    -        facet->center= qh_facetcenter(facet->vertices);
    -      for (k=0; k < num; k++)
    -        qh_fprintf(fp, 9067, qh_REAL_1, facet->center[k]);
    -    }else {
    -      for (k=0; k < num; k++)
    -        qh_fprintf(fp, 9068, qh_REAL_1, qh_INFINITE);
    -    }
    -  }else /* qh CENTERtype == qh_AScentrum */ {
    -    num= qh hull_dim;
    -    if (format == qh_PRINTtriangles && qh DELAUNAY)
    -      num--;
    -    if (!facet->center)
    -      facet->center= qh_getcentrum(facet);
    -    for (k=0; k < num; k++)
    -      qh_fprintf(fp, 9069, qh_REAL_1, facet->center[k]);
    -  }
    -  if (format == qh_PRINTgeom && num == 2)
    -    qh_fprintf(fp, 9070, " 0\n");
    -  else
    -    qh_fprintf(fp, 9071, "\n");
    -} /* printcenter */
    -
    -/*---------------------------------
    -
    -  qh_printcentrum( fp, facet, radius )
    -    print centrum for a facet in OOGL format
    -    radius defines size of centrum
    -    2-d or 3-d only
    -
    -  returns:
    -    defines facet->center if needed
    -*/
    -void qh_printcentrum(FILE *fp, facetT *facet, realT radius) {
    -  pointT *centrum, *projpt;
    -  boolT tempcentrum= False;
    -  realT xaxis[4], yaxis[4], normal[4], dist;
    -  realT green[3]={0, 1, 0};
    -  vertexT *apex;
    -  int k;
    -
    -  if (qh CENTERtype == qh_AScentrum) {
    -    if (!facet->center)
    -      facet->center= qh_getcentrum(facet);
    -    centrum= facet->center;
    -  }else {
    -    centrum= qh_getcentrum(facet);
    -    tempcentrum= True;
    -  }
    -  qh_fprintf(fp, 9072, "{appearance {-normal -edge normscale 0} ");
    -  if (qh firstcentrum) {
    -    qh firstcentrum= False;
    -    qh_fprintf(fp, 9073, "{INST geom { define centrum CQUAD  # f%d\n\
    --0.3 -0.3 0.0001     0 0 1 1\n\
    - 0.3 -0.3 0.0001     0 0 1 1\n\
    - 0.3  0.3 0.0001     0 0 1 1\n\
    --0.3  0.3 0.0001     0 0 1 1 } transform { \n", facet->id);
    -  }else
    -    qh_fprintf(fp, 9074, "{INST geom { : centrum } transform { # f%d\n", facet->id);
    -  apex= SETfirstt_(facet->vertices, vertexT);
    -  qh_distplane(apex->point, facet, &dist);
    -  projpt= qh_projectpoint(apex->point, facet, dist);
    -  for (k=qh hull_dim; k--; ) {
    -    xaxis[k]= projpt[k] - centrum[k];
    -    normal[k]= facet->normal[k];
    -  }
    -  if (qh hull_dim == 2) {
    -    xaxis[2]= 0;
    -    normal[2]= 0;
    -  }else if (qh hull_dim == 4) {
    -    qh_projectdim3 (xaxis, xaxis);
    -    qh_projectdim3 (normal, normal);
    -    qh_normalize2 (normal, qh PRINTdim, True, NULL, NULL);
    -  }
    -  qh_crossproduct(3, xaxis, normal, yaxis);
    -  qh_fprintf(fp, 9075, "%8.4g %8.4g %8.4g 0\n", xaxis[0], xaxis[1], xaxis[2]);
    -  qh_fprintf(fp, 9076, "%8.4g %8.4g %8.4g 0\n", yaxis[0], yaxis[1], yaxis[2]);
    -  qh_fprintf(fp, 9077, "%8.4g %8.4g %8.4g 0\n", normal[0], normal[1], normal[2]);
    -  qh_printpoint3 (fp, centrum);
    -  qh_fprintf(fp, 9078, "1 }}}\n");
    -  qh_memfree(projpt, qh normal_size);
    -  qh_printpointvect(fp, centrum, facet->normal, NULL, radius, green);
    -  if (tempcentrum)
    -    qh_memfree(centrum, qh normal_size);
    -} /* printcentrum */
    -
    -/*---------------------------------
    -
    -  qh_printend( fp, format )
    -    prints trailer for all output formats
    -
    -  see:
    -    qh_printbegin() and qh_printafacet()
    -
    -*/
    -void qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    -  int num;
    -  facetT *facet, **facetp;
    -
    -  if (!qh printoutnum)
    -    qh_fprintf(qh ferr, 7055, "qhull warning: no facets printed\n");
    -  switch (format) {
    -  case qh_PRINTgeom:
    -    if (qh hull_dim == 4 && qh DROPdim < 0  && !qh PRINTnoplanes) {
    -      qh visit_id++;
    -      num= 0;
    -      FORALLfacet_(facetlist)
    -        qh_printend4geom(fp, facet,&num, printall);
    -      FOREACHfacet_(facets)
    -        qh_printend4geom(fp, facet, &num, printall);
    -      if (num != qh ridgeoutnum || qh printoutvar != qh ridgeoutnum) {
    -        qh_fprintf(qh ferr, 6069, "qhull internal error (qh_printend): number of ridges %d != number printed %d and at end %d\n", qh ridgeoutnum, qh printoutvar, num);
    -        qh_errexit(qh_ERRqhull, NULL, NULL);
    -      }
    -    }else
    -      qh_fprintf(fp, 9079, "}\n");
    -    break;
    -  case qh_PRINTinner:
    -  case qh_PRINTnormals:
    -  case qh_PRINTouter:
    -    if (qh CDDoutput)
    -      qh_fprintf(fp, 9080, "end\n");
    -    break;
    -  case qh_PRINTmaple:
    -    qh_fprintf(fp, 9081, "));\n");
    -    break;
    -  case qh_PRINTmathematica:
    -    qh_fprintf(fp, 9082, "}\n");
    -    break;
    -  case qh_PRINTpoints:
    -    if (qh CDDoutput)
    -      qh_fprintf(fp, 9083, "end\n");
    -    break;
    -  default:
    -    break;
    -  }
    -} /* printend */
    -
    -/*---------------------------------
    -
    -  qh_printend4geom( fp, facet, numridges, printall )
    -    helper function for qh_printbegin/printend
    -
    -  returns:
    -    number of printed ridges
    -
    -  notes:
    -    just counts printed ridges if fp=NULL
    -    uses facet->visitid
    -    must agree with qh_printfacet4geom...
    -
    -  design:
    -    computes color for facet from its normal
    -    prints each ridge of facet
    -*/
    -void qh_printend4geom(FILE *fp, facetT *facet, int *nump, boolT printall) {
    -  realT color[3];
    -  int i, num= *nump;
    -  facetT *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -
    -  if (!printall && qh_skipfacet(facet))
    -    return;
    -  if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
    -    return;
    -  if (!facet->normal)
    -    return;
    -  if (fp) {
    -    for (i=0; i < 3; i++) {
    -      color[i]= (facet->normal[i]+1.0)/2.0;
    -      maximize_(color[i], -1.0);
    -      minimize_(color[i], +1.0);
    -    }
    -  }
    -  facet->visitid= qh visit_id;
    -  if (facet->simplicial) {
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid != qh visit_id) {
    -        if (fp)
    -          qh_fprintf(fp, 9084, "3 %d %d %d %8.4g %8.4g %8.4g 1 # f%d f%d\n",
    -                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
    -                 facet->id, neighbor->id);
    -        num++;
    -      }
    -    }
    -  }else {
    -    FOREACHridge_(facet->ridges) {
    -      neighbor= otherfacet_(ridge, facet);
    -      if (neighbor->visitid != qh visit_id) {
    -        if (fp)
    -          qh_fprintf(fp, 9085, "3 %d %d %d %8.4g %8.4g %8.4g 1 #r%d f%d f%d\n",
    -                 3*num, 3*num+1, 3*num+2, color[0], color[1], color[2],
    -                 ridge->id, facet->id, neighbor->id);
    -        num++;
    -      }
    -    }
    -  }
    -  *nump= num;
    -} /* printend4geom */
    -
    -/*---------------------------------
    -
    -  qh_printextremes( fp, facetlist, facets, printall )
    -    print extreme points for convex hulls or halfspace intersections
    -
    -  notes:
    -    #points, followed by ids, one per line
    -
    -    sorted by id
    -    same order as qh_printpoints_out if no coplanar/interior points
    -*/
    -void qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
    -  setT *vertices, *points;
    -  pointT *point;
    -  vertexT *vertex, **vertexp;
    -  int id;
    -  int numpoints=0, point_i, point_n;
    -  int allpoints= qh num_points + qh_setsize(qh other_points);
    -
    -  points= qh_settemp(allpoints);
    -  qh_setzero(points, 0, allpoints);
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  FOREACHvertex_(vertices) {
    -    id= qh_pointid(vertex->point);
    -    if (id >= 0) {
    -      SETelem_(points, id)= vertex->point;
    -      numpoints++;
    -    }
    -  }
    -  qh_settempfree(&vertices);
    -  qh_fprintf(fp, 9086, "%d\n", numpoints);
    -  FOREACHpoint_i_(points) {
    -    if (point)
    -      qh_fprintf(fp, 9087, "%d\n", point_i);
    -  }
    -  qh_settempfree(&points);
    -} /* printextremes */
    -
    -/*---------------------------------
    -
    -  qh_printextremes_2d( fp, facetlist, facets, printall )
    -    prints point ids for facets in qh_ORIENTclock order
    -
    -  notes:
    -    #points, followed by ids, one per line
    -    if facetlist/facets are disjoint than the output includes skips
    -    errors if facets form a loop
    -    does not print coplanar points
    -*/
    -void qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
    -  int numfacets, numridges, totneighbors, numcoplanars, numsimplicial, numtricoplanars;
    -  setT *vertices;
    -  facetT *facet, *startfacet, *nextfacet;
    -  vertexT *vertexA, *vertexB;
    -
    -  qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    -      &totneighbors, &numridges, &numcoplanars, &numtricoplanars); /* marks qh visit_id */
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  qh_fprintf(fp, 9088, "%d\n", qh_setsize(vertices));
    -  qh_settempfree(&vertices);
    -  if (!numfacets)
    -    return;
    -  facet= startfacet= facetlist ? facetlist : SETfirstt_(facets, facetT);
    -  qh vertex_visit++;
    -  qh visit_id++;
    -  do {
    -    if (facet->toporient ^ qh_ORIENTclock) {
    -      vertexA= SETfirstt_(facet->vertices, vertexT);
    -      vertexB= SETsecondt_(facet->vertices, vertexT);
    -      nextfacet= SETfirstt_(facet->neighbors, facetT);
    -    }else {
    -      vertexA= SETsecondt_(facet->vertices, vertexT);
    -      vertexB= SETfirstt_(facet->vertices, vertexT);
    -      nextfacet= SETsecondt_(facet->neighbors, facetT);
    -    }
    -    if (facet->visitid == qh visit_id) {
    -      qh_fprintf(qh ferr, 6218, "Qhull internal error (qh_printextremes_2d): loop in facet list.  facet %d nextfacet %d\n",
    -                 facet->id, nextfacet->id);
    -      qh_errexit2 (qh_ERRqhull, facet, nextfacet);
    -    }
    -    if (facet->visitid) {
    -      if (vertexA->visitid != qh vertex_visit) {
    -        vertexA->visitid= qh vertex_visit;
    -        qh_fprintf(fp, 9089, "%d\n", qh_pointid(vertexA->point));
    -      }
    -      if (vertexB->visitid != qh vertex_visit) {
    -        vertexB->visitid= qh vertex_visit;
    -        qh_fprintf(fp, 9090, "%d\n", qh_pointid(vertexB->point));
    -      }
    -    }
    -    facet->visitid= qh visit_id;
    -    facet= nextfacet;
    -  }while (facet && facet != startfacet);
    -} /* printextremes_2d */
    -
    -/*---------------------------------
    -
    -  qh_printextremes_d( fp, facetlist, facets, printall )
    -    print extreme points of input sites for Delaunay triangulations
    -
    -  notes:
    -    #points, followed by ids, one per line
    -
    -    unordered
    -*/
    -void qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
    -  setT *vertices;
    -  vertexT *vertex, **vertexp;
    -  boolT upperseen, lowerseen;
    -  facetT *neighbor, **neighborp;
    -  int numpoints=0;
    -
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  qh_vertexneighbors();
    -  FOREACHvertex_(vertices) {
    -    upperseen= lowerseen= False;
    -    FOREACHneighbor_(vertex) {
    -      if (neighbor->upperdelaunay)
    -        upperseen= True;
    -      else
    -        lowerseen= True;
    -    }
    -    if (upperseen && lowerseen) {
    -      vertex->seen= True;
    -      numpoints++;
    -    }else
    -      vertex->seen= False;
    -  }
    -  qh_fprintf(fp, 9091, "%d\n", numpoints);
    -  FOREACHvertex_(vertices) {
    -    if (vertex->seen)
    -      qh_fprintf(fp, 9092, "%d\n", qh_pointid(vertex->point));
    -  }
    -  qh_settempfree(&vertices);
    -} /* printextremes_d */
    -
    -/*---------------------------------
    -
    -  qh_printfacet( fp, facet )
    -    prints all fields of a facet to fp
    -
    -  notes:
    -    ridges printed in neighbor order
    -*/
    -void qh_printfacet(FILE *fp, facetT *facet) {
    -
    -  qh_printfacetheader(fp, facet);
    -  if (facet->ridges)
    -    qh_printfacetridges(fp, facet);
    -} /* printfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet2geom( fp, facet, color )
    -    print facet as part of a 2-d VECT for Geomview
    -
    -    notes:
    -      assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
    -      mindist is calculated within io.c.  maxoutside is calculated elsewhere
    -      so a DISTround error may have occured.
    -*/
    -void qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]) {
    -  pointT *point0, *point1;
    -  realT mindist, innerplane, outerplane;
    -  int k;
    -
    -  qh_facet2point(facet, &point0, &point1, &mindist);
    -  qh_geomplanes(facet, &outerplane, &innerplane);
    -  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    -    qh_printfacet2geom_points(fp, point0, point1, facet, outerplane, color);
    -  if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
    -                outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
    -    for (k=3; k--; )
    -      color[k]= 1.0 - color[k];
    -    qh_printfacet2geom_points(fp, point0, point1, facet, innerplane, color);
    -  }
    -  qh_memfree(point1, qh normal_size);
    -  qh_memfree(point0, qh normal_size);
    -} /* printfacet2geom */
    -
    -/*---------------------------------
    -
    -  qh_printfacet2geom_points( fp, point1, point2, facet, offset, color )
    -    prints a 2-d facet as a VECT with 2 points at some offset.
    -    The points are on the facet's plane.
    -*/
    -void qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
    -                               facetT *facet, realT offset, realT color[3]) {
    -  pointT *p1= point1, *p2= point2;
    -
    -  qh_fprintf(fp, 9093, "VECT 1 2 1 2 1 # f%d\n", facet->id);
    -  if (offset != 0.0) {
    -    p1= qh_projectpoint(p1, facet, -offset);
    -    p2= qh_projectpoint(p2, facet, -offset);
    -  }
    -  qh_fprintf(fp, 9094, "%8.4g %8.4g %8.4g\n%8.4g %8.4g %8.4g\n",
    -           p1[0], p1[1], 0.0, p2[0], p2[1], 0.0);
    -  if (offset != 0.0) {
    -    qh_memfree(p1, qh normal_size);
    -    qh_memfree(p2, qh normal_size);
    -  }
    -  qh_fprintf(fp, 9095, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
    -} /* printfacet2geom_points */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet2math( fp, facet, format, notfirst )
    -    print 2-d Maple or Mathematica output for a facet
    -    may be non-simplicial
    -
    -  notes:
    -    use %16.8f since Mathematica 2.2 does not handle exponential format
    -    see qh_printfacet3math
    -*/
    -void qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
    -  pointT *point0, *point1;
    -  realT mindist;
    -  const char *pointfmt;
    -
    -  qh_facet2point(facet, &point0, &point1, &mindist);
    -  if (notfirst)
    -    qh_fprintf(fp, 9096, ",");
    -  if (format == qh_PRINTmaple)
    -    pointfmt= "[[%16.8f, %16.8f], [%16.8f, %16.8f]]\n";
    -  else
    -    pointfmt= "Line[{{%16.8f, %16.8f}, {%16.8f, %16.8f}}]\n";
    -  qh_fprintf(fp, 9097, pointfmt, point0[0], point0[1], point1[0], point1[1]);
    -  qh_memfree(point1, qh normal_size);
    -  qh_memfree(point0, qh normal_size);
    -} /* printfacet2math */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet3geom_nonsimplicial( fp, facet, color )
    -    print Geomview OFF for a 3-d nonsimplicial facet.
    -    if DOintersections, prints ridges to unvisited neighbors(qh visit_id)
    -
    -  notes
    -    uses facet->visitid for intersections and ridges
    -*/
    -void qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
    -  ridgeT *ridge, **ridgep;
    -  setT *projectedpoints, *vertices;
    -  vertexT *vertex, **vertexp, *vertexA, *vertexB;
    -  pointT *projpt, *point, **pointp;
    -  facetT *neighbor;
    -  realT dist, outerplane, innerplane;
    -  int cntvertices, k;
    -  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
    -
    -  qh_geomplanes(facet, &outerplane, &innerplane);
    -  vertices= qh_facet3vertex(facet); /* oriented */
    -  cntvertices= qh_setsize(vertices);
    -  projectedpoints= qh_settemp(cntvertices);
    -  FOREACHvertex_(vertices) {
    -    zinc_(Zdistio);
    -    qh_distplane(vertex->point, facet, &dist);
    -    projpt= qh_projectpoint(vertex->point, facet, dist);
    -    qh_setappend(&projectedpoints, projpt);
    -  }
    -  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    -    qh_printfacet3geom_points(fp, projectedpoints, facet, outerplane, color);
    -  if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
    -                outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
    -    for (k=3; k--; )
    -      color[k]= 1.0 - color[k];
    -    qh_printfacet3geom_points(fp, projectedpoints, facet, innerplane, color);
    -  }
    -  FOREACHpoint_(projectedpoints)
    -    qh_memfree(point, qh normal_size);
    -  qh_settempfree(&projectedpoints);
    -  qh_settempfree(&vertices);
    -  if ((qh DOintersections || qh PRINTridges)
    -  && (!facet->visible || !qh NEWfacets)) {
    -    facet->visitid= qh visit_id;
    -    FOREACHridge_(facet->ridges) {
    -      neighbor= otherfacet_(ridge, facet);
    -      if (neighbor->visitid != qh visit_id) {
    -        if (qh DOintersections)
    -          qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, black);
    -        if (qh PRINTridges) {
    -          vertexA= SETfirstt_(ridge->vertices, vertexT);
    -          vertexB= SETsecondt_(ridge->vertices, vertexT);
    -          qh_printline3geom(fp, vertexA->point, vertexB->point, green);
    -        }
    -      }
    -    }
    -  }
    -} /* printfacet3geom_nonsimplicial */
    -
    -/*---------------------------------
    -
    -  qh_printfacet3geom_points( fp, points, facet, offset )
    -    prints a 3-d facet as OFF Geomview object.
    -    offset is relative to the facet's hyperplane
    -    Facet is determined as a list of points
    -*/
    -void qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]) {
    -  int k, n= qh_setsize(points), i;
    -  pointT *point, **pointp;
    -  setT *printpoints;
    -
    -  qh_fprintf(fp, 9098, "{ OFF %d 1 1 # f%d\n", n, facet->id);
    -  if (offset != 0.0) {
    -    printpoints= qh_settemp(n);
    -    FOREACHpoint_(points)
    -      qh_setappend(&printpoints, qh_projectpoint(point, facet, -offset));
    -  }else
    -    printpoints= points;
    -  FOREACHpoint_(printpoints) {
    -    for (k=0; k < qh hull_dim; k++) {
    -      if (k == qh DROPdim)
    -        qh_fprintf(fp, 9099, "0 ");
    -      else
    -        qh_fprintf(fp, 9100, "%8.4g ", point[k]);
    -    }
    -    if (printpoints != points)
    -      qh_memfree(point, qh normal_size);
    -    qh_fprintf(fp, 9101, "\n");
    -  }
    -  if (printpoints != points)
    -    qh_settempfree(&printpoints);
    -  qh_fprintf(fp, 9102, "%d ", n);
    -  for (i=0; i < n; i++)
    -    qh_fprintf(fp, 9103, "%d ", i);
    -  qh_fprintf(fp, 9104, "%8.4g %8.4g %8.4g 1.0 }\n", color[0], color[1], color[2]);
    -} /* printfacet3geom_points */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet3geom_simplicial(  )
    -    print Geomview OFF for a 3-d simplicial facet.
    -
    -  notes:
    -    may flip color
    -    uses facet->visitid for intersections and ridges
    -
    -    assume precise calculations in io.c with roundoff covered by qh_GEOMepsilon
    -    innerplane may be off by qh DISTround.  Maxoutside is calculated elsewhere
    -    so a DISTround error may have occured.
    -*/
    -void qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
    -  setT *points, *vertices;
    -  vertexT *vertex, **vertexp, *vertexA, *vertexB;
    -  facetT *neighbor, **neighborp;
    -  realT outerplane, innerplane;
    -  realT black[3]={0, 0, 0}, green[3]={0, 1, 0};
    -  int k;
    -
    -  qh_geomplanes(facet, &outerplane, &innerplane);
    -  vertices= qh_facet3vertex(facet);
    -  points= qh_settemp(qh TEMPsize);
    -  FOREACHvertex_(vertices)
    -    qh_setappend(&points, vertex->point);
    -  if (qh PRINTouter || (!qh PRINTnoplanes && !qh PRINTinner))
    -    qh_printfacet3geom_points(fp, points, facet, outerplane, color);
    -  if (qh PRINTinner || (!qh PRINTnoplanes && !qh PRINTouter &&
    -              outerplane - innerplane > 2 * qh MAXabs_coord * qh_GEOMepsilon)) {
    -    for (k=3; k--; )
    -      color[k]= 1.0 - color[k];
    -    qh_printfacet3geom_points(fp, points, facet, innerplane, color);
    -  }
    -  qh_settempfree(&points);
    -  qh_settempfree(&vertices);
    -  if ((qh DOintersections || qh PRINTridges)
    -  && (!facet->visible || !qh NEWfacets)) {
    -    facet->visitid= qh visit_id;
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid != qh visit_id) {
    -        vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
    -                          SETindex_(facet->neighbors, neighbor), 0);
    -        if (qh DOintersections)
    -           qh_printhyperplaneintersection(fp, facet, neighbor, vertices, black);
    -        if (qh PRINTridges) {
    -          vertexA= SETfirstt_(vertices, vertexT);
    -          vertexB= SETsecondt_(vertices, vertexT);
    -          qh_printline3geom(fp, vertexA->point, vertexB->point, green);
    -        }
    -        qh_setfree(&vertices);
    -      }
    -    }
    -  }
    -} /* printfacet3geom_simplicial */
    -
    -/*---------------------------------
    -
    -  qh_printfacet3math( fp, facet, notfirst )
    -    print 3-d Maple or Mathematica output for a facet
    -
    -  notes:
    -    may be non-simplicial
    -    use %16.8f since Mathematica 2.2 does not handle exponential format
    -    see qh_printfacet2math
    -*/
    -void qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst) {
    -  vertexT *vertex, **vertexp;
    -  setT *points, *vertices;
    -  pointT *point, **pointp;
    -  boolT firstpoint= True;
    -  realT dist;
    -  const char *pointfmt, *endfmt;
    -
    -  if (notfirst)
    -    qh_fprintf(fp, 9105, ",\n");
    -  vertices= qh_facet3vertex(facet);
    -  points= qh_settemp(qh_setsize(vertices));
    -  FOREACHvertex_(vertices) {
    -    zinc_(Zdistio);
    -    qh_distplane(vertex->point, facet, &dist);
    -    point= qh_projectpoint(vertex->point, facet, dist);
    -    qh_setappend(&points, point);
    -  }
    -  if (format == qh_PRINTmaple) {
    -    qh_fprintf(fp, 9106, "[");
    -    pointfmt= "[%16.8f, %16.8f, %16.8f]";
    -    endfmt= "]";
    -  }else {
    -    qh_fprintf(fp, 9107, "Polygon[{");
    -    pointfmt= "{%16.8f, %16.8f, %16.8f}";
    -    endfmt= "}]";
    -  }
    -  FOREACHpoint_(points) {
    -    if (firstpoint)
    -      firstpoint= False;
    -    else
    -      qh_fprintf(fp, 9108, ",\n");
    -    qh_fprintf(fp, 9109, pointfmt, point[0], point[1], point[2]);
    -  }
    -  FOREACHpoint_(points)
    -    qh_memfree(point, qh normal_size);
    -  qh_settempfree(&points);
    -  qh_settempfree(&vertices);
    -  qh_fprintf(fp, 9110, endfmt);
    -} /* printfacet3math */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet3vertex( fp, facet, format )
    -    print vertices in a 3-d facet as point ids
    -
    -  notes:
    -    prints number of vertices first if format == qh_PRINToff
    -    the facet may be non-simplicial
    -*/
    -void qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format) {
    -  vertexT *vertex, **vertexp;
    -  setT *vertices;
    -
    -  vertices= qh_facet3vertex(facet);
    -  if (format == qh_PRINToff)
    -    qh_fprintf(fp, 9111, "%d ", qh_setsize(vertices));
    -  FOREACHvertex_(vertices)
    -    qh_fprintf(fp, 9112, "%d ", qh_pointid(vertex->point));
    -  qh_fprintf(fp, 9113, "\n");
    -  qh_settempfree(&vertices);
    -} /* printfacet3vertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet4geom_nonsimplicial(  )
    -    print Geomview 4OFF file for a 4d nonsimplicial facet
    -    prints all ridges to unvisited neighbors (qh.visit_id)
    -    if qh.DROPdim
    -      prints in OFF format
    -
    -  notes:
    -    must agree with printend4geom()
    -*/
    -void qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]) {
    -  facetT *neighbor;
    -  ridgeT *ridge, **ridgep;
    -  vertexT *vertex, **vertexp;
    -  pointT *point;
    -  int k;
    -  realT dist;
    -
    -  facet->visitid= qh visit_id;
    -  if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
    -    return;
    -  FOREACHridge_(facet->ridges) {
    -    neighbor= otherfacet_(ridge, facet);
    -    if (neighbor->visitid == qh visit_id)
    -      continue;
    -    if (qh PRINTtransparent && !neighbor->good)
    -      continue;
    -    if (qh DOintersections)
    -      qh_printhyperplaneintersection(fp, facet, neighbor, ridge->vertices, color);
    -    else {
    -      if (qh DROPdim >= 0)
    -        qh_fprintf(fp, 9114, "OFF 3 1 1 # f%d\n", facet->id);
    -      else {
    -        qh printoutvar++;
    -        qh_fprintf(fp, 9115, "# r%d between f%d f%d\n", ridge->id, facet->id, neighbor->id);
    -      }
    -      FOREACHvertex_(ridge->vertices) {
    -        zinc_(Zdistio);
    -        qh_distplane(vertex->point,facet, &dist);
    -        point=qh_projectpoint(vertex->point,facet, dist);
    -        for (k=0; k < qh hull_dim; k++) {
    -          if (k != qh DROPdim)
    -            qh_fprintf(fp, 9116, "%8.4g ", point[k]);
    -        }
    -        qh_fprintf(fp, 9117, "\n");
    -        qh_memfree(point, qh normal_size);
    -      }
    -      if (qh DROPdim >= 0)
    -        qh_fprintf(fp, 9118, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    -    }
    -  }
    -} /* printfacet4geom_nonsimplicial */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacet4geom_simplicial( fp, facet, color )
    -    print Geomview 4OFF file for a 4d simplicial facet
    -    prints triangles for unvisited neighbors (qh.visit_id)
    -
    -  notes:
    -    must agree with printend4geom()
    -*/
    -void qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]) {
    -  setT *vertices;
    -  facetT *neighbor, **neighborp;
    -  vertexT *vertex, **vertexp;
    -  int k;
    -
    -  facet->visitid= qh visit_id;
    -  if (qh PRINTnoplanes || (facet->visible && qh NEWfacets))
    -    return;
    -  FOREACHneighbor_(facet) {
    -    if (neighbor->visitid == qh visit_id)
    -      continue;
    -    if (qh PRINTtransparent && !neighbor->good)
    -      continue;
    -    vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
    -                          SETindex_(facet->neighbors, neighbor), 0);
    -    if (qh DOintersections)
    -      qh_printhyperplaneintersection(fp, facet, neighbor, vertices, color);
    -    else {
    -      if (qh DROPdim >= 0)
    -        qh_fprintf(fp, 9119, "OFF 3 1 1 # ridge between f%d f%d\n",
    -                facet->id, neighbor->id);
    -      else {
    -        qh printoutvar++;
    -        qh_fprintf(fp, 9120, "# ridge between f%d f%d\n", facet->id, neighbor->id);
    -      }
    -      FOREACHvertex_(vertices) {
    -        for (k=0; k < qh hull_dim; k++) {
    -          if (k != qh DROPdim)
    -            qh_fprintf(fp, 9121, "%8.4g ", vertex->point[k]);
    -        }
    -        qh_fprintf(fp, 9122, "\n");
    -      }
    -      if (qh DROPdim >= 0)
    -        qh_fprintf(fp, 9123, "3 0 1 2 %8.4g %8.4g %8.4g\n", color[0], color[1], color[2]);
    -    }
    -    qh_setfree(&vertices);
    -  }
    -} /* printfacet4geom_simplicial */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacetNvertex_nonsimplicial( fp, facet, id, format )
    -    print vertices for an N-d non-simplicial facet
    -    triangulates each ridge to the id
    -*/
    -void qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format) {
    -  vertexT *vertex, **vertexp;
    -  ridgeT *ridge, **ridgep;
    -
    -  if (facet->visible && qh NEWfacets)
    -    return;
    -  FOREACHridge_(facet->ridges) {
    -    if (format == qh_PRINTtriangles)
    -      qh_fprintf(fp, 9124, "%d ", qh hull_dim);
    -    qh_fprintf(fp, 9125, "%d ", id);
    -    if ((ridge->top == facet) ^ qh_ORIENTclock) {
    -      FOREACHvertex_(ridge->vertices)
    -        qh_fprintf(fp, 9126, "%d ", qh_pointid(vertex->point));
    -    }else {
    -      FOREACHvertexreverse12_(ridge->vertices)
    -        qh_fprintf(fp, 9127, "%d ", qh_pointid(vertex->point));
    -    }
    -    qh_fprintf(fp, 9128, "\n");
    -  }
    -} /* printfacetNvertex_nonsimplicial */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacetNvertex_simplicial( fp, facet, format )
    -    print vertices for an N-d simplicial facet
    -    prints vertices for non-simplicial facets
    -      2-d facets (orientation preserved by qh_mergefacet2d)
    -      PRINToff ('o') for 4-d and higher
    -*/
    -void qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format) {
    -  vertexT *vertex, **vertexp;
    -
    -  if (format == qh_PRINToff || format == qh_PRINTtriangles)
    -    qh_fprintf(fp, 9129, "%d ", qh_setsize(facet->vertices));
    -  if ((facet->toporient ^ qh_ORIENTclock)
    -  || (qh hull_dim > 2 && !facet->simplicial)) {
    -    FOREACHvertex_(facet->vertices)
    -      qh_fprintf(fp, 9130, "%d ", qh_pointid(vertex->point));
    -  }else {
    -    FOREACHvertexreverse12_(facet->vertices)
    -      qh_fprintf(fp, 9131, "%d ", qh_pointid(vertex->point));
    -  }
    -  qh_fprintf(fp, 9132, "\n");
    -} /* printfacetNvertex_simplicial */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacetheader( fp, facet )
    -    prints header fields of a facet to fp
    -
    -  notes:
    -    for 'f' output and debugging
    -    Same as QhullFacet::printHeader()
    -*/
    -void qh_printfacetheader(FILE *fp, facetT *facet) {
    -  pointT *point, **pointp, *furthest;
    -  facetT *neighbor, **neighborp;
    -  realT dist;
    -
    -  if (facet == qh_MERGEridge) {
    -    qh_fprintf(fp, 9133, " MERGEridge\n");
    -    return;
    -  }else if (facet == qh_DUPLICATEridge) {
    -    qh_fprintf(fp, 9134, " DUPLICATEridge\n");
    -    return;
    -  }else if (!facet) {
    -    qh_fprintf(fp, 9135, " NULLfacet\n");
    -    return;
    -  }
    -  qh old_randomdist= qh RANDOMdist;
    -  qh RANDOMdist= False;
    -  qh_fprintf(fp, 9136, "- f%d\n", facet->id);
    -  qh_fprintf(fp, 9137, "    - flags:");
    -  if (facet->toporient)
    -    qh_fprintf(fp, 9138, " top");
    -  else
    -    qh_fprintf(fp, 9139, " bottom");
    -  if (facet->simplicial)
    -    qh_fprintf(fp, 9140, " simplicial");
    -  if (facet->tricoplanar)
    -    qh_fprintf(fp, 9141, " tricoplanar");
    -  if (facet->upperdelaunay)
    -    qh_fprintf(fp, 9142, " upperDelaunay");
    -  if (facet->visible)
    -    qh_fprintf(fp, 9143, " visible");
    -  if (facet->newfacet)
    -    qh_fprintf(fp, 9144, " new");
    -  if (facet->tested)
    -    qh_fprintf(fp, 9145, " tested");
    -  if (!facet->good)
    -    qh_fprintf(fp, 9146, " notG");
    -  if (facet->seen)
    -    qh_fprintf(fp, 9147, " seen");
    -  if (facet->coplanar)
    -    qh_fprintf(fp, 9148, " coplanar");
    -  if (facet->mergehorizon)
    -    qh_fprintf(fp, 9149, " mergehorizon");
    -  if (facet->keepcentrum)
    -    qh_fprintf(fp, 9150, " keepcentrum");
    -  if (facet->dupridge)
    -    qh_fprintf(fp, 9151, " dupridge");
    -  if (facet->mergeridge && !facet->mergeridge2)
    -    qh_fprintf(fp, 9152, " mergeridge1");
    -  if (facet->mergeridge2)
    -    qh_fprintf(fp, 9153, " mergeridge2");
    -  if (facet->newmerge)
    -    qh_fprintf(fp, 9154, " newmerge");
    -  if (facet->flipped)
    -    qh_fprintf(fp, 9155, " flipped");
    -  if (facet->notfurthest)
    -    qh_fprintf(fp, 9156, " notfurthest");
    -  if (facet->degenerate)
    -    qh_fprintf(fp, 9157, " degenerate");
    -  if (facet->redundant)
    -    qh_fprintf(fp, 9158, " redundant");
    -  qh_fprintf(fp, 9159, "\n");
    -  if (facet->isarea)
    -    qh_fprintf(fp, 9160, "    - area: %2.2g\n", facet->f.area);
    -  else if (qh NEWfacets && facet->visible && facet->f.replace)
    -    qh_fprintf(fp, 9161, "    - replacement: f%d\n", facet->f.replace->id);
    -  else if (facet->newfacet) {
    -    if (facet->f.samecycle && facet->f.samecycle != facet)
    -      qh_fprintf(fp, 9162, "    - shares same visible/horizon as f%d\n", facet->f.samecycle->id);
    -  }else if (facet->tricoplanar /* !isarea */) {
    -    if (facet->f.triowner)
    -      qh_fprintf(fp, 9163, "    - owner of normal & centrum is facet f%d\n", facet->f.triowner->id);
    -  }else if (facet->f.newcycle)
    -    qh_fprintf(fp, 9164, "    - was horizon to f%d\n", facet->f.newcycle->id);
    -  if (facet->nummerge)
    -    qh_fprintf(fp, 9165, "    - merges: %d\n", facet->nummerge);
    -  qh_printpointid(fp, "    - normal: ", qh hull_dim, facet->normal, -1);
    -  qh_fprintf(fp, 9166, "    - offset: %10.7g\n", facet->offset);
    -  if (qh CENTERtype == qh_ASvoronoi || facet->center)
    -    qh_printcenter(fp, qh_PRINTfacets, "    - center: ", facet);
    -#if qh_MAXoutside
    -  if (facet->maxoutside > qh DISTround)
    -    qh_fprintf(fp, 9167, "    - maxoutside: %10.7g\n", facet->maxoutside);
    -#endif
    -  if (!SETempty_(facet->outsideset)) {
    -    furthest= (pointT*)qh_setlast(facet->outsideset);
    -    if (qh_setsize(facet->outsideset) < 6) {
    -      qh_fprintf(fp, 9168, "    - outside set(furthest p%d):\n", qh_pointid(furthest));
    -      FOREACHpoint_(facet->outsideset)
    -        qh_printpoint(fp, "     ", point);
    -    }else if (qh_setsize(facet->outsideset) < 21) {
    -      qh_printpoints(fp, "    - outside set:", facet->outsideset);
    -    }else {
    -      qh_fprintf(fp, 9169, "    - outside set:  %d points.", qh_setsize(facet->outsideset));
    -      qh_printpoint(fp, "  Furthest", furthest);
    -    }
    -#if !qh_COMPUTEfurthest
    -    qh_fprintf(fp, 9170, "    - furthest distance= %2.2g\n", facet->furthestdist);
    -#endif
    -  }
    -  if (!SETempty_(facet->coplanarset)) {
    -    furthest= (pointT*)qh_setlast(facet->coplanarset);
    -    if (qh_setsize(facet->coplanarset) < 6) {
    -      qh_fprintf(fp, 9171, "    - coplanar set(furthest p%d):\n", qh_pointid(furthest));
    -      FOREACHpoint_(facet->coplanarset)
    -        qh_printpoint(fp, "     ", point);
    -    }else if (qh_setsize(facet->coplanarset) < 21) {
    -      qh_printpoints(fp, "    - coplanar set:", facet->coplanarset);
    -    }else {
    -      qh_fprintf(fp, 9172, "    - coplanar set:  %d points.", qh_setsize(facet->coplanarset));
    -      qh_printpoint(fp, "  Furthest", furthest);
    -    }
    -    zinc_(Zdistio);
    -    qh_distplane(furthest, facet, &dist);
    -    qh_fprintf(fp, 9173, "      furthest distance= %2.2g\n", dist);
    -  }
    -  qh_printvertices(fp, "    - vertices:", facet->vertices);
    -  qh_fprintf(fp, 9174, "    - neighboring facets:");
    -  FOREACHneighbor_(facet) {
    -    if (neighbor == qh_MERGEridge)
    -      qh_fprintf(fp, 9175, " MERGE");
    -    else if (neighbor == qh_DUPLICATEridge)
    -      qh_fprintf(fp, 9176, " DUP");
    -    else
    -      qh_fprintf(fp, 9177, " f%d", neighbor->id);
    -  }
    -  qh_fprintf(fp, 9178, "\n");
    -  qh RANDOMdist= qh old_randomdist;
    -} /* printfacetheader */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacetridges( fp, facet )
    -    prints ridges of a facet to fp
    -
    -  notes:
    -    ridges printed in neighbor order
    -    assumes the ridges exist
    -    for 'f' output
    -    same as QhullFacet::printRidges
    -*/
    -void qh_printfacetridges(FILE *fp, facetT *facet) {
    -  facetT *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -  int numridges= 0;
    -
    -
    -  if (facet->visible && qh NEWfacets) {
    -    qh_fprintf(fp, 9179, "    - ridges(ids may be garbage):");
    -    FOREACHridge_(facet->ridges)
    -      qh_fprintf(fp, 9180, " r%d", ridge->id);
    -    qh_fprintf(fp, 9181, "\n");
    -  }else {
    -    qh_fprintf(fp, 9182, "    - ridges:\n");
    -    FOREACHridge_(facet->ridges)
    -      ridge->seen= False;
    -    if (qh hull_dim == 3) {
    -      ridge= SETfirstt_(facet->ridges, ridgeT);
    -      while (ridge && !ridge->seen) {
    -        ridge->seen= True;
    -        qh_printridge(fp, ridge);
    -        numridges++;
    -        ridge= qh_nextridge3d(ridge, facet, NULL);
    -        }
    -    }else {
    -      FOREACHneighbor_(facet) {
    -        FOREACHridge_(facet->ridges) {
    -          if (otherfacet_(ridge,facet) == neighbor) {
    -            ridge->seen= True;
    -            qh_printridge(fp, ridge);
    -            numridges++;
    -          }
    -        }
    -      }
    -    }
    -    if (numridges != qh_setsize(facet->ridges)) {
    -      qh_fprintf(fp, 9183, "     - all ridges:");
    -      FOREACHridge_(facet->ridges)
    -        qh_fprintf(fp, 9184, " r%d", ridge->id);
    -        qh_fprintf(fp, 9185, "\n");
    -    }
    -    FOREACHridge_(facet->ridges) {
    -      if (!ridge->seen)
    -        qh_printridge(fp, ridge);
    -    }
    -  }
    -} /* printfacetridges */
    -
    -/*---------------------------------
    -
    -  qh_printfacets( fp, format, facetlist, facets, printall )
    -    prints facetlist and/or facet set in output format
    -
    -  notes:
    -    also used for specialized formats ('FO' and summary)
    -    turns off 'Rn' option since want actual numbers
    -*/
    -void qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    -  int numfacets, numsimplicial, numridges, totneighbors, numcoplanars, numtricoplanars;
    -  facetT *facet, **facetp;
    -  setT *vertices;
    -  coordT *center;
    -  realT outerplane, innerplane;
    -
    -  qh old_randomdist= qh RANDOMdist;
    -  qh RANDOMdist= False;
    -  if (qh CDDoutput && (format == qh_PRINTcentrums || format == qh_PRINTpointintersect || format == qh_PRINToff))
    -    qh_fprintf(qh ferr, 7056, "qhull warning: CDD format is not available for centrums, halfspace\nintersections, and OFF file format.\n");
    -  if (format == qh_PRINTnone)
    -    ; /* print nothing */
    -  else if (format == qh_PRINTaverage) {
    -    vertices= qh_facetvertices(facetlist, facets, printall);
    -    center= qh_getcenter(vertices);
    -    qh_fprintf(fp, 9186, "%d 1\n", qh hull_dim);
    -    qh_printpointid(fp, NULL, qh hull_dim, center, -1);
    -    qh_memfree(center, qh normal_size);
    -    qh_settempfree(&vertices);
    -  }else if (format == qh_PRINTextremes) {
    -    if (qh DELAUNAY)
    -      qh_printextremes_d(fp, facetlist, facets, printall);
    -    else if (qh hull_dim == 2)
    -      qh_printextremes_2d(fp, facetlist, facets, printall);
    -    else
    -      qh_printextremes(fp, facetlist, facets, printall);
    -  }else if (format == qh_PRINToptions)
    -    qh_fprintf(fp, 9187, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
    -  else if (format == qh_PRINTpoints && !qh VORONOI)
    -    qh_printpoints_out(fp, facetlist, facets, printall);
    -  else if (format == qh_PRINTqhull)
    -    qh_fprintf(fp, 9188, "%s | %s\n", qh rbox_command, qh qhull_command);
    -  else if (format == qh_PRINTsize) {
    -    qh_fprintf(fp, 9189, "0\n2 ");
    -    qh_fprintf(fp, 9190, qh_REAL_1, qh totarea);
    -    qh_fprintf(fp, 9191, qh_REAL_1, qh totvol);
    -    qh_fprintf(fp, 9192, "\n");
    -  }else if (format == qh_PRINTsummary) {
    -    qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    -      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);
    -    vertices= qh_facetvertices(facetlist, facets, printall);
    -    qh_fprintf(fp, 9193, "10 %d %d %d %d %d %d %d %d %d %d\n2 ", qh hull_dim,
    -                qh num_points + qh_setsize(qh other_points),
    -                qh num_vertices, qh num_facets - qh num_visible,
    -                qh_setsize(vertices), numfacets, numcoplanars,
    -                numfacets - numsimplicial, zzval_(Zdelvertextot),
    -                numtricoplanars);
    -    qh_settempfree(&vertices);
    -    qh_outerinner(NULL, &outerplane, &innerplane);
    -    qh_fprintf(fp, 9194, qh_REAL_2n, outerplane, innerplane);
    -  }else if (format == qh_PRINTvneighbors)
    -    qh_printvneighbors(fp, facetlist, facets, printall);
    -  else if (qh VORONOI && format == qh_PRINToff)
    -    qh_printvoronoi(fp, format, facetlist, facets, printall);
    -  else if (qh VORONOI && format == qh_PRINTgeom) {
    -    qh_printbegin(fp, format, facetlist, facets, printall);
    -    qh_printvoronoi(fp, format, facetlist, facets, printall);
    -    qh_printend(fp, format, facetlist, facets, printall);
    -  }else if (qh VORONOI
    -  && (format == qh_PRINTvertices || format == qh_PRINTinner || format == qh_PRINTouter))
    -    qh_printvdiagram(fp, format, facetlist, facets, printall);
    -  else {
    -    qh_printbegin(fp, format, facetlist, facets, printall);
    -    FORALLfacet_(facetlist)
    -      qh_printafacet(fp, format, facet, printall);
    -    FOREACHfacet_(facets)
    -      qh_printafacet(fp, format, facet, printall);
    -    qh_printend(fp, format, facetlist, facets, printall);
    -  }
    -  qh RANDOMdist= qh old_randomdist;
    -} /* printfacets */
    -
    -
    -/*---------------------------------
    -
    -  qh_printhyperplaneintersection( fp, facet1, facet2, vertices, color )
    -    print Geomview OFF or 4OFF for the intersection of two hyperplanes in 3-d or 4-d
    -*/
    -void qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
    -                   setT *vertices, realT color[3]) {
    -  realT costheta, denominator, dist1, dist2, s, t, mindenom, p[4];
    -  vertexT *vertex, **vertexp;
    -  int i, k;
    -  boolT nearzero1, nearzero2;
    -
    -  costheta= qh_getangle(facet1->normal, facet2->normal);
    -  denominator= 1 - costheta * costheta;
    -  i= qh_setsize(vertices);
    -  if (qh hull_dim == 3)
    -    qh_fprintf(fp, 9195, "VECT 1 %d 1 %d 1 ", i, i);
    -  else if (qh hull_dim == 4 && qh DROPdim >= 0)
    -    qh_fprintf(fp, 9196, "OFF 3 1 1 ");
    -  else
    -    qh printoutvar++;
    -  qh_fprintf(fp, 9197, "# intersect f%d f%d\n", facet1->id, facet2->id);
    -  mindenom= 1 / (10.0 * qh MAXabs_coord);
    -  FOREACHvertex_(vertices) {
    -    zadd_(Zdistio, 2);
    -    qh_distplane(vertex->point, facet1, &dist1);
    -    qh_distplane(vertex->point, facet2, &dist2);
    -    s= qh_divzero(-dist1 + costheta * dist2, denominator,mindenom,&nearzero1);
    -    t= qh_divzero(-dist2 + costheta * dist1, denominator,mindenom,&nearzero2);
    -    if (nearzero1 || nearzero2)
    -      s= t= 0.0;
    -    for (k=qh hull_dim; k--; )
    -      p[k]= vertex->point[k] + facet1->normal[k] * s + facet2->normal[k] * t;
    -    if (qh PRINTdim <= 3) {
    -      qh_projectdim3 (p, p);
    -      qh_fprintf(fp, 9198, "%8.4g %8.4g %8.4g # ", p[0], p[1], p[2]);
    -    }else
    -      qh_fprintf(fp, 9199, "%8.4g %8.4g %8.4g %8.4g # ", p[0], p[1], p[2], p[3]);
    -    if (nearzero1+nearzero2)
    -      qh_fprintf(fp, 9200, "p%d(coplanar facets)\n", qh_pointid(vertex->point));
    -    else
    -      qh_fprintf(fp, 9201, "projected p%d\n", qh_pointid(vertex->point));
    -  }
    -  if (qh hull_dim == 3)
    -    qh_fprintf(fp, 9202, "%8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
    -  else if (qh hull_dim == 4 && qh DROPdim >= 0)
    -    qh_fprintf(fp, 9203, "3 0 1 2 %8.4g %8.4g %8.4g 1.0\n", color[0], color[1], color[2]);
    -} /* printhyperplaneintersection */
    -
    -/*---------------------------------
    -
    -  qh_printline3geom( fp, pointA, pointB, color )
    -    prints a line as a VECT
    -    prints 0's for qh.DROPdim
    -
    -  notes:
    -    if pointA == pointB,
    -      it's a 1 point VECT
    -*/
    -void qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]) {
    -  int k;
    -  realT pA[4], pB[4];
    -
    -  qh_projectdim3(pointA, pA);
    -  qh_projectdim3(pointB, pB);
    -  if ((fabs(pA[0] - pB[0]) > 1e-3) ||
    -      (fabs(pA[1] - pB[1]) > 1e-3) ||
    -      (fabs(pA[2] - pB[2]) > 1e-3)) {
    -    qh_fprintf(fp, 9204, "VECT 1 2 1 2 1\n");
    -    for (k=0; k < 3; k++)
    -       qh_fprintf(fp, 9205, "%8.4g ", pB[k]);
    -    qh_fprintf(fp, 9206, " # p%d\n", qh_pointid(pointB));
    -  }else
    -    qh_fprintf(fp, 9207, "VECT 1 1 1 1 1\n");
    -  for (k=0; k < 3; k++)
    -    qh_fprintf(fp, 9208, "%8.4g ", pA[k]);
    -  qh_fprintf(fp, 9209, " # p%d\n", qh_pointid(pointA));
    -  qh_fprintf(fp, 9210, "%8.4g %8.4g %8.4g 1\n", color[0], color[1], color[2]);
    -}
    -
    -/*---------------------------------
    -
    -  qh_printneighborhood( fp, format, facetA, facetB, printall )
    -    print neighborhood of one or two facets
    -
    -  notes:
    -    calls qh_findgood_all()
    -    bumps qh.visit_id
    -*/
    -void qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall) {
    -  facetT *neighbor, **neighborp, *facet;
    -  setT *facets;
    -
    -  if (format == qh_PRINTnone)
    -    return;
    -  qh_findgood_all(qh facet_list);
    -  if (facetA == facetB)
    -    facetB= NULL;
    -  facets= qh_settemp(2*(qh_setsize(facetA->neighbors)+1));
    -  qh visit_id++;
    -  for (facet= facetA; facet; facet= ((facet == facetA) ? facetB : NULL)) {
    -    if (facet->visitid != qh visit_id) {
    -      facet->visitid= qh visit_id;
    -      qh_setappend(&facets, facet);
    -    }
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid == qh visit_id)
    -        continue;
    -      neighbor->visitid= qh visit_id;
    -      if (printall || !qh_skipfacet(neighbor))
    -        qh_setappend(&facets, neighbor);
    -    }
    -  }
    -  qh_printfacets(fp, format, NULL, facets, printall);
    -  qh_settempfree(&facets);
    -} /* printneighborhood */
    -
    -/*---------------------------------
    -
    -  qh_printpoint( fp, string, point )
    -  qh_printpointid( fp, string, dim, point, id )
    -    prints the coordinates of a point
    -
    -  returns:
    -    if string is defined
    -      prints 'string p%d' (skips p%d if id=-1)
    -
    -  notes:
    -    nop if point is NULL
    -    prints id unless it is undefined (-1)
    -    Same as QhullPoint's printPoint
    -*/
    -void qh_printpoint(FILE *fp, const char *string, pointT *point) {
    -  int id= qh_pointid( point);
    -
    -  qh_printpointid( fp, string, qh hull_dim, point, id);
    -} /* printpoint */
    -
    -void qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id) {
    -  int k;
    -  realT r; /*bug fix*/
    -
    -  if (!point)
    -    return;
    -  if (string) {
    -    qh_fprintf(fp, 9211, "%s", string);
    -   if (id != -1)
    -      qh_fprintf(fp, 9212, " p%d: ", id);
    -  }
    -  for (k=dim; k--; ) {
    -    r= *point++;
    -    if (string)
    -      qh_fprintf(fp, 9213, " %8.4g", r);
    -    else
    -      qh_fprintf(fp, 9214, qh_REAL_1, r);
    -  }
    -  qh_fprintf(fp, 9215, "\n");
    -} /* printpointid */
    -
    -/*---------------------------------
    -
    -  qh_printpoint3( fp, point )
    -    prints 2-d, 3-d, or 4-d point as Geomview 3-d coordinates
    -*/
    -void qh_printpoint3 (FILE *fp, pointT *point) {
    -  int k;
    -  realT p[4];
    -
    -  qh_projectdim3 (point, p);
    -  for (k=0; k < 3; k++)
    -    qh_fprintf(fp, 9216, "%8.4g ", p[k]);
    -  qh_fprintf(fp, 9217, " # p%d\n", qh_pointid(point));
    -} /* printpoint3 */
    -
    -/*----------------------------------------
    --printpoints- print pointids for a set of points starting at index
    -   see geom.c
    -*/
    -
    -/*---------------------------------
    -
    -  qh_printpoints_out( fp, facetlist, facets, printall )
    -    prints vertices, coplanar/inside points, for facets by their point coordinates
    -    allows qh.CDDoutput
    -
    -  notes:
    -    same format as qhull input
    -    if no coplanar/interior points,
    -      same order as qh_printextremes
    -*/
    -void qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall) {
    -  int allpoints= qh num_points + qh_setsize(qh other_points);
    -  int numpoints=0, point_i, point_n;
    -  setT *vertices, *points;
    -  facetT *facet, **facetp;
    -  pointT *point, **pointp;
    -  vertexT *vertex, **vertexp;
    -  int id;
    -
    -  points= qh_settemp(allpoints);
    -  qh_setzero(points, 0, allpoints);
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  FOREACHvertex_(vertices) {
    -    id= qh_pointid(vertex->point);
    -    if (id >= 0)
    -      SETelem_(points, id)= vertex->point;
    -  }
    -  if (qh KEEPinside || qh KEEPcoplanar || qh KEEPnearinside) {
    -    FORALLfacet_(facetlist) {
    -      if (!printall && qh_skipfacet(facet))
    -        continue;
    -      FOREACHpoint_(facet->coplanarset) {
    -        id= qh_pointid(point);
    -        if (id >= 0)
    -          SETelem_(points, id)= point;
    -      }
    -    }
    -    FOREACHfacet_(facets) {
    -      if (!printall && qh_skipfacet(facet))
    -        continue;
    -      FOREACHpoint_(facet->coplanarset) {
    -        id= qh_pointid(point);
    -        if (id >= 0)
    -          SETelem_(points, id)= point;
    -      }
    -    }
    -  }
    -  qh_settempfree(&vertices);
    -  FOREACHpoint_i_(points) {
    -    if (point)
    -      numpoints++;
    -  }
    -  if (qh CDDoutput)
    -    qh_fprintf(fp, 9218, "%s | %s\nbegin\n%d %d real\n", qh rbox_command,
    -             qh qhull_command, numpoints, qh hull_dim + 1);
    -  else
    -    qh_fprintf(fp, 9219, "%d\n%d\n", qh hull_dim, numpoints);
    -  FOREACHpoint_i_(points) {
    -    if (point) {
    -      if (qh CDDoutput)
    -        qh_fprintf(fp, 9220, "1 ");
    -      qh_printpoint(fp, NULL, point);
    -    }
    -  }
    -  if (qh CDDoutput)
    -    qh_fprintf(fp, 9221, "end\n");
    -  qh_settempfree(&points);
    -} /* printpoints_out */
    -
    -
    -/*---------------------------------
    -
    -  qh_printpointvect( fp, point, normal, center, radius, color )
    -    prints a 2-d, 3-d, or 4-d point as 3-d VECT's relative to normal or to center point
    -*/
    -void qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]) {
    -  realT diff[4], pointA[4];
    -  int k;
    -
    -  for (k=qh hull_dim; k--; ) {
    -    if (center)
    -      diff[k]= point[k]-center[k];
    -    else if (normal)
    -      diff[k]= normal[k];
    -    else
    -      diff[k]= 0;
    -  }
    -  if (center)
    -    qh_normalize2 (diff, qh hull_dim, True, NULL, NULL);
    -  for (k=qh hull_dim; k--; )
    -    pointA[k]= point[k]+diff[k] * radius;
    -  qh_printline3geom(fp, point, pointA, color);
    -} /* printpointvect */
    -
    -/*---------------------------------
    -
    -  qh_printpointvect2( fp, point, normal, center, radius )
    -    prints a 2-d, 3-d, or 4-d point as 2 3-d VECT's for an imprecise point
    -*/
    -void qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius) {
    -  realT red[3]={1, 0, 0}, yellow[3]={1, 1, 0};
    -
    -  qh_printpointvect(fp, point, normal, center, radius, red);
    -  qh_printpointvect(fp, point, normal, center, -radius, yellow);
    -} /* printpointvect2 */
    -
    -/*---------------------------------
    -
    -  qh_printridge( fp, ridge )
    -    prints the information in a ridge
    -
    -  notes:
    -    for qh_printfacetridges()
    -    same as operator<< [QhullRidge.cpp]
    -*/
    -void qh_printridge(FILE *fp, ridgeT *ridge) {
    -
    -  qh_fprintf(fp, 9222, "     - r%d", ridge->id);
    -  if (ridge->tested)
    -    qh_fprintf(fp, 9223, " tested");
    -  if (ridge->nonconvex)
    -    qh_fprintf(fp, 9224, " nonconvex");
    -  qh_fprintf(fp, 9225, "\n");
    -  qh_printvertices(fp, "           vertices:", ridge->vertices);
    -  if (ridge->top && ridge->bottom)
    -    qh_fprintf(fp, 9226, "           between f%d and f%d\n",
    -            ridge->top->id, ridge->bottom->id);
    -} /* printridge */
    -
    -/*---------------------------------
    -
    -  qh_printspheres( fp, vertices, radius )
    -    prints 3-d vertices as OFF spheres
    -
    -  notes:
    -    inflated octahedron from Stuart Levy earth/mksphere2
    -*/
    -void qh_printspheres(FILE *fp, setT *vertices, realT radius) {
    -  vertexT *vertex, **vertexp;
    -
    -  qh printoutnum++;
    -  qh_fprintf(fp, 9227, "{appearance {-edge -normal normscale 0} {\n\
    -INST geom {define vsphere OFF\n\
    -18 32 48\n\
    -\n\
    -0 0 1\n\
    -1 0 0\n\
    -0 1 0\n\
    --1 0 0\n\
    -0 -1 0\n\
    -0 0 -1\n\
    -0.707107 0 0.707107\n\
    -0 -0.707107 0.707107\n\
    -0.707107 -0.707107 0\n\
    --0.707107 0 0.707107\n\
    --0.707107 -0.707107 0\n\
    -0 0.707107 0.707107\n\
    --0.707107 0.707107 0\n\
    -0.707107 0.707107 0\n\
    -0.707107 0 -0.707107\n\
    -0 0.707107 -0.707107\n\
    --0.707107 0 -0.707107\n\
    -0 -0.707107 -0.707107\n\
    -\n\
    -3 0 6 11\n\
    -3 0 7 6 \n\
    -3 0 9 7 \n\
    -3 0 11 9\n\
    -3 1 6 8 \n\
    -3 1 8 14\n\
    -3 1 13 6\n\
    -3 1 14 13\n\
    -3 2 11 13\n\
    -3 2 12 11\n\
    -3 2 13 15\n\
    -3 2 15 12\n\
    -3 3 9 12\n\
    -3 3 10 9\n\
    -3 3 12 16\n\
    -3 3 16 10\n\
    -3 4 7 10\n\
    -3 4 8 7\n\
    -3 4 10 17\n\
    -3 4 17 8\n\
    -3 5 14 17\n\
    -3 5 15 14\n\
    -3 5 16 15\n\
    -3 5 17 16\n\
    -3 6 13 11\n\
    -3 7 8 6\n\
    -3 9 10 7\n\
    -3 11 12 9\n\
    -3 14 8 17\n\
    -3 15 13 14\n\
    -3 16 12 15\n\
    -3 17 10 16\n} transforms { TLIST\n");
    -  FOREACHvertex_(vertices) {
    -    qh_fprintf(fp, 9228, "%8.4g 0 0 0 # v%d\n 0 %8.4g 0 0\n0 0 %8.4g 0\n",
    -      radius, vertex->id, radius, radius);
    -    qh_printpoint3 (fp, vertex->point);
    -    qh_fprintf(fp, 9229, "1\n");
    -  }
    -  qh_fprintf(fp, 9230, "}}}\n");
    -} /* printspheres */
    -
    -
    -/*----------------------------------------------
    --printsummary-
    -                see libqhull.c
    -*/
    -
    -/*---------------------------------
    -
    -  qh_printvdiagram( fp, format, facetlist, facets, printall )
    -    print voronoi diagram
    -      # of pairs of input sites
    -      #indices site1 site2 vertex1 ...
    -
    -    sites indexed by input point id
    -      point 0 is the first input point
    -    vertices indexed by 'o' and 'p' order
    -      vertex 0 is the 'vertex-at-infinity'
    -      vertex 1 is the first Voronoi vertex
    -
    -  see:
    -    qh_printvoronoi()
    -    qh_eachvoronoi_all()
    -
    -  notes:
    -    if all facets are upperdelaunay,
    -      prints upper hull (furthest-site Voronoi diagram)
    -*/
    -void qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    -  setT *vertices;
    -  int totcount, numcenters;
    -  boolT isLower;
    -  qh_RIDGE innerouter= qh_RIDGEall;
    -  printvridgeT printvridge= NULL;
    -
    -  if (format == qh_PRINTvertices) {
    -    innerouter= qh_RIDGEall;
    -    printvridge= qh_printvridge;
    -  }else if (format == qh_PRINTinner) {
    -    innerouter= qh_RIDGEinner;
    -    printvridge= qh_printvnorm;
    -  }else if (format == qh_PRINTouter) {
    -    innerouter= qh_RIDGEouter;
    -    printvridge= qh_printvnorm;
    -  }else {
    -    qh_fprintf(qh ferr, 6219, "Qhull internal error (qh_printvdiagram): unknown print format %d.\n", format);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
    -  totcount= qh_printvdiagram2 (NULL, NULL, vertices, innerouter, False);
    -  qh_fprintf(fp, 9231, "%d\n", totcount);
    -  totcount= qh_printvdiagram2 (fp, printvridge, vertices, innerouter, True /* inorder*/);
    -  qh_settempfree(&vertices);
    -#if 0  /* for testing qh_eachvoronoi_all */
    -  qh_fprintf(fp, 9232, "\n");
    -  totcount= qh_eachvoronoi_all(fp, printvridge, qh UPPERdelaunay, innerouter, True /* inorder*/);
    -  qh_fprintf(fp, 9233, "%d\n", totcount);
    -#endif
    -} /* printvdiagram */
    -
    -/*---------------------------------
    -
    -  qh_printvdiagram2( fp, printvridge, vertices, innerouter, inorder )
    -    visit all pairs of input sites (vertices) for selected Voronoi vertices
    -    vertices may include NULLs
    -
    -  innerouter:
    -    qh_RIDGEall   print inner ridges(bounded) and outer ridges(unbounded)
    -    qh_RIDGEinner print only inner ridges
    -    qh_RIDGEouter print only outer ridges
    -
    -  inorder:
    -    print 3-d Voronoi vertices in order
    -
    -  assumes:
    -    qh_markvoronoi marked facet->visitid for Voronoi vertices
    -    all facet->seen= False
    -    all facet->seen2= True
    -
    -  returns:
    -    total number of Voronoi ridges
    -    if printvridge,
    -      calls printvridge( fp, vertex, vertexA, centers) for each ridge
    -      [see qh_eachvoronoi()]
    -
    -  see:
    -    qh_eachvoronoi_all()
    -*/
    -int qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder) {
    -  int totcount= 0;
    -  int vertex_i, vertex_n;
    -  vertexT *vertex;
    -
    -  FORALLvertices
    -    vertex->seen= False;
    -  FOREACHvertex_i_(vertices) {
    -    if (vertex) {
    -      if (qh GOODvertex > 0 && qh_pointid(vertex->point)+1 != qh GOODvertex)
    -        continue;
    -      totcount += qh_eachvoronoi(fp, printvridge, vertex, !qh_ALL, innerouter, inorder);
    -    }
    -  }
    -  return totcount;
    -} /* printvdiagram2 */
    -
    -/*---------------------------------
    -
    -  qh_printvertex( fp, vertex )
    -    prints the information in a vertex
    -    Duplicated as operator<< [QhullVertex.cpp]
    -*/
    -void qh_printvertex(FILE *fp, vertexT *vertex) {
    -  pointT *point;
    -  int k, count= 0;
    -  facetT *neighbor, **neighborp;
    -  realT r; /*bug fix*/
    -
    -  if (!vertex) {
    -    qh_fprintf(fp, 9234, "  NULLvertex\n");
    -    return;
    -  }
    -  qh_fprintf(fp, 9235, "- p%d(v%d):", qh_pointid(vertex->point), vertex->id);
    -  point= vertex->point;
    -  if (point) {
    -    for (k=qh hull_dim; k--; ) {
    -      r= *point++;
    -      qh_fprintf(fp, 9236, " %5.2g", r);
    -    }
    -  }
    -  if (vertex->deleted)
    -    qh_fprintf(fp, 9237, " deleted");
    -  if (vertex->delridge)
    -    qh_fprintf(fp, 9238, " ridgedeleted");
    -  qh_fprintf(fp, 9239, "\n");
    -  if (vertex->neighbors) {
    -    qh_fprintf(fp, 9240, "  neighbors:");
    -    FOREACHneighbor_(vertex) {
    -      if (++count % 100 == 0)
    -        qh_fprintf(fp, 9241, "\n     ");
    -      qh_fprintf(fp, 9242, " f%d", neighbor->id);
    -    }
    -    qh_fprintf(fp, 9243, "\n");
    -  }
    -} /* printvertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_printvertexlist( fp, string, facetlist, facets, printall )
    -    prints vertices used by a facetlist or facet set
    -    tests qh_skipfacet() if !printall
    -*/
    -void qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
    -                         setT *facets, boolT printall) {
    -  vertexT *vertex, **vertexp;
    -  setT *vertices;
    -
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  qh_fprintf(fp, 9244, "%s", string);
    -  FOREACHvertex_(vertices)
    -    qh_printvertex(fp, vertex);
    -  qh_settempfree(&vertices);
    -} /* printvertexlist */
    -
    -
    -/*---------------------------------
    -
    -  qh_printvertices( fp, string, vertices )
    -    prints vertices in a set
    -    duplicated as printVertexSet [QhullVertex.cpp]
    -*/
    -void qh_printvertices(FILE *fp, const char* string, setT *vertices) {
    -  vertexT *vertex, **vertexp;
    -
    -  qh_fprintf(fp, 9245, "%s", string);
    -  FOREACHvertex_(vertices)
    -    qh_fprintf(fp, 9246, " p%d(v%d)", qh_pointid(vertex->point), vertex->id);
    -  qh_fprintf(fp, 9247, "\n");
    -} /* printvertices */
    -
    -/*---------------------------------
    -
    -  qh_printvneighbors( fp, facetlist, facets, printall )
    -    print vertex neighbors of vertices in facetlist and facets ('FN')
    -
    -  notes:
    -    qh_countfacets clears facet->visitid for non-printed facets
    -
    -  design:
    -    collect facet count and related statistics
    -    if necessary, build neighbor sets for each vertex
    -    collect vertices in facetlist and facets
    -    build a point array for point->vertex and point->coplanar facet
    -    for each point
    -      list vertex neighbors or coplanar facet
    -*/
    -void qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall) {
    -  int numfacets, numsimplicial, numridges, totneighbors, numneighbors, numcoplanars, numtricoplanars;
    -  setT *vertices, *vertex_points, *coplanar_points;
    -  int numpoints= qh num_points + qh_setsize(qh other_points);
    -  vertexT *vertex, **vertexp;
    -  int vertex_i, vertex_n;
    -  facetT *facet, **facetp, *neighbor, **neighborp;
    -  pointT *point, **pointp;
    -
    -  qh_countfacets(facetlist, facets, printall, &numfacets, &numsimplicial,
    -      &totneighbors, &numridges, &numcoplanars, &numtricoplanars);  /* sets facet->visitid */
    -  qh_fprintf(fp, 9248, "%d\n", numpoints);
    -  qh_vertexneighbors();
    -  vertices= qh_facetvertices(facetlist, facets, printall);
    -  vertex_points= qh_settemp(numpoints);
    -  coplanar_points= qh_settemp(numpoints);
    -  qh_setzero(vertex_points, 0, numpoints);
    -  qh_setzero(coplanar_points, 0, numpoints);
    -  FOREACHvertex_(vertices)
    -    qh_point_add(vertex_points, vertex->point, vertex);
    -  FORALLfacet_(facetlist) {
    -    FOREACHpoint_(facet->coplanarset)
    -      qh_point_add(coplanar_points, point, facet);
    -  }
    -  FOREACHfacet_(facets) {
    -    FOREACHpoint_(facet->coplanarset)
    -      qh_point_add(coplanar_points, point, facet);
    -  }
    -  FOREACHvertex_i_(vertex_points) {
    -    if (vertex) {
    -      numneighbors= qh_setsize(vertex->neighbors);
    -      qh_fprintf(fp, 9249, "%d", numneighbors);
    -      if (qh hull_dim == 3)
    -        qh_order_vertexneighbors(vertex);
    -      else if (qh hull_dim >= 4)
    -        qsort(SETaddr_(vertex->neighbors, facetT), (size_t)numneighbors,
    -             sizeof(facetT *), qh_compare_facetvisit);
    -      FOREACHneighbor_(vertex)
    -        qh_fprintf(fp, 9250, " %d",
    -                 neighbor->visitid ? neighbor->visitid - 1 : 0 - neighbor->id);
    -      qh_fprintf(fp, 9251, "\n");
    -    }else if ((facet= SETelemt_(coplanar_points, vertex_i, facetT)))
    -      qh_fprintf(fp, 9252, "1 %d\n",
    -                  facet->visitid ? facet->visitid - 1 : 0 - facet->id);
    -    else
    -      qh_fprintf(fp, 9253, "0\n");
    -  }
    -  qh_settempfree(&coplanar_points);
    -  qh_settempfree(&vertex_points);
    -  qh_settempfree(&vertices);
    -} /* printvneighbors */
    -
    -/*---------------------------------
    -
    -  qh_printvoronoi( fp, format, facetlist, facets, printall )
    -    print voronoi diagram in 'o' or 'G' format
    -    for 'o' format
    -      prints voronoi centers for each facet and for infinity
    -      for each vertex, lists ids of printed facets or infinity
    -      assumes facetlist and facets are disjoint
    -    for 'G' format
    -      prints an OFF object
    -      adds a 0 coordinate to center
    -      prints infinity but does not list in vertices
    -
    -  see:
    -    qh_printvdiagram()
    -
    -  notes:
    -    if 'o',
    -      prints a line for each point except "at-infinity"
    -    if all facets are upperdelaunay,
    -      reverses lower and upper hull
    -*/
    -void qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall) {
    -  int k, numcenters, numvertices= 0, numneighbors, numinf, vid=1, vertex_i, vertex_n;
    -  facetT *facet, **facetp, *neighbor, **neighborp;
    -  setT *vertices;
    -  vertexT *vertex;
    -  boolT isLower;
    -  unsigned int numfacets= (unsigned int) qh num_facets;
    -
    -  vertices= qh_markvoronoi(facetlist, facets, printall, &isLower, &numcenters);
    -  FOREACHvertex_i_(vertices) {
    -    if (vertex) {
    -      numvertices++;
    -      numneighbors = numinf = 0;
    -      FOREACHneighbor_(vertex) {
    -        if (neighbor->visitid == 0)
    -          numinf= 1;
    -        else if (neighbor->visitid < numfacets)
    -          numneighbors++;
    -      }
    -      if (numinf && !numneighbors) {
    -        SETelem_(vertices, vertex_i)= NULL;
    -        numvertices--;
    -      }
    -    }
    -  }
    -  if (format == qh_PRINTgeom)
    -    qh_fprintf(fp, 9254, "{appearance {+edge -face} OFF %d %d 1 # Voronoi centers and cells\n",
    -                numcenters, numvertices);
    -  else
    -    qh_fprintf(fp, 9255, "%d\n%d %d 1\n", qh hull_dim-1, numcenters, qh_setsize(vertices));
    -  if (format == qh_PRINTgeom) {
    -    for (k=qh hull_dim-1; k--; )
    -      qh_fprintf(fp, 9256, qh_REAL_1, 0.0);
    -    qh_fprintf(fp, 9257, " 0 # infinity not used\n");
    -  }else {
    -    for (k=qh hull_dim-1; k--; )
    -      qh_fprintf(fp, 9258, qh_REAL_1, qh_INFINITE);
    -    qh_fprintf(fp, 9259, "\n");
    -  }
    -  FORALLfacet_(facetlist) {
    -    if (facet->visitid && facet->visitid < numfacets) {
    -      if (format == qh_PRINTgeom)
    -        qh_fprintf(fp, 9260, "# %d f%d\n", vid++, facet->id);
    -      qh_printcenter(fp, format, NULL, facet);
    -    }
    -  }
    -  FOREACHfacet_(facets) {
    -    if (facet->visitid && facet->visitid < numfacets) {
    -      if (format == qh_PRINTgeom)
    -        qh_fprintf(fp, 9261, "# %d f%d\n", vid++, facet->id);
    -      qh_printcenter(fp, format, NULL, facet);
    -    }
    -  }
    -  FOREACHvertex_i_(vertices) {
    -    numneighbors= 0;
    -    numinf=0;
    -    if (vertex) {
    -      if (qh hull_dim == 3)
    -        qh_order_vertexneighbors(vertex);
    -      else if (qh hull_dim >= 4)
    -        qsort(SETaddr_(vertex->neighbors, facetT),
    -             (size_t)qh_setsize(vertex->neighbors),
    -             sizeof(facetT *), qh_compare_facetvisit);
    -      FOREACHneighbor_(vertex) {
    -        if (neighbor->visitid == 0)
    -          numinf= 1;
    -        else if (neighbor->visitid < numfacets)
    -          numneighbors++;
    -      }
    -    }
    -    if (format == qh_PRINTgeom) {
    -      if (vertex) {
    -        qh_fprintf(fp, 9262, "%d", numneighbors);
    -        FOREACHneighbor_(vertex) {
    -          if (neighbor->visitid && neighbor->visitid < numfacets)
    -            qh_fprintf(fp, 9263, " %d", neighbor->visitid);
    -        }
    -        qh_fprintf(fp, 9264, " # p%d(v%d)\n", vertex_i, vertex->id);
    -      }else
    -        qh_fprintf(fp, 9265, " # p%d is coplanar or isolated\n", vertex_i);
    -    }else {
    -      if (numinf)
    -        numneighbors++;
    -      qh_fprintf(fp, 9266, "%d", numneighbors);
    -      if (vertex) {
    -        FOREACHneighbor_(vertex) {
    -          if (neighbor->visitid == 0) {
    -            if (numinf) {
    -              numinf= 0;
    -              qh_fprintf(fp, 9267, " %d", neighbor->visitid);
    -            }
    -          }else if (neighbor->visitid < numfacets)
    -            qh_fprintf(fp, 9268, " %d", neighbor->visitid);
    -        }
    -      }
    -      qh_fprintf(fp, 9269, "\n");
    -    }
    -  }
    -  if (format == qh_PRINTgeom)
    -    qh_fprintf(fp, 9270, "}\n");
    -  qh_settempfree(&vertices);
    -} /* printvoronoi */
    -
    -/*---------------------------------
    -
    -  qh_printvnorm( fp, vertex, vertexA, centers, unbounded )
    -    print one separating plane of the Voronoi diagram for a pair of input sites
    -    unbounded==True if centers includes vertex-at-infinity
    -
    -  assumes:
    -    qh_ASvoronoi and qh_vertexneighbors() already set
    -
    -  note:
    -    parameter unbounded is UNUSED by this callback
    -
    -  see:
    -    qh_printvdiagram()
    -    qh_eachvoronoi()
    -*/
    -void qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
    -  pointT *normal;
    -  realT offset;
    -  int k;
    -  QHULL_UNUSED(unbounded);
    -
    -  normal= qh_detvnorm(vertex, vertexA, centers, &offset);
    -  qh_fprintf(fp, 9271, "%d %d %d ",
    -      2+qh hull_dim, qh_pointid(vertex->point), qh_pointid(vertexA->point));
    -  for (k=0; k< qh hull_dim-1; k++)
    -    qh_fprintf(fp, 9272, qh_REAL_1, normal[k]);
    -  qh_fprintf(fp, 9273, qh_REAL_1, offset);
    -  qh_fprintf(fp, 9274, "\n");
    -} /* printvnorm */
    -
    -/*---------------------------------
    -
    -  qh_printvridge( fp, vertex, vertexA, centers, unbounded )
    -    print one ridge of the Voronoi diagram for a pair of input sites
    -    unbounded==True if centers includes vertex-at-infinity
    -
    -  see:
    -    qh_printvdiagram()
    -
    -  notes:
    -    the user may use a different function
    -    parameter unbounded is UNUSED
    -*/
    -void qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded) {
    -  facetT *facet, **facetp;
    -  QHULL_UNUSED(unbounded);
    -
    -  qh_fprintf(fp, 9275, "%d %d %d", qh_setsize(centers)+2,
    -       qh_pointid(vertex->point), qh_pointid(vertexA->point));
    -  FOREACHfacet_(centers)
    -    qh_fprintf(fp, 9276, " %d", facet->visitid);
    -  qh_fprintf(fp, 9277, "\n");
    -} /* printvridge */
    -
    -/*---------------------------------
    -
    -  qh_projectdim3( source, destination )
    -    project 2-d 3-d or 4-d point to a 3-d point
    -    uses qh.DROPdim and qh.hull_dim
    -    source and destination may be the same
    -
    -  notes:
    -    allocate 4 elements to destination just in case
    -*/
    -void qh_projectdim3 (pointT *source, pointT *destination) {
    -  int i,k;
    -
    -  for (k=0, i=0; k < qh hull_dim; k++) {
    -    if (qh hull_dim == 4) {
    -      if (k != qh DROPdim)
    -        destination[i++]= source[k];
    -    }else if (k == qh DROPdim)
    -      destination[i++]= 0;
    -    else
    -      destination[i++]= source[k];
    -  }
    -  while (i < 3)
    -    destination[i++]= 0.0;
    -} /* projectdim3 */
    -
    -/*---------------------------------
    -
    -  qh_readfeasible( dim, curline )
    -    read feasible point from current line and qh.fin
    -
    -  returns:
    -    number of lines read from qh.fin
    -    sets qh.FEASIBLEpoint with malloc'd coordinates
    -
    -  notes:
    -    checks for qh.HALFspace
    -    assumes dim > 1
    -
    -  see:
    -    qh_setfeasible
    -*/
    -int qh_readfeasible(int dim, const char *curline) {
    -  boolT isfirst= True;
    -  int linecount= 0, tokcount= 0;
    -  const char *s;
    -  char *t, firstline[qh_MAXfirst+1];
    -  coordT *coords, value;
    -
    -  if (!qh HALFspace) {
    -    qh_fprintf(qh ferr, 6070, "qhull input error: feasible point(dim 1 coords) is only valid for halfspace intersection\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (qh feasible_string)
    -    qh_fprintf(qh ferr, 7057, "qhull input warning: feasible point(dim 1 coords) overrides 'Hn,n,n' feasible point for halfspace intersection\n");
    -  if (!(qh feasible_point= (coordT*)qh_malloc(dim* sizeof(coordT)))) {
    -    qh_fprintf(qh ferr, 6071, "qhull error: insufficient memory for feasible point\n");
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  coords= qh feasible_point;
    -  while ((s= (isfirst ?  curline : fgets(firstline, qh_MAXfirst, qh fin)))) {
    -    if (isfirst)
    -      isfirst= False;
    -    else
    -      linecount++;
    -    while (*s) {
    -      while (isspace(*s))
    -        s++;
    -      value= qh_strtod(s, &t);
    -      if (s == t)
    -        break;
    -      s= t;
    -      *(coords++)= value;
    -      if (++tokcount == dim) {
    -        while (isspace(*s))
    -          s++;
    -        qh_strtod(s, &t);
    -        if (s != t) {
    -          qh_fprintf(qh ferr, 6072, "qhull input error: coordinates for feasible point do not finish out the line: %s\n",
    -               s);
    -          qh_errexit(qh_ERRinput, NULL, NULL);
    -        }
    -        return linecount;
    -      }
    -    }
    -  }
    -  qh_fprintf(qh ferr, 6073, "qhull input error: only %d coordinates.  Could not read %d-d feasible point.\n",
    -           tokcount, dim);
    -  qh_errexit(qh_ERRinput, NULL, NULL);
    -  return 0;
    -} /* readfeasible */
    -
    -/*---------------------------------
    -
    -  qh_readpoints( numpoints, dimension, ismalloc )
    -    read points from qh.fin into qh.first_point, qh.num_points
    -    qh.fin is lines of coordinates, one per vertex, first line number of points
    -    if 'rbox D4',
    -      gives message
    -    if qh.ATinfinity,
    -      adds point-at-infinity for Delaunay triangulations
    -
    -  returns:
    -    number of points, array of point coordinates, dimension, ismalloc True
    -    if qh.DELAUNAY & !qh.PROJECTinput, projects points to paraboloid
    -        and clears qh.PROJECTdelaunay
    -    if qh.HALFspace, reads optional feasible point, reads halfspaces,
    -        converts to dual.
    -
    -  for feasible point in "cdd format" in 3-d:
    -    3 1
    -    coordinates
    -    comments
    -    begin
    -    n 4 real/integer
    -    ...
    -    end
    -
    -  notes:
    -    dimension will change in qh_initqhull_globals if qh.PROJECTinput
    -    uses malloc() since qh_mem not initialized
    -    FIXUP QH11012: qh_readpoints needs rewriting, too long
    -*/
    -coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc) {
    -  coordT *points, *coords, *infinity= NULL;
    -  realT paraboloid, maxboloid= -REALmax, value;
    -  realT *coordp= NULL, *offsetp= NULL, *normalp= NULL;
    -  char *s= 0, *t, firstline[qh_MAXfirst+1];
    -  int diminput=0, numinput=0, dimfeasible= 0, newnum, k, tempi;
    -  int firsttext=0, firstshort=0, firstlong=0, firstpoint=0;
    -  int tokcount= 0, linecount=0, maxcount, coordcount=0;
    -  boolT islong, isfirst= True, wasbegin= False;
    -  boolT isdelaunay= qh DELAUNAY && !qh PROJECTinput;
    -
    -  if (qh CDDinput) {
    -    while ((s= fgets(firstline, qh_MAXfirst, qh fin))) {
    -      linecount++;
    -      if (qh HALFspace && linecount == 1 && isdigit(*s)) {
    -        dimfeasible= qh_strtol(s, &s);
    -        while (isspace(*s))
    -          s++;
    -        if (qh_strtol(s, &s) == 1)
    -          linecount += qh_readfeasible(dimfeasible, s);
    -        else
    -          dimfeasible= 0;
    -      }else if (!memcmp(firstline, "begin", (size_t)5) || !memcmp(firstline, "BEGIN", (size_t)5))
    -        break;
    -      else if (!*qh rbox_command)
    -        strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
    -    }
    -    if (!s) {
    -      qh_fprintf(qh ferr, 6074, "qhull input error: missing \"begin\" for cdd-formated input\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -  }
    -  while (!numinput && (s= fgets(firstline, qh_MAXfirst, qh fin))) {
    -    linecount++;
    -    if (!memcmp(s, "begin", (size_t)5) || !memcmp(s, "BEGIN", (size_t)5))
    -      wasbegin= True;
    -    while (*s) {
    -      while (isspace(*s))
    -        s++;
    -      if (!*s)
    -        break;
    -      if (!isdigit(*s)) {
    -        if (!*qh rbox_command) {
    -          strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
    -          firsttext= linecount;
    -        }
    -        break;
    -      }
    -      if (!diminput)
    -        diminput= qh_strtol(s, &s);
    -      else {
    -        numinput= qh_strtol(s, &s);
    -        if (numinput == 1 && diminput >= 2 && qh HALFspace && !qh CDDinput) {
    -          linecount += qh_readfeasible(diminput, s); /* checks if ok */
    -          dimfeasible= diminput;
    -          diminput= numinput= 0;
    -        }else
    -          break;
    -      }
    -    }
    -  }
    -  if (!s) {
    -    qh_fprintf(qh ferr, 6075, "qhull input error: short input file.  Did not find dimension and number of points\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (diminput > numinput) {
    -    tempi= diminput;    /* exchange dim and n, e.g., for cdd input format */
    -    diminput= numinput;
    -    numinput= tempi;
    -  }
    -  if (diminput < 2) {
    -    qh_fprintf(qh ferr, 6220,"qhull input error: dimension %d(first number) should be at least 2\n",
    -            diminput);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (isdelaunay) {
    -    qh PROJECTdelaunay= False;
    -    if (qh CDDinput)
    -      *dimension= diminput;
    -    else
    -      *dimension= diminput+1;
    -    *numpoints= numinput;
    -    if (qh ATinfinity)
    -      (*numpoints)++;
    -  }else if (qh HALFspace) {
    -    *dimension= diminput - 1;
    -    *numpoints= numinput;
    -    if (diminput < 3) {
    -      qh_fprintf(qh ferr, 6221,"qhull input error: dimension %d(first number, includes offset) should be at least 3 for halfspaces\n",
    -            diminput);
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    if (dimfeasible) {
    -      if (dimfeasible != *dimension) {
    -        qh_fprintf(qh ferr, 6222,"qhull input error: dimension %d of feasible point is not one less than dimension %d for halfspaces\n",
    -          dimfeasible, diminput);
    -        qh_errexit(qh_ERRinput, NULL, NULL);
    -      }
    -    }else
    -      qh_setfeasible(*dimension);
    -  }else {
    -    if (qh CDDinput)
    -      *dimension= diminput-1;
    -    else
    -      *dimension= diminput;
    -    *numpoints= numinput;
    -  }
    -  qh normal_size= *dimension * sizeof(coordT); /* for tracing with qh_printpoint */
    -  if (qh HALFspace) {
    -    qh half_space= coordp= (coordT*)qh_malloc(qh normal_size + sizeof(coordT));
    -    if (qh CDDinput) {
    -      offsetp= qh half_space;
    -      normalp= offsetp + 1;
    -    }else {
    -      normalp= qh half_space;
    -      offsetp= normalp + *dimension;
    -    }
    -  }
    -  qh maxline= diminput * (qh_REALdigits + 5);
    -  maximize_(qh maxline, 500);
    -  qh line= (char*)qh_malloc((qh maxline+1) * sizeof(char));
    -  *ismalloc= True;  /* use malloc since memory not setup */
    -  coords= points= qh temp_malloc=
    -        (coordT*)qh_malloc((*numpoints)*(*dimension)*sizeof(coordT));
    -  if (!coords || !qh line || (qh HALFspace && !qh half_space)) {
    -    qh_fprintf(qh ferr, 6076, "qhull error: insufficient memory to read %d points\n",
    -            numinput);
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  if (isdelaunay && qh ATinfinity) {
    -    infinity= points + numinput * (*dimension);
    -    for (k= (*dimension) - 1; k--; )
    -      infinity[k]= 0.0;
    -  }
    -  maxcount= numinput * diminput;
    -  paraboloid= 0.0;
    -  while ((s= (isfirst ?  s : fgets(qh line, qh maxline, qh fin)))) {
    -    if (!isfirst) {
    -      linecount++;
    -      if (*s == 'e' || *s == 'E') {
    -        if (!memcmp(s, "end", (size_t)3) || !memcmp(s, "END", (size_t)3)) {
    -          if (qh CDDinput )
    -            break;
    -          else if (wasbegin)
    -            qh_fprintf(qh ferr, 7058, "qhull input warning: the input appears to be in cdd format.  If so, use 'Fd'\n");
    -        }
    -      }
    -    }
    -    islong= False;
    -    while (*s) {
    -      while (isspace(*s))
    -        s++;
    -      value= qh_strtod(s, &t);
    -      if (s == t) {
    -        if (!*qh rbox_command)
    -         strncat(qh rbox_command, s, sizeof(qh rbox_command)-1);
    -        if (*s && !firsttext)
    -          firsttext= linecount;
    -        if (!islong && !firstshort && coordcount)
    -          firstshort= linecount;
    -        break;
    -      }
    -      if (!firstpoint)
    -        firstpoint= linecount;
    -      s= t;
    -      if (++tokcount > maxcount)
    -        continue;
    -      if (qh HALFspace) {
    -        if (qh CDDinput)
    -          *(coordp++)= -value; /* both coefficients and offset */
    -        else
    -          *(coordp++)= value;
    -      }else {
    -        *(coords++)= value;
    -        if (qh CDDinput && !coordcount) {
    -          if (value != 1.0) {
    -            qh_fprintf(qh ferr, 6077, "qhull input error: for cdd format, point at line %d does not start with '1'\n",
    -                   linecount);
    -            qh_errexit(qh_ERRinput, NULL, NULL);
    -          }
    -          coords--;
    -        }else if (isdelaunay) {
    -          paraboloid += value * value;
    -          if (qh ATinfinity) {
    -            if (qh CDDinput)
    -              infinity[coordcount-1] += value;
    -            else
    -              infinity[coordcount] += value;
    -          }
    -        }
    -      }
    -      if (++coordcount == diminput) {
    -        coordcount= 0;
    -        if (isdelaunay) {
    -          *(coords++)= paraboloid;
    -          maximize_(maxboloid, paraboloid);
    -          paraboloid= 0.0;
    -        }else if (qh HALFspace) {
    -          if (!qh_sethalfspace(*dimension, coords, &coords, normalp, offsetp, qh feasible_point)) {
    -            qh_fprintf(qh ferr, 8048, "The halfspace was on line %d\n", linecount);
    -            if (wasbegin)
    -              qh_fprintf(qh ferr, 8049, "The input appears to be in cdd format.  If so, you should use option 'Fd'\n");
    -            qh_errexit(qh_ERRinput, NULL, NULL);
    -          }
    -          coordp= qh half_space;
    -        }
    -        while (isspace(*s))
    -          s++;
    -        if (*s) {
    -          islong= True;
    -          if (!firstlong)
    -            firstlong= linecount;
    -        }
    -      }
    -    }
    -    if (!islong && !firstshort && coordcount)
    -      firstshort= linecount;
    -    if (!isfirst && s - qh line >= qh maxline) {
    -      qh_fprintf(qh ferr, 6078, "qhull input error: line %d contained more than %d characters\n",
    -              linecount, (int) (s - qh line));   /* WARN64 */
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    isfirst= False;
    -  }
    -  if (tokcount != maxcount) {
    -    newnum= fmin_(numinput, tokcount/diminput);
    -    qh_fprintf(qh ferr, 7073,"\
    -qhull warning: instead of %d %d-dimensional points, input contains\n\
    -%d points and %d extra coordinates.  Line %d is the first\npoint",
    -       numinput, diminput, tokcount/diminput, tokcount % diminput, firstpoint);
    -    if (firsttext)
    -      qh_fprintf(qh ferr, 8051, ", line %d is the first comment", firsttext);
    -    if (firstshort)
    -      qh_fprintf(qh ferr, 8052, ", line %d is the first short\nline", firstshort);
    -    if (firstlong)
    -      qh_fprintf(qh ferr, 8053, ", line %d is the first long line", firstlong);
    -    qh_fprintf(qh ferr, 8054, ".  Continue with %d points.\n", newnum);
    -    numinput= newnum;
    -    if (isdelaunay && qh ATinfinity) {
    -      for (k= tokcount % diminput; k--; )
    -        infinity[k] -= *(--coords);
    -      *numpoints= newnum+1;
    -    }else {
    -      coords -= tokcount % diminput;
    -      *numpoints= newnum;
    -    }
    -  }
    -  if (isdelaunay && qh ATinfinity) {
    -    for (k= (*dimension) -1; k--; )
    -      infinity[k] /= numinput;
    -    if (coords == infinity)
    -      coords += (*dimension) -1;
    -    else {
    -      for (k=0; k < (*dimension) -1; k++)
    -        *(coords++)= infinity[k];
    -    }
    -    *(coords++)= maxboloid * 1.1;
    -  }
    -  if (qh rbox_command[0]) {
    -    qh rbox_command[strlen(qh rbox_command)-1]= '\0';
    -    if (!strcmp(qh rbox_command, "./rbox D4"))
    -      qh_fprintf(qh ferr, 8055, "\n\
    -This is the qhull test case.  If any errors or core dumps occur,\n\
    -recompile qhull with 'make new'.  If errors still occur, there is\n\
    -an incompatibility.  You should try a different compiler.  You can also\n\
    -change the choices in user.h.  If you discover the source of the problem,\n\
    -please send mail to qhull_bug@qhull.org.\n\
    -\n\
    -Type 'qhull' for a short list of options.\n");
    -  }
    -  qh_free(qh line);
    -  qh line= NULL;
    -  if (qh half_space) {
    -    qh_free(qh half_space);
    -    qh half_space= NULL;
    -  }
    -  qh temp_malloc= NULL;
    -  trace1((qh ferr, 1008,"qh_readpoints: read in %d %d-dimensional points\n",
    -          numinput, diminput));
    -  return(points);
    -} /* readpoints */
    -
    -
    -/*---------------------------------
    -
    -  qh_setfeasible( dim )
    -    set qh.FEASIBLEpoint from qh.feasible_string in "n,n,n" or "n n n" format
    -
    -  notes:
    -    "n,n,n" already checked by qh_initflags()
    -    see qh_readfeasible()
    -*/
    -void qh_setfeasible(int dim) {
    -  int tokcount= 0;
    -  char *s;
    -  coordT *coords, value;
    -
    -  if (!(s= qh feasible_string)) {
    -    qh_fprintf(qh ferr, 6223, "\
    -qhull input error: halfspace intersection needs a feasible point.\n\
    -Either prepend the input with 1 point or use 'Hn,n,n'.  See manual.\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (!(qh feasible_point= (pointT*)qh_malloc(dim * sizeof(coordT)))) {
    -    qh_fprintf(qh ferr, 6079, "qhull error: insufficient memory for 'Hn,n,n'\n");
    -    qh_errexit(qh_ERRmem, NULL, NULL);
    -  }
    -  coords= qh feasible_point;
    -  while (*s) {
    -    value= qh_strtod(s, &s);
    -    if (++tokcount > dim) {
    -      qh_fprintf(qh ferr, 7059, "qhull input warning: more coordinates for 'H%s' than dimension %d\n",
    -          qh feasible_string, dim);
    -      break;
    -    }
    -    *(coords++)= value;
    -    if (*s)
    -      s++;
    -  }
    -  while (++tokcount <= dim)
    -    *(coords++)= 0.0;
    -} /* setfeasible */
    -
    -/*---------------------------------
    -
    -  qh_skipfacet( facet )
    -    returns 'True' if this facet is not to be printed
    -
    -  notes:
    -    based on the user provided slice thresholds and 'good' specifications
    -*/
    -boolT qh_skipfacet(facetT *facet) {
    -  facetT *neighbor, **neighborp;
    -
    -  if (qh PRINTneighbors) {
    -    if (facet->good)
    -      return !qh PRINTgood;
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->good)
    -        return False;
    -    }
    -    return True;
    -  }else if (qh PRINTgood)
    -    return !facet->good;
    -  else if (!facet->normal)
    -    return True;
    -  return(!qh_inthresholds(facet->normal, NULL));
    -} /* skipfacet */
    -
    -/*---------------------------------
    -
    -  qh_skipfilename( string )
    -    returns pointer to character after filename
    -
    -  notes:
    -    skips leading spaces
    -    ends with spacing or eol
    -    if starts with ' or " ends with the same, skipping \' or \"
    -    For qhull, qh_argv_to_command() only uses double quotes
    -*/
    -char *qh_skipfilename(char *filename) {
    -  char *s= filename;  /* non-const due to return */
    -  char c;
    -
    -  while (*s && isspace(*s))
    -    s++;
    -  c= *s++;
    -  if (c == '\0') {
    -    qh_fprintf(qh ferr, 6204, "qhull input error: filename expected, none found.\n");
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  if (c == '\'' || c == '"') {
    -    while (*s !=c || s[-1] == '\\') {
    -      if (!*s) {
    -        qh_fprintf(qh ferr, 6203, "qhull input error: missing quote after filename -- %s\n", filename);
    -        qh_errexit(qh_ERRinput, NULL, NULL);
    -      }
    -      s++;
    -    }
    -    s++;
    -  }
    -  else while (*s && !isspace(*s))
    -      s++;
    -  return s;
    -} /* skipfilename */
    diff --git a/extern/qhull/io.h b/extern/qhull/io.h
    deleted file mode 100644
    index 580d51b9b3fd..000000000000
    --- a/extern/qhull/io.h
    +++ /dev/null
    @@ -1,159 +0,0 @@
    -/*
      ---------------------------------
    -
    -   io.h
    -   declarations of Input/Output functions
    -
    -   see README, libqhull.h and io.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/io.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFio
    -#define qhDEFio 1
    -
    -#include "libqhull.h"
    -
    -/*============ constants and flags ==================*/
    -
    -/*----------------------------------
    -
    -  qh_MAXfirst
    -    maximum length of first two lines of stdin
    -*/
    -#define qh_MAXfirst  200
    -
    -/*----------------------------------
    -
    -  qh_MINradius
    -    min radius for Gp and Gv, fraction of maxcoord
    -*/
    -#define qh_MINradius 0.02
    -
    -/*----------------------------------
    -
    -  qh_GEOMepsilon
    -    adjust outer planes for 'lines closer' and geomview roundoff.
    -    This prevents bleed through.
    -*/
    -#define qh_GEOMepsilon 2e-3
    -
    -/*----------------------------------
    -
    -  qh_WHITESPACE
    -    possible values of white space
    -*/
    -#define qh_WHITESPACE " \n\t\v\r\f"
    -
    -
    -/*----------------------------------
    -
    -  qh_RIDGE
    -    to select which ridges to print in qh_eachvoronoi
    -*/
    -typedef enum
    -{
    -    qh_RIDGEall = 0, qh_RIDGEinner, qh_RIDGEouter
    -}
    -qh_RIDGE;
    -
    -/*----------------------------------
    -
    -  printvridgeT
    -    prints results of qh_printvdiagram
    -
    -  see:
    -    qh_printvridge for an example
    -*/
    -typedef void (*printvridgeT)(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    -
    -/*============== -prototypes in alphabetical order =========*/
    -
    -void    dfacet(unsigned id);
    -void    dvertex(unsigned id);
    -int     qh_compare_facetarea(const void *p1, const void *p2);
    -int     qh_compare_facetmerge(const void *p1, const void *p2);
    -int     qh_compare_facetvisit(const void *p1, const void *p2);
    -int     qh_compare_vertexpoint(const void *p1, const void *p2); /* not used */
    -void    qh_copyfilename(char *filename, int size, const char* source, int length);
    -void    qh_countfacets(facetT *facetlist, setT *facets, boolT printall,
    -              int *numfacetsp, int *numsimplicialp, int *totneighborsp,
    -              int *numridgesp, int *numcoplanarsp, int *numnumtricoplanarsp);
    -pointT *qh_detvnorm(vertexT *vertex, vertexT *vertexA, setT *centers, realT *offsetp);
    -setT   *qh_detvridge(vertexT *vertex);
    -setT   *qh_detvridge3 (vertexT *atvertex, vertexT *vertex);
    -int     qh_eachvoronoi(FILE *fp, printvridgeT printvridge, vertexT *atvertex, boolT visitall, qh_RIDGE innerouter, boolT inorder);
    -int     qh_eachvoronoi_all(FILE *fp, printvridgeT printvridge, boolT isUpper, qh_RIDGE innerouter, boolT inorder);
    -void    qh_facet2point(facetT *facet, pointT **point0, pointT **point1, realT *mindist);
    -setT   *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
    -void    qh_geomplanes(facetT *facet, realT *outerplane, realT *innerplane);
    -void    qh_markkeep(facetT *facetlist);
    -setT   *qh_markvoronoi(facetT *facetlist, setT *facets, boolT printall, boolT *isLowerp, int *numcentersp);
    -void    qh_order_vertexneighbors(vertexT *vertex);
    -void    qh_prepare_output(void);
    -void    qh_printafacet(FILE *fp, qh_PRINT format, facetT *facet, boolT printall);
    -void    qh_printbegin(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printcenter(FILE *fp, qh_PRINT format, const char *string, facetT *facet);
    -void    qh_printcentrum(FILE *fp, facetT *facet, realT radius);
    -void    qh_printend(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printend4geom(FILE *fp, facetT *facet, int *num, boolT printall);
    -void    qh_printextremes(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printextremes_2d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printextremes_d(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printfacet(FILE *fp, facetT *facet);
    -void    qh_printfacet2math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    -void    qh_printfacet2geom(FILE *fp, facetT *facet, realT color[3]);
    -void    qh_printfacet2geom_points(FILE *fp, pointT *point1, pointT *point2,
    -                               facetT *facet, realT offset, realT color[3]);
    -void    qh_printfacet3math(FILE *fp, facetT *facet, qh_PRINT format, int notfirst);
    -void    qh_printfacet3geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
    -void    qh_printfacet3geom_points(FILE *fp, setT *points, facetT *facet, realT offset, realT color[3]);
    -void    qh_printfacet3geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
    -void    qh_printfacet3vertex(FILE *fp, facetT *facet, qh_PRINT format);
    -void    qh_printfacet4geom_nonsimplicial(FILE *fp, facetT *facet, realT color[3]);
    -void    qh_printfacet4geom_simplicial(FILE *fp, facetT *facet, realT color[3]);
    -void    qh_printfacetNvertex_nonsimplicial(FILE *fp, facetT *facet, int id, qh_PRINT format);
    -void    qh_printfacetNvertex_simplicial(FILE *fp, facetT *facet, qh_PRINT format);
    -void    qh_printfacetheader(FILE *fp, facetT *facet);
    -void    qh_printfacetridges(FILE *fp, facetT *facet);
    -void    qh_printfacets(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printhyperplaneintersection(FILE *fp, facetT *facet1, facetT *facet2,
    -                   setT *vertices, realT color[3]);
    -void    qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    -void    qh_printline3geom(FILE *fp, pointT *pointA, pointT *pointB, realT color[3]);
    -void    qh_printpoint(FILE *fp, const char *string, pointT *point);
    -void    qh_printpointid(FILE *fp, const char *string, int dim, pointT *point, int id);
    -void    qh_printpoint3 (FILE *fp, pointT *point);
    -void    qh_printpoints_out(FILE *fp, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printpointvect(FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius, realT color[3]);
    -void    qh_printpointvect2 (FILE *fp, pointT *point, coordT *normal, pointT *center, realT radius);
    -void    qh_printridge(FILE *fp, ridgeT *ridge);
    -void    qh_printspheres(FILE *fp, setT *vertices, realT radius);
    -void    qh_printvdiagram(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    -int     qh_printvdiagram2 (FILE *fp, printvridgeT printvridge, setT *vertices, qh_RIDGE innerouter, boolT inorder);
    -void    qh_printvertex(FILE *fp, vertexT *vertex);
    -void    qh_printvertexlist(FILE *fp, const char* string, facetT *facetlist,
    -                         setT *facets, boolT printall);
    -void    qh_printvertices(FILE *fp, const char* string, setT *vertices);
    -void    qh_printvneighbors(FILE *fp, facetT* facetlist, setT *facets, boolT printall);
    -void    qh_printvoronoi(FILE *fp, qh_PRINT format, facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printvnorm(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    -void    qh_printvridge(FILE *fp, vertexT *vertex, vertexT *vertexA, setT *centers, boolT unbounded);
    -void    qh_produce_output(void);
    -void    qh_produce_output2(void);
    -void    qh_projectdim3 (pointT *source, pointT *destination);
    -int     qh_readfeasible(int dim, const char *curline);
    -coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
    -void    qh_setfeasible(int dim);
    -boolT   qh_skipfacet(facetT *facet);
    -char   *qh_skipfilename(char *filename);
    -
    -#endif /* qhDEFio */
    diff --git a/extern/qhull/libqhull.c b/extern/qhull/libqhull.c
    deleted file mode 100644
    index 34a0fc611289..000000000000
    --- a/extern/qhull/libqhull.c
    +++ /dev/null
    @@ -1,1399 +0,0 @@
    -/*
      ---------------------------------
    -
    -   libqhull.c
    -   Quickhull algorithm for convex hulls
    -
    -   qhull() and top-level routines
    -
    -   see qh-qhull.htm, libqhull.h, unix.c
    -
    -   see qhull_a.h for internal functions
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/libqhull.c#4 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*============= functions in alphabetic order after qhull() =======*/
    -
    -/*---------------------------------
    -
    -  qh_qhull()
    -    compute DIM3 convex hull of qh.num_points starting at qh.first_point
    -    qh contains all global options and variables
    -
    -  returns:
    -    returns polyhedron
    -      qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices,
    -
    -    returns global variables
    -      qh.hulltime, qh.max_outside, qh.interior_point, qh.max_vertex, qh.min_vertex
    -
    -    returns precision constants
    -      qh.ANGLEround, centrum_radius, cos_max, DISTround, MAXabs_coord, ONEmerge
    -
    -  notes:
    -    unless needed for output
    -      qh.max_vertex and qh.min_vertex are max/min due to merges
    -
    -  see:
    -    to add individual points to either qh.num_points
    -      use qh_addpoint()
    -
    -    if qh.GETarea
    -      qh_produceoutput() returns qh.totarea and qh.totvol via qh_getarea()
    -
    -  design:
    -    record starting time
    -    initialize hull and partition points
    -    build convex hull
    -    unless early termination
    -      update facet->maxoutside for vertices, coplanar, and near-inside points
    -    error if temporary sets exist
    -    record end time
    -*/
    -
    -void qh_qhull(void) {
    -  int numoutside;
    -
    -  qh hulltime= qh_CPUclock;
    -  if (qh RERUN || qh JOGGLEmax < REALmax/2)
    -    qh_build_withrestart();
    -  else {
    -    qh_initbuild();
    -    qh_buildhull();
    -  }
    -  if (!qh STOPpoint && !qh STOPcone) {
    -    if (qh ZEROall_ok && !qh TESTvneighbors && qh MERGEexact)
    -      qh_checkzero( qh_ALL);
    -    if (qh ZEROall_ok && !qh TESTvneighbors && !qh WAScoplanar) {
    -      trace2((qh ferr, 2055, "qh_qhull: all facets are clearly convex and no coplanar points.  Post-merging and check of maxout not needed.\n"));
    -      qh DOcheckmax= False;
    -    }else {
    -      if (qh MERGEexact || (qh hull_dim > qh_DIMreduceBuild && qh PREmerge))
    -        qh_postmerge("First post-merge", qh premerge_centrum, qh premerge_cos,
    -             (qh POSTmerge ? False : qh TESTvneighbors));
    -      else if (!qh POSTmerge && qh TESTvneighbors)
    -        qh_postmerge("For testing vertex neighbors", qh premerge_centrum,
    -             qh premerge_cos, True);
    -      if (qh POSTmerge)
    -        qh_postmerge("For post-merging", qh postmerge_centrum,
    -             qh postmerge_cos, qh TESTvneighbors);
    -      if (qh visible_list == qh facet_list) { /* i.e., merging done */
    -        qh findbestnew= True;
    -        qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numoutside);
    -        qh findbestnew= False;
    -        qh_deletevisible(/*qh visible_list*/);
    -        qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
    -      }
    -    }
    -    if (qh DOcheckmax){
    -      if (qh REPORTfreq) {
    -        qh_buildtracing(NULL, NULL);
    -        qh_fprintf(qh ferr, 8115, "\nTesting all coplanar points.\n");
    -      }
    -      qh_check_maxout();
    -    }
    -    if (qh KEEPnearinside && !qh maxoutdone)
    -      qh_nearcoplanar();
    -  }
    -  if (qh_setsize(qhmem.tempstack) != 0) {
    -    qh_fprintf(qh ferr, 6164, "qhull internal error (qh_qhull): temporary sets not empty(%d)\n",
    -             qh_setsize(qhmem.tempstack));
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  qh hulltime= qh_CPUclock - qh hulltime;
    -  qh QHULLfinished= True;
    -  trace1((qh ferr, 1036, "Qhull: algorithm completed\n"));
    -} /* qhull */
    -
    -/*---------------------------------
    -
    -  qh_addpoint( furthest, facet, checkdist )
    -    add point (usually furthest point) above facet to hull
    -    if checkdist,
    -      check that point is above facet.
    -      if point is not outside of the hull, uses qh_partitioncoplanar()
    -      assumes that facet is defined by qh_findbestfacet()
    -    else if facet specified,
    -      assumes that point is above facet (major damage if below)
    -    for Delaunay triangulations,
    -      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
    -      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
    -
    -  returns:
    -    returns False if user requested an early termination
    -     qh.visible_list, newfacet_list, delvertex_list, NEWfacets may be defined
    -    updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
    -    clear qh.maxoutdone (will need to call qh_check_maxout() for facet->maxoutside)
    -    if unknown point, adds a pointer to qh.other_points
    -      do not deallocate the point's coordinates
    -
    -  notes:
    -    assumes point is near its best facet and not at a local minimum of a lens
    -      distributions.  Use qh_findbestfacet to avoid this case.
    -    uses qh.visible_list, qh.newfacet_list, qh.delvertex_list, qh.NEWfacets
    -
    -  see also:
    -    qh_triangulate() -- triangulate non-simplicial facets
    -
    -  design:
    -    add point to other_points if needed
    -    if checkdist
    -      if point not above facet
    -        partition coplanar point
    -        exit
    -    exit if pre STOPpoint requested
    -    find horizon and visible facets for point
    -    make new facets for point to horizon
    -    make hyperplanes for point
    -    compute balance statistics
    -    match neighboring new facets
    -    update vertex neighbors and delete interior vertices
    -    exit if STOPcone requested
    -    merge non-convex new facets
    -    if merge found, many merges, or 'Qf'
    -       use qh_findbestnew() instead of qh_findbest()
    -    partition outside points from visible facets
    -    delete visible facets
    -    check polyhedron if requested
    -    exit if post STOPpoint requested
    -    reset working lists of facets and vertices
    -*/
    -boolT qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist) {
    -  int goodvisible, goodhorizon;
    -  vertexT *vertex;
    -  facetT *newfacet;
    -  realT dist, newbalance, pbalance;
    -  boolT isoutside= False;
    -  int numpart, numpoints, numnew, firstnew;
    -
    -  qh maxoutdone= False;
    -  if (qh_pointid(furthest) == -1)
    -    qh_setappend(&qh other_points, furthest);
    -  if (!facet) {
    -    qh_fprintf(qh ferr, 6213, "qhull internal error (qh_addpoint): NULL facet.  Need to call qh_findbestfacet first\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (checkdist) {
    -    facet= qh_findbest(furthest, facet, !qh_ALL, !qh_ISnewfacets, !qh_NOupper,
    -                        &dist, &isoutside, &numpart);
    -    zzadd_(Zpartition, numpart);
    -    if (!isoutside) {
    -      zinc_(Znotmax);  /* last point of outsideset is no longer furthest. */
    -      facet->notfurthest= True;
    -      qh_partitioncoplanar(furthest, facet, &dist);
    -      return True;
    -    }
    -  }
    -  qh_buildtracing(furthest, facet);
    -  if (qh STOPpoint < 0 && qh furthest_id == -qh STOPpoint-1) {
    -    facet->notfurthest= True;
    -    return False;
    -  }
    -  qh_findhorizon(furthest, facet, &goodvisible, &goodhorizon);
    -  if (qh ONLYgood && !(goodvisible+goodhorizon) && !qh GOODclosest) {
    -    zinc_(Znotgood);
    -    facet->notfurthest= True;
    -    /* last point of outsideset is no longer furthest.  This is ok
    -       since all points of the outside are likely to be bad */
    -    qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
    -    return True;
    -  }
    -  zzinc_(Zprocessed);
    -  firstnew= qh facet_id;
    -  vertex= qh_makenewfacets(furthest /*visible_list, attaches if !ONLYgood */);
    -  qh_makenewplanes(/* newfacet_list */);
    -  numnew= qh facet_id - firstnew;
    -  newbalance= numnew - (realT) (qh num_facets-qh num_visible)
    -                         * qh hull_dim/qh num_vertices;
    -  wadd_(Wnewbalance, newbalance);
    -  wadd_(Wnewbalance2, newbalance * newbalance);
    -  if (qh ONLYgood
    -  && !qh_findgood(qh newfacet_list, goodhorizon) && !qh GOODclosest) {
    -    FORALLnew_facets
    -      qh_delfacet(newfacet);
    -    qh_delvertex(vertex);
    -    qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
    -    zinc_(Znotgoodnew);
    -    facet->notfurthest= True;
    -    return True;
    -  }
    -  if (qh ONLYgood)
    -    qh_attachnewfacets(/*visible_list*/);
    -  qh_matchnewfacets();
    -  qh_updatevertices();
    -  if (qh STOPcone && qh furthest_id == qh STOPcone-1) {
    -    facet->notfurthest= True;
    -    return False;  /* visible_list etc. still defined */
    -  }
    -  qh findbestnew= False;
    -  if (qh PREmerge || qh MERGEexact) {
    -    qh_premerge(vertex, qh premerge_centrum, qh premerge_cos);
    -    if (qh_USEfindbestnew)
    -      qh findbestnew= True;
    -    else {
    -      FORALLnew_facets {
    -        if (!newfacet->simplicial) {
    -          qh findbestnew= True;  /* use qh_findbestnew instead of qh_findbest*/
    -          break;
    -        }
    -      }
    -    }
    -  }else if (qh BESToutside)
    -    qh findbestnew= True;
    -  qh_partitionvisible(/*visible_list, newfacet_list*/ !qh_ALL, &numpoints);
    -  qh findbestnew= False;
    -  qh findbest_notsharp= False;
    -  zinc_(Zpbalance);
    -  pbalance= numpoints - (realT) qh hull_dim /* assumes all points extreme */
    -                * (qh num_points - qh num_vertices)/qh num_vertices;
    -  wadd_(Wpbalance, pbalance);
    -  wadd_(Wpbalance2, pbalance * pbalance);
    -  qh_deletevisible(/*qh visible_list*/);
    -  zmax_(Zmaxvertex, qh num_vertices);
    -  qh NEWfacets= False;
    -  if (qh IStracing >= 4) {
    -    if (qh num_facets < 2000)
    -      qh_printlists();
    -    qh_printfacetlist(qh newfacet_list, NULL, True);
    -    qh_checkpolygon(qh facet_list);
    -  }else if (qh CHECKfrequently) {
    -    if (qh num_facets < 50)
    -      qh_checkpolygon(qh facet_list);
    -    else
    -      qh_checkpolygon(qh newfacet_list);
    -  }
    -  if (qh STOPpoint > 0 && qh furthest_id == qh STOPpoint-1)
    -    return False;
    -  qh_resetlists(True, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
    -  /* qh_triangulate(); to test qh.TRInormals */
    -  trace2((qh ferr, 2056, "qh_addpoint: added p%d new facets %d new balance %2.2g point balance %2.2g\n",
    -    qh_pointid(furthest), numnew, newbalance, pbalance));
    -  return True;
    -} /* addpoint */
    -
    -/*---------------------------------
    -
    -  qh_build_withrestart()
    -    allow restarts due to qh.JOGGLEmax while calling qh_buildhull()
    -    qh.FIRSTpoint/qh.NUMpoints is point array
    -        it may be moved by qh_joggleinput()
    -*/
    -void qh_build_withrestart(void) {
    -  int restart;
    -
    -  qh ALLOWrestart= True;
    -  while (True) {
    -    restart= setjmp(qh restartexit); /* simple statement for CRAY J916 */
    -    if (restart) {       /* only from qh_precision() */
    -      zzinc_(Zretry);
    -      wmax_(Wretrymax, qh JOGGLEmax);
    -      /* QH7078 warns about using 'TCn' with 'QJn' */
    -      qh STOPcone= -1; /* if break from joggle, prevents normal output */
    -    }
    -    if (!qh RERUN && qh JOGGLEmax < REALmax/2) {
    -      if (qh build_cnt > qh_JOGGLEmaxretry) {
    -        qh_fprintf(qh ferr, 6229, "qhull precision error: %d attempts to construct a convex hull\n\
    -        with joggled input.  Increase joggle above 'QJ%2.2g'\n\
    -        or modify qh_JOGGLE... parameters in user.h\n",
    -           qh build_cnt, qh JOGGLEmax);
    -        qh_errexit(qh_ERRqhull, NULL, NULL);
    -      }
    -      if (qh build_cnt && !restart)
    -        break;
    -    }else if (qh build_cnt && qh build_cnt >= qh RERUN)
    -      break;
    -    qh STOPcone= 0;
    -    qh_freebuild(True);  /* first call is a nop */
    -    qh build_cnt++;
    -    if (!qh qhull_optionsiz)
    -      qh qhull_optionsiz= (int)strlen(qh qhull_options);   /* WARN64 */
    -    else {
    -      qh qhull_options [qh qhull_optionsiz]= '\0';
    -      qh qhull_optionlen= qh_OPTIONline;  /* starts a new line */
    -    }
    -    qh_option("_run", &qh build_cnt, NULL);
    -    if (qh build_cnt == qh RERUN) {
    -      qh IStracing= qh TRACElastrun;  /* duplicated from qh_initqhull_globals */
    -      if (qh TRACEpoint != -1 || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
    -        qh TRACElevel= (qh IStracing? qh IStracing : 3);
    -        qh IStracing= 0;
    -      }
    -      qhmem.IStracing= qh IStracing;
    -    }
    -    if (qh JOGGLEmax < REALmax/2)
    -      qh_joggleinput();
    -    qh_initbuild();
    -    qh_buildhull();
    -    if (qh JOGGLEmax < REALmax/2 && !qh MERGING)
    -      qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    -  }
    -  qh ALLOWrestart= False;
    -} /* qh_build_withrestart */
    -
    -/*---------------------------------
    -
    -  qh_buildhull()
    -    construct a convex hull by adding outside points one at a time
    -
    -  returns:
    -
    -  notes:
    -    may be called multiple times
    -    checks facet and vertex lists for incorrect flags
    -    to recover from STOPcone, call qh_deletevisible and qh_resetlists
    -
    -  design:
    -    check visible facet and newfacet flags
    -    check newlist vertex flags and qh.STOPcone/STOPpoint
    -    for each facet with a furthest outside point
    -      add point to facet
    -      exit if qh.STOPcone or qh.STOPpoint requested
    -    if qh.NARROWhull for initial simplex
    -      partition remaining outside points to coplanar sets
    -*/
    -void qh_buildhull(void) {
    -  facetT *facet;
    -  pointT *furthest;
    -  vertexT *vertex;
    -  int id;
    -
    -  trace1((qh ferr, 1037, "qh_buildhull: start build hull\n"));
    -  FORALLfacets {
    -    if (facet->visible || facet->newfacet) {
    -      qh_fprintf(qh ferr, 6165, "qhull internal error (qh_buildhull): visible or new facet f%d in facet list\n",
    -                   facet->id);
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -  }
    -  FORALLvertices {
    -    if (vertex->newlist) {
    -      qh_fprintf(qh ferr, 6166, "qhull internal error (qh_buildhull): new vertex f%d in vertex list\n",
    -                   vertex->id);
    -      qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -    id= qh_pointid(vertex->point);
    -    if ((qh STOPpoint>0 && id == qh STOPpoint-1) ||
    -        (qh STOPpoint<0 && id == -qh STOPpoint-1) ||
    -        (qh STOPcone>0 && id == qh STOPcone-1)) {
    -      trace1((qh ferr, 1038,"qh_buildhull: stop point or cone P%d in initial hull\n", id));
    -      return;
    -    }
    -  }
    -  qh facet_next= qh facet_list;      /* advance facet when processed */
    -  while ((furthest= qh_nextfurthest(&facet))) {
    -    qh num_outside--;  /* if ONLYmax, furthest may not be outside */
    -    if (!qh_addpoint(furthest, facet, qh ONLYmax))
    -      break;
    -  }
    -  if (qh NARROWhull) /* move points from outsideset to coplanarset */
    -    qh_outcoplanar( /* facet_list */ );
    -  if (qh num_outside && !furthest) {
    -    qh_fprintf(qh ferr, 6167, "qhull internal error (qh_buildhull): %d outside points were never processed.\n", qh num_outside);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  trace1((qh ferr, 1039, "qh_buildhull: completed the hull construction\n"));
    -} /* buildhull */
    -
    -
    -/*---------------------------------
    -
    -  qh_buildtracing( furthest, facet )
    -    trace an iteration of qh_buildhull() for furthest point and facet
    -    if !furthest, prints progress message
    -
    -  returns:
    -    tracks progress with qh.lastreport
    -    updates qh.furthest_id (-3 if furthest is NULL)
    -    also resets visit_id, vertext_visit on wrap around
    -
    -  see:
    -    qh_tracemerging()
    -
    -  design:
    -    if !furthest
    -      print progress message
    -      exit
    -    if 'TFn' iteration
    -      print progress message
    -    else if tracing
    -      trace furthest point and facet
    -    reset qh.visit_id and qh.vertex_visit if overflow may occur
    -    set qh.furthest_id for tracing
    -*/
    -void qh_buildtracing(pointT *furthest, facetT *facet) {
    -  realT dist= 0;
    -  float cpu;
    -  int total, furthestid;
    -  time_t timedata;
    -  struct tm *tp;
    -  vertexT *vertex;
    -
    -  qh old_randomdist= qh RANDOMdist;
    -  qh RANDOMdist= False;
    -  if (!furthest) {
    -    time(&timedata);
    -    tp= localtime(&timedata);
    -    cpu= (float)qh_CPUclock - (float)qh hulltime;
    -    cpu /= (float)qh_SECticks;
    -    total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
    -    qh_fprintf(qh ferr, 8118, "\n\
    -At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
    - The current hull contains %d facets and %d vertices.  Last point was p%d\n",
    -      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
    -      total, qh num_facets, qh num_vertices, qh furthest_id);
    -    return;
    -  }
    -  furthestid= qh_pointid(furthest);
    -  if (qh TRACEpoint == furthestid) {
    -    qh IStracing= qh TRACElevel;
    -    qhmem.IStracing= qh TRACElevel;
    -  }else if (qh TRACEpoint != -1 && qh TRACEdist < REALmax/2) {
    -    qh IStracing= 0;
    -    qhmem.IStracing= 0;
    -  }
    -  if (qh REPORTfreq && (qh facet_id-1 > qh lastreport+qh REPORTfreq)) {
    -    qh lastreport= qh facet_id-1;
    -    time(&timedata);
    -    tp= localtime(&timedata);
    -    cpu= (float)qh_CPUclock - (float)qh hulltime;
    -    cpu /= (float)qh_SECticks;
    -    total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
    -    zinc_(Zdistio);
    -    qh_distplane(furthest, facet, &dist);
    -    qh_fprintf(qh ferr, 8119, "\n\
    -At %02d:%02d:%02d & %2.5g CPU secs, qhull has created %d facets and merged %d.\n\
    - The current hull contains %d facets and %d vertices.  There are %d\n\
    - outside points.  Next is point p%d(v%d), %2.2g above f%d.\n",
    -      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, qh facet_id -1,
    -      total, qh num_facets, qh num_vertices, qh num_outside+1,
    -      furthestid, qh vertex_id, dist, getid_(facet));
    -  }else if (qh IStracing >=1) {
    -    cpu= (float)qh_CPUclock - (float)qh hulltime;
    -    cpu /= (float)qh_SECticks;
    -    qh_distplane(furthest, facet, &dist);
    -    qh_fprintf(qh ferr, 8120, "qh_addpoint: add p%d(v%d) to hull of %d facets(%2.2g above f%d) and %d outside at %4.4g CPU secs.  Previous was p%d.\n",
    -      furthestid, qh vertex_id, qh num_facets, dist,
    -      getid_(facet), qh num_outside+1, cpu, qh furthest_id);
    -  }
    -  zmax_(Zvisit2max, (int)qh visit_id/2);
    -  if (qh visit_id > (unsigned) INT_MAX) {
    -    zinc_(Zvisit);
    -    qh visit_id= 0;
    -    FORALLfacets
    -      facet->visitid= 0;
    -  }
    -  zmax_(Zvvisit2max, (int)qh vertex_visit/2);
    -  if (qh vertex_visit > (unsigned) INT_MAX/2) { /* 31 bits */
    -    zinc_(Zvvisit);
    -    qh vertex_visit= 0;
    -    FORALLvertices
    -      vertex->visitid= 0;
    -  }
    -  qh furthest_id= furthestid;
    -  qh RANDOMdist= qh old_randomdist;
    -} /* buildtracing */
    -
    -/*---------------------------------
    -
    -  qh_errexit2( exitcode, facet, otherfacet )
    -    return exitcode to system after an error
    -    report two facets
    -
    -  returns:
    -    assumes exitcode non-zero
    -
    -  see:
    -    normally use qh_errexit() in user.c(reports a facet and a ridge)
    -*/
    -void qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet) {
    -
    -  qh_errprint("ERRONEOUS", facet, otherfacet, NULL, NULL);
    -  qh_errexit(exitcode, NULL, NULL);
    -} /* errexit2 */
    -
    -
    -/*---------------------------------
    -
    -  qh_findhorizon( point, facet, goodvisible, goodhorizon )
    -    given a visible facet, find the point's horizon and visible facets
    -    for all facets, !facet-visible
    -
    -  returns:
    -    returns qh.visible_list/num_visible with all visible facets
    -      marks visible facets with ->visible
    -    updates count of good visible and good horizon facets
    -    updates qh.max_outside, qh.max_vertex, facet->maxoutside
    -
    -  see:
    -    similar to qh_delpoint()
    -
    -  design:
    -    move facet to qh.visible_list at end of qh.facet_list
    -    for all visible facets
    -     for each unvisited neighbor of a visible facet
    -       compute distance of point to neighbor
    -       if point above neighbor
    -         move neighbor to end of qh.visible_list
    -       else if point is coplanar with neighbor
    -         update qh.max_outside, qh.max_vertex, neighbor->maxoutside
    -         mark neighbor coplanar (will create a samecycle later)
    -         update horizon statistics
    -*/
    -void qh_findhorizon(pointT *point, facetT *facet, int *goodvisible, int *goodhorizon) {
    -  facetT *neighbor, **neighborp, *visible;
    -  int numhorizon= 0, coplanar= 0;
    -  realT dist;
    -
    -  trace1((qh ferr, 1040,"qh_findhorizon: find horizon for point p%d facet f%d\n",qh_pointid(point),facet->id));
    -  *goodvisible= *goodhorizon= 0;
    -  zinc_(Ztotvisible);
    -  qh_removefacet(facet);  /* visible_list at end of qh facet_list */
    -  qh_appendfacet(facet);
    -  qh num_visible= 1;
    -  if (facet->good)
    -    (*goodvisible)++;
    -  qh visible_list= facet;
    -  facet->visible= True;
    -  facet->f.replace= NULL;
    -  if (qh IStracing >=4)
    -    qh_errprint("visible", facet, NULL, NULL, NULL);
    -  qh visit_id++;
    -  FORALLvisible_facets {
    -    if (visible->tricoplanar && !qh TRInormals) {
    -      qh_fprintf(qh ferr, 6230, "Qhull internal error (qh_findhorizon): does not work for tricoplanar facets.  Use option 'Q11'\n");
    -      qh_errexit(qh_ERRqhull, visible, NULL);
    -    }
    -    visible->visitid= qh visit_id;
    -    FOREACHneighbor_(visible) {
    -      if (neighbor->visitid == qh visit_id)
    -        continue;
    -      neighbor->visitid= qh visit_id;
    -      zzinc_(Znumvisibility);
    -      qh_distplane(point, neighbor, &dist);
    -      if (dist > qh MINvisible) {
    -        zinc_(Ztotvisible);
    -        qh_removefacet(neighbor);  /* append to end of qh visible_list */
    -        qh_appendfacet(neighbor);
    -        neighbor->visible= True;
    -        neighbor->f.replace= NULL;
    -        qh num_visible++;
    -        if (neighbor->good)
    -          (*goodvisible)++;
    -        if (qh IStracing >=4)
    -          qh_errprint("visible", neighbor, NULL, NULL, NULL);
    -      }else {
    -        if (dist > - qh MAXcoplanar) {
    -          neighbor->coplanar= True;
    -          zzinc_(Zcoplanarhorizon);
    -          qh_precision("coplanar horizon");
    -          coplanar++;
    -          if (qh MERGING) {
    -            if (dist > 0) {
    -              maximize_(qh max_outside, dist);
    -              maximize_(qh max_vertex, dist);
    -#if qh_MAXoutside
    -              maximize_(neighbor->maxoutside, dist);
    -#endif
    -            }else
    -              minimize_(qh min_vertex, dist);  /* due to merge later */
    -          }
    -          trace2((qh ferr, 2057, "qh_findhorizon: point p%d is coplanar to horizon f%d, dist=%2.7g < qh MINvisible(%2.7g)\n",
    -              qh_pointid(point), neighbor->id, dist, qh MINvisible));
    -        }else
    -          neighbor->coplanar= False;
    -        zinc_(Ztothorizon);
    -        numhorizon++;
    -        if (neighbor->good)
    -          (*goodhorizon)++;
    -        if (qh IStracing >=4)
    -          qh_errprint("horizon", neighbor, NULL, NULL, NULL);
    -      }
    -    }
    -  }
    -  if (!numhorizon) {
    -    qh_precision("empty horizon");
    -    qh_fprintf(qh ferr, 6168, "qhull precision error (qh_findhorizon): empty horizon\n\
    -QhullPoint p%d was above all facets.\n", qh_pointid(point));
    -    qh_printfacetlist(qh facet_list, NULL, True);
    -    qh_errexit(qh_ERRprec, NULL, NULL);
    -  }
    -  trace1((qh ferr, 1041, "qh_findhorizon: %d horizon facets(good %d), %d visible(good %d), %d coplanar\n",
    -       numhorizon, *goodhorizon, qh num_visible, *goodvisible, coplanar));
    -  if (qh IStracing >= 4 && qh num_facets < 50)
    -    qh_printlists();
    -} /* findhorizon */
    -
    -/*---------------------------------
    -
    -  qh_nextfurthest( visible )
    -    returns next furthest point and visible facet for qh_addpoint()
    -    starts search at qh.facet_next
    -
    -  returns:
    -    removes furthest point from outside set
    -    NULL if none available
    -    advances qh.facet_next over facets with empty outside sets
    -
    -  design:
    -    for each facet from qh.facet_next
    -      if empty outside set
    -        advance qh.facet_next
    -      else if qh.NARROWhull
    -        determine furthest outside point
    -        if furthest point is not outside
    -          advance qh.facet_next(point will be coplanar)
    -    remove furthest point from outside set
    -*/
    -pointT *qh_nextfurthest(facetT **visible) {
    -  facetT *facet;
    -  int size, idx;
    -  realT randr, dist;
    -  pointT *furthest;
    -
    -  while ((facet= qh facet_next) != qh facet_tail) {
    -    if (!facet->outsideset) {
    -      qh facet_next= facet->next;
    -      continue;
    -    }
    -    SETreturnsize_(facet->outsideset, size);
    -    if (!size) {
    -      qh_setfree(&facet->outsideset);
    -      qh facet_next= facet->next;
    -      continue;
    -    }
    -    if (qh NARROWhull) {
    -      if (facet->notfurthest)
    -        qh_furthestout(facet);
    -      furthest= (pointT*)qh_setlast(facet->outsideset);
    -#if qh_COMPUTEfurthest
    -      qh_distplane(furthest, facet, &dist);
    -      zinc_(Zcomputefurthest);
    -#else
    -      dist= facet->furthestdist;
    -#endif
    -      if (dist < qh MINoutside) { /* remainder of outside set is coplanar for qh_outcoplanar */
    -        qh facet_next= facet->next;
    -        continue;
    -      }
    -    }
    -    if (!qh RANDOMoutside && !qh VIRTUALmemory) {
    -      if (qh PICKfurthest) {
    -        qh_furthestnext(/* qh facet_list */);
    -        facet= qh facet_next;
    -      }
    -      *visible= facet;
    -      return((pointT*)qh_setdellast(facet->outsideset));
    -    }
    -    if (qh RANDOMoutside) {
    -      int outcoplanar = 0;
    -      if (qh NARROWhull) {
    -        FORALLfacets {
    -          if (facet == qh facet_next)
    -            break;
    -          if (facet->outsideset)
    -            outcoplanar += qh_setsize( facet->outsideset);
    -        }
    -      }
    -      randr= qh_RANDOMint;
    -      randr= randr/(qh_RANDOMmax+1);
    -      idx= (int)floor((qh num_outside - outcoplanar) * randr);
    -      FORALLfacet_(qh facet_next) {
    -        if (facet->outsideset) {
    -          SETreturnsize_(facet->outsideset, size);
    -          if (!size)
    -            qh_setfree(&facet->outsideset);
    -          else if (size > idx) {
    -            *visible= facet;
    -            return((pointT*)qh_setdelnth(facet->outsideset, idx));
    -          }else
    -            idx -= size;
    -        }
    -      }
    -      qh_fprintf(qh ferr, 6169, "qhull internal error (qh_nextfurthest): num_outside %d is too low\nby at least %d, or a random real %g >= 1.0\n",
    -              qh num_outside, idx+1, randr);
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }else { /* VIRTUALmemory */
    -      facet= qh facet_tail->previous;
    -      if (!(furthest= (pointT*)qh_setdellast(facet->outsideset))) {
    -        if (facet->outsideset)
    -          qh_setfree(&facet->outsideset);
    -        qh_removefacet(facet);
    -        qh_prependfacet(facet, &qh facet_list);
    -        continue;
    -      }
    -      *visible= facet;
    -      return furthest;
    -    }
    -  }
    -  return NULL;
    -} /* nextfurthest */
    -
    -/*---------------------------------
    -
    -  qh_partitionall( vertices, points, numpoints )
    -    partitions all points in points/numpoints to the outsidesets of facets
    -    vertices= vertices in qh.facet_list(!partitioned)
    -
    -  returns:
    -    builds facet->outsideset
    -    does not partition qh.GOODpoint
    -    if qh.ONLYgood && !qh.MERGING,
    -      does not partition qh.GOODvertex
    -
    -  notes:
    -    faster if qh.facet_list sorted by anticipated size of outside set
    -
    -  design:
    -    initialize pointset with all points
    -    remove vertices from pointset
    -    remove qh.GOODpointp from pointset (unless it's qh.STOPcone or qh.STOPpoint)
    -    for all facets
    -      for all remaining points in pointset
    -        compute distance from point to facet
    -        if point is outside facet
    -          remove point from pointset (by not reappending)
    -          update bestpoint
    -          append point or old bestpoint to facet's outside set
    -      append bestpoint to facet's outside set (furthest)
    -    for all points remaining in pointset
    -      partition point into facets' outside sets and coplanar sets
    -*/
    -void qh_partitionall(setT *vertices, pointT *points, int numpoints){
    -  setT *pointset;
    -  vertexT *vertex, **vertexp;
    -  pointT *point, **pointp, *bestpoint;
    -  int size, point_i, point_n, point_end, remaining, i, id;
    -  facetT *facet;
    -  realT bestdist= -REALmax, dist, distoutside;
    -
    -  trace1((qh ferr, 1042, "qh_partitionall: partition all points into outside sets\n"));
    -  pointset= qh_settemp(numpoints);
    -  qh num_outside= 0;
    -  pointp= SETaddr_(pointset, pointT);
    -  for (i=numpoints, point= points; i--; point += qh hull_dim)
    -    *(pointp++)= point;
    -  qh_settruncate(pointset, numpoints);
    -  FOREACHvertex_(vertices) {
    -    if ((id= qh_pointid(vertex->point)) >= 0)
    -      SETelem_(pointset, id)= NULL;
    -  }
    -  id= qh_pointid(qh GOODpointp);
    -  if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
    -    SETelem_(pointset, id)= NULL;
    -  if (qh GOODvertexp && qh ONLYgood && !qh MERGING) { /* matches qhull()*/
    -    if ((id= qh_pointid(qh GOODvertexp)) >= 0)
    -      SETelem_(pointset, id)= NULL;
    -  }
    -  if (!qh BESToutside) {  /* matches conditional for qh_partitionpoint below */
    -    distoutside= qh_DISToutside; /* multiple of qh.MINoutside & qh.max_outside, see user.h */
    -    zval_(Ztotpartition)= qh num_points - qh hull_dim - 1; /*misses GOOD... */
    -    remaining= qh num_facets;
    -    point_end= numpoints;
    -    FORALLfacets {
    -      size= point_end/(remaining--) + 100;
    -      facet->outsideset= qh_setnew(size);
    -      bestpoint= NULL;
    -      point_end= 0;
    -      FOREACHpoint_i_(pointset) {
    -        if (point) {
    -          zzinc_(Zpartitionall);
    -          qh_distplane(point, facet, &dist);
    -          if (dist < distoutside)
    -            SETelem_(pointset, point_end++)= point;
    -          else {
    -            qh num_outside++;
    -            if (!bestpoint) {
    -              bestpoint= point;
    -              bestdist= dist;
    -            }else if (dist > bestdist) {
    -              qh_setappend(&facet->outsideset, bestpoint);
    -              bestpoint= point;
    -              bestdist= dist;
    -            }else
    -              qh_setappend(&facet->outsideset, point);
    -          }
    -        }
    -      }
    -      if (bestpoint) {
    -        qh_setappend(&facet->outsideset, bestpoint);
    -#if !qh_COMPUTEfurthest
    -        facet->furthestdist= bestdist;
    -#endif
    -      }else
    -        qh_setfree(&facet->outsideset);
    -      qh_settruncate(pointset, point_end);
    -    }
    -  }
    -  /* if !qh BESToutside, pointset contains points not assigned to outsideset */
    -  if (qh BESToutside || qh MERGING || qh KEEPcoplanar || qh KEEPinside) {
    -    qh findbestnew= True;
    -    FOREACHpoint_i_(pointset) {
    -      if (point)
    -        qh_partitionpoint(point, qh facet_list);
    -    }
    -    qh findbestnew= False;
    -  }
    -  zzadd_(Zpartitionall, zzval_(Zpartition));
    -  zzval_(Zpartition)= 0;
    -  qh_settempfree(&pointset);
    -  if (qh IStracing >= 4)
    -    qh_printfacetlist(qh facet_list, NULL, True);
    -} /* partitionall */
    -
    -
    -/*---------------------------------
    -
    -  qh_partitioncoplanar( point, facet, dist )
    -    partition coplanar point to a facet
    -    dist is distance from point to facet
    -    if dist NULL,
    -      searches for bestfacet and does nothing if inside
    -    if qh.findbestnew set,
    -      searches new facets instead of using qh_findbest()
    -
    -  returns:
    -    qh.max_ouside updated
    -    if qh.KEEPcoplanar or qh.KEEPinside
    -      point assigned to best coplanarset
    -
    -  notes:
    -    facet->maxoutside is updated at end by qh_check_maxout
    -
    -  design:
    -    if dist undefined
    -      find best facet for point
    -      if point sufficiently below facet (depends on qh.NEARinside and qh.KEEPinside)
    -        exit
    -    if keeping coplanar/nearinside/inside points
    -      if point is above furthest coplanar point
    -        append point to coplanar set (it is the new furthest)
    -        update qh.max_outside
    -      else
    -        append point one before end of coplanar set
    -    else if point is clearly outside of qh.max_outside and bestfacet->coplanarset
    -    and bestfacet is more than perpendicular to facet
    -      repartition the point using qh_findbest() -- it may be put on an outsideset
    -    else
    -      update qh.max_outside
    -*/
    -void qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist) {
    -  facetT *bestfacet;
    -  pointT *oldfurthest;
    -  realT bestdist, dist2= 0, angle;
    -  int numpart= 0, oldfindbest;
    -  boolT isoutside;
    -
    -  qh WAScoplanar= True;
    -  if (!dist) {
    -    if (qh findbestnew)
    -      bestfacet= qh_findbestnew(point, facet, &bestdist, qh_ALL, &isoutside, &numpart);
    -    else
    -      bestfacet= qh_findbest(point, facet, qh_ALL, !qh_ISnewfacets, qh DELAUNAY,
    -                          &bestdist, &isoutside, &numpart);
    -    zinc_(Ztotpartcoplanar);
    -    zzadd_(Zpartcoplanar, numpart);
    -    if (!qh DELAUNAY && !qh KEEPinside) { /*  for 'd', bestdist skips upperDelaunay facets */
    -      if (qh KEEPnearinside) {
    -        if (bestdist < -qh NEARinside) {
    -          zinc_(Zcoplanarinside);
    -          trace4((qh ferr, 4062, "qh_partitioncoplanar: point p%d is more than near-inside facet f%d dist %2.2g findbestnew %d\n",
    -                  qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
    -          return;
    -        }
    -      }else if (bestdist < -qh MAXcoplanar) {
    -          trace4((qh ferr, 4063, "qh_partitioncoplanar: point p%d is inside facet f%d dist %2.2g findbestnew %d\n",
    -                  qh_pointid(point), bestfacet->id, bestdist, qh findbestnew));
    -        zinc_(Zcoplanarinside);
    -        return;
    -      }
    -    }
    -  }else {
    -    bestfacet= facet;
    -    bestdist= *dist;
    -  }
    -  if (bestdist > qh max_outside) {
    -    if (!dist && facet != bestfacet) {
    -      zinc_(Zpartangle);
    -      angle= qh_getangle(facet->normal, bestfacet->normal);
    -      if (angle < 0) {
    -        /* typically due to deleted vertex and coplanar facets, e.g.,
    -             RBOX 1000 s Z1 G1e-13 t1001185205 | QHULL Tv */
    -        zinc_(Zpartflip);
    -        trace2((qh ferr, 2058, "qh_partitioncoplanar: repartition point p%d from f%d.  It is above flipped facet f%d dist %2.2g\n",
    -                qh_pointid(point), facet->id, bestfacet->id, bestdist));
    -        oldfindbest= qh findbestnew;
    -        qh findbestnew= False;
    -        qh_partitionpoint(point, bestfacet);
    -        qh findbestnew= oldfindbest;
    -        return;
    -      }
    -    }
    -    qh max_outside= bestdist;
    -    if (bestdist > qh TRACEdist) {
    -      qh_fprintf(qh ferr, 8122, "qh_partitioncoplanar: ====== p%d from f%d increases max_outside to %2.2g of f%d last p%d\n",
    -                     qh_pointid(point), facet->id, bestdist, bestfacet->id, qh furthest_id);
    -      qh_errprint("DISTANT", facet, bestfacet, NULL, NULL);
    -    }
    -  }
    -  if (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside) {
    -    oldfurthest= (pointT*)qh_setlast(bestfacet->coplanarset);
    -    if (oldfurthest) {
    -      zinc_(Zcomputefurthest);
    -      qh_distplane(oldfurthest, bestfacet, &dist2);
    -    }
    -    if (!oldfurthest || dist2 < bestdist)
    -      qh_setappend(&bestfacet->coplanarset, point);
    -    else
    -      qh_setappend2ndlast(&bestfacet->coplanarset, point);
    -  }
    -  trace4((qh ferr, 4064, "qh_partitioncoplanar: point p%d is coplanar with facet f%d(or inside) dist %2.2g\n",
    -          qh_pointid(point), bestfacet->id, bestdist));
    -} /* partitioncoplanar */
    -
    -/*---------------------------------
    -
    -  qh_partitionpoint( point, facet )
    -    assigns point to an outside set, coplanar set, or inside set (i.e., dropt)
    -    if qh.findbestnew
    -      uses qh_findbestnew() to search all new facets
    -    else
    -      uses qh_findbest()
    -
    -  notes:
    -    after qh_distplane(), this and qh_findbest() are most expensive in 3-d
    -
    -  design:
    -    find best facet for point
    -      (either exhaustive search of new facets or directed search from facet)
    -    if qh.NARROWhull
    -      retain coplanar and nearinside points as outside points
    -    if point is outside bestfacet
    -      if point above furthest point for bestfacet
    -        append point to outside set (it becomes the new furthest)
    -        if outside set was empty
    -          move bestfacet to end of qh.facet_list (i.e., after qh.facet_next)
    -        update bestfacet->furthestdist
    -      else
    -        append point one before end of outside set
    -    else if point is coplanar to bestfacet
    -      if keeping coplanar points or need to update qh.max_outside
    -        partition coplanar point into bestfacet
    -    else if near-inside point
    -      partition as coplanar point into bestfacet
    -    else is an inside point
    -      if keeping inside points
    -        partition as coplanar point into bestfacet
    -*/
    -void qh_partitionpoint(pointT *point, facetT *facet) {
    -  realT bestdist;
    -  boolT isoutside;
    -  facetT *bestfacet;
    -  int numpart;
    -#if qh_COMPUTEfurthest
    -  realT dist;
    -#endif
    -
    -  if (qh findbestnew)
    -    bestfacet= qh_findbestnew(point, facet, &bestdist, qh BESToutside, &isoutside, &numpart);
    -  else
    -    bestfacet= qh_findbest(point, facet, qh BESToutside, qh_ISnewfacets, !qh_NOupper,
    -                          &bestdist, &isoutside, &numpart);
    -  zinc_(Ztotpartition);
    -  zzadd_(Zpartition, numpart);
    -  if (qh NARROWhull) {
    -    if (qh DELAUNAY && !isoutside && bestdist >= -qh MAXcoplanar)
    -      qh_precision("nearly incident point(narrow hull)");
    -    if (qh KEEPnearinside) {
    -      if (bestdist >= -qh NEARinside)
    -        isoutside= True;
    -    }else if (bestdist >= -qh MAXcoplanar)
    -      isoutside= True;
    -  }
    -
    -  if (isoutside) {
    -    if (!bestfacet->outsideset
    -    || !qh_setlast(bestfacet->outsideset)) {
    -      qh_setappend(&(bestfacet->outsideset), point);
    -      if (!bestfacet->newfacet) {
    -        qh_removefacet(bestfacet);  /* make sure it's after qh facet_next */
    -        qh_appendfacet(bestfacet);
    -      }
    -#if !qh_COMPUTEfurthest
    -      bestfacet->furthestdist= bestdist;
    -#endif
    -    }else {
    -#if qh_COMPUTEfurthest
    -      zinc_(Zcomputefurthest);
    -      qh_distplane(oldfurthest, bestfacet, &dist);
    -      if (dist < bestdist)
    -        qh_setappend(&(bestfacet->outsideset), point);
    -      else
    -        qh_setappend2ndlast(&(bestfacet->outsideset), point);
    -#else
    -      if (bestfacet->furthestdist < bestdist) {
    -        qh_setappend(&(bestfacet->outsideset), point);
    -        bestfacet->furthestdist= bestdist;
    -      }else
    -        qh_setappend2ndlast(&(bestfacet->outsideset), point);
    -#endif
    -    }
    -    qh num_outside++;
    -    trace4((qh ferr, 4065, "qh_partitionpoint: point p%d is outside facet f%d new? %d (or narrowhull)\n",
    -          qh_pointid(point), bestfacet->id, bestfacet->newfacet));
    -  }else if (qh DELAUNAY || bestdist >= -qh MAXcoplanar) { /* for 'd', bestdist skips upperDelaunay facets */
    -    zzinc_(Zcoplanarpart);
    -    if (qh DELAUNAY)
    -      qh_precision("nearly incident point");
    -    if ((qh KEEPcoplanar + qh KEEPnearinside) || bestdist > qh max_outside)
    -      qh_partitioncoplanar(point, bestfacet, &bestdist);
    -    else {
    -      trace4((qh ferr, 4066, "qh_partitionpoint: point p%d is coplanar to facet f%d (dropped)\n",
    -          qh_pointid(point), bestfacet->id));
    -    }
    -  }else if (qh KEEPnearinside && bestdist > -qh NEARinside) {
    -    zinc_(Zpartnear);
    -    qh_partitioncoplanar(point, bestfacet, &bestdist);
    -  }else {
    -    zinc_(Zpartinside);
    -    trace4((qh ferr, 4067, "qh_partitionpoint: point p%d is inside all facets, closest to f%d dist %2.2g\n",
    -          qh_pointid(point), bestfacet->id, bestdist));
    -    if (qh KEEPinside)
    -      qh_partitioncoplanar(point, bestfacet, &bestdist);
    -  }
    -} /* partitionpoint */
    -
    -/*---------------------------------
    -
    -  qh_partitionvisible( allpoints, numoutside )
    -    partitions points in visible facets to qh.newfacet_list
    -    qh.visible_list= visible facets
    -    for visible facets
    -      1st neighbor (if any) points to a horizon facet or a new facet
    -    if allpoints(!used),
    -      repartitions coplanar points
    -
    -  returns:
    -    updates outside sets and coplanar sets of qh.newfacet_list
    -    updates qh.num_outside (count of outside points)
    -
    -  notes:
    -    qh.findbest_notsharp should be clear (extra work if set)
    -
    -  design:
    -    for all visible facets with outside set or coplanar set
    -      select a newfacet for visible facet
    -      if outside set
    -        partition outside set into new facets
    -      if coplanar set and keeping coplanar/near-inside/inside points
    -        if allpoints
    -          partition coplanar set into new facets, may be assigned outside
    -        else
    -          partition coplanar set into coplanar sets of new facets
    -    for each deleted vertex
    -      if allpoints
    -        partition vertex into new facets, may be assigned outside
    -      else
    -        partition vertex into coplanar sets of new facets
    -*/
    -void qh_partitionvisible(/*visible_list*/ boolT allpoints, int *numoutside) {
    -  facetT *visible, *newfacet;
    -  pointT *point, **pointp;
    -  int coplanar=0, size;
    -  unsigned count;
    -  vertexT *vertex, **vertexp;
    -
    -  if (qh ONLYmax)
    -    maximize_(qh MINoutside, qh max_vertex);
    -  *numoutside= 0;
    -  FORALLvisible_facets {
    -    if (!visible->outsideset && !visible->coplanarset)
    -      continue;
    -    newfacet= visible->f.replace;
    -    count= 0;
    -    while (newfacet && newfacet->visible) {
    -      newfacet= newfacet->f.replace;
    -      if (count++ > qh facet_id)
    -        qh_infiniteloop(visible);
    -    }
    -    if (!newfacet)
    -      newfacet= qh newfacet_list;
    -    if (newfacet == qh facet_tail) {
    -      qh_fprintf(qh ferr, 6170, "qhull precision error (qh_partitionvisible): all new facets deleted as\n        degenerate facets. Can not continue.\n");
    -      qh_errexit(qh_ERRprec, NULL, NULL);
    -    }
    -    if (visible->outsideset) {
    -      size= qh_setsize(visible->outsideset);
    -      *numoutside += size;
    -      qh num_outside -= size;
    -      FOREACHpoint_(visible->outsideset)
    -        qh_partitionpoint(point, newfacet);
    -    }
    -    if (visible->coplanarset && (qh KEEPcoplanar + qh KEEPinside + qh KEEPnearinside)) {
    -      size= qh_setsize(visible->coplanarset);
    -      coplanar += size;
    -      FOREACHpoint_(visible->coplanarset) {
    -        if (allpoints) /* not used */
    -          qh_partitionpoint(point, newfacet);
    -        else
    -          qh_partitioncoplanar(point, newfacet, NULL);
    -      }
    -    }
    -  }
    -  FOREACHvertex_(qh del_vertices) {
    -    if (vertex->point) {
    -      if (allpoints) /* not used */
    -        qh_partitionpoint(vertex->point, qh newfacet_list);
    -      else
    -        qh_partitioncoplanar(vertex->point, qh newfacet_list, NULL);
    -    }
    -  }
    -  trace1((qh ferr, 1043,"qh_partitionvisible: partitioned %d points from outsidesets and %d points from coplanarsets\n", *numoutside, coplanar));
    -} /* partitionvisible */
    -
    -
    -
    -/*---------------------------------
    -
    -  qh_precision( reason )
    -    restart on precision errors if not merging and if 'QJn'
    -*/
    -void qh_precision(const char *reason) {
    -
    -  if (qh ALLOWrestart && !qh PREmerge && !qh MERGEexact) {
    -    if (qh JOGGLEmax < REALmax/2) {
    -      trace0((qh ferr, 26, "qh_precision: qhull restart because of %s\n", reason));
    -      longjmp(qh restartexit, qh_ERRprec);
    -    }
    -  }
    -} /* qh_precision */
    -
    -/*---------------------------------
    -
    -  qh_printsummary( fp )
    -    prints summary to fp
    -
    -  notes:
    -    not in io.c so that user_eg.c can prevent io.c from loading
    -    qh_printsummary and qh_countfacets must match counts
    -
    -  design:
    -    determine number of points, vertices, and coplanar points
    -    print summary
    -*/
    -void qh_printsummary(FILE *fp) {
    -  realT ratio, outerplane, innerplane;
    -  float cpu;
    -  int size, id, nummerged, numvertices, numcoplanars= 0, nonsimplicial=0;
    -  int goodused;
    -  facetT *facet;
    -  const char *s;
    -  int numdel= zzval_(Zdelvertextot);
    -  int numtricoplanars= 0;
    -
    -  size= qh num_points + qh_setsize(qh other_points);
    -  numvertices= qh num_vertices - qh_setsize(qh del_vertices);
    -  id= qh_pointid(qh GOODpointp);
    -  FORALLfacets {
    -    if (facet->coplanarset)
    -      numcoplanars += qh_setsize( facet->coplanarset);
    -    if (facet->good) {
    -      if (facet->simplicial) {
    -        if (facet->keepcentrum && facet->tricoplanar)
    -          numtricoplanars++;
    -      }else if (qh_setsize(facet->vertices) != qh hull_dim)
    -        nonsimplicial++;
    -    }
    -  }
    -  if (id >=0 && qh STOPcone-1 != id && -qh STOPpoint-1 != id)
    -    size--;
    -  if (qh STOPcone || qh STOPpoint)
    -      qh_fprintf(fp, 9288, "\nAt a premature exit due to 'TVn', 'TCn', 'TRn', or precision error with 'QJn'.");
    -  if (qh UPPERdelaunay)
    -    goodused= qh GOODvertex + qh GOODpoint + qh SPLITthresholds;
    -  else if (qh DELAUNAY)
    -    goodused= qh GOODvertex + qh GOODpoint + qh GOODthreshold;
    -  else
    -    goodused= qh num_good;
    -  nummerged= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
    -  if (qh VORONOI) {
    -    if (qh UPPERdelaunay)
    -      qh_fprintf(fp, 9289, "\n\
    -Furthest-site Voronoi vertices by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    else
    -      qh_fprintf(fp, 9290, "\n\
    -Voronoi diagram by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    qh_fprintf(fp, 9291, "  Number of Voronoi regions%s: %d\n",
    -              qh ATinfinity ? " and at-infinity" : "", numvertices);
    -    if (numdel)
    -      qh_fprintf(fp, 9292, "  Total number of deleted points due to merging: %d\n", numdel);
    -    if (numcoplanars - numdel > 0)
    -      qh_fprintf(fp, 9293, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    -    else if (size - numvertices - numdel > 0)
    -      qh_fprintf(fp, 9294, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    -    qh_fprintf(fp, 9295, "  Number of%s Voronoi vertices: %d\n",
    -              goodused ? " 'good'" : "", qh num_good);
    -    if (nonsimplicial)
    -      qh_fprintf(fp, 9296, "  Number of%s non-simplicial Voronoi vertices: %d\n",
    -              goodused ? " 'good'" : "", nonsimplicial);
    -  }else if (qh DELAUNAY) {
    -    if (qh UPPERdelaunay)
    -      qh_fprintf(fp, 9297, "\n\
    -Furthest-site Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    else
    -      qh_fprintf(fp, 9298, "\n\
    -Delaunay triangulation by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    qh_fprintf(fp, 9299, "  Number of input sites%s: %d\n",
    -              qh ATinfinity ? " and at-infinity" : "", numvertices);
    -    if (numdel)
    -      qh_fprintf(fp, 9300, "  Total number of deleted points due to merging: %d\n", numdel);
    -    if (numcoplanars - numdel > 0)
    -      qh_fprintf(fp, 9301, "  Number of nearly incident points: %d\n", numcoplanars - numdel);
    -    else if (size - numvertices - numdel > 0)
    -      qh_fprintf(fp, 9302, "  Total number of nearly incident points: %d\n", size - numvertices - numdel);
    -    qh_fprintf(fp, 9303, "  Number of%s Delaunay regions: %d\n",
    -              goodused ? " 'good'" : "", qh num_good);
    -    if (nonsimplicial)
    -      qh_fprintf(fp, 9304, "  Number of%s non-simplicial Delaunay regions: %d\n",
    -              goodused ? " 'good'" : "", nonsimplicial);
    -  }else if (qh HALFspace) {
    -    qh_fprintf(fp, 9305, "\n\
    -Halfspace intersection by the convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    qh_fprintf(fp, 9306, "  Number of halfspaces: %d\n", size);
    -    qh_fprintf(fp, 9307, "  Number of non-redundant halfspaces: %d\n", numvertices);
    -    if (numcoplanars) {
    -      if (qh KEEPinside && qh KEEPcoplanar)
    -        s= "similar and redundant";
    -      else if (qh KEEPinside)
    -        s= "redundant";
    -      else
    -        s= "similar";
    -      qh_fprintf(fp, 9308, "  Number of %s halfspaces: %d\n", s, numcoplanars);
    -    }
    -    qh_fprintf(fp, 9309, "  Number of intersection points: %d\n", qh num_facets - qh num_visible);
    -    if (goodused)
    -      qh_fprintf(fp, 9310, "  Number of 'good' intersection points: %d\n", qh num_good);
    -    if (nonsimplicial)
    -      qh_fprintf(fp, 9311, "  Number of%s non-simplicial intersection points: %d\n",
    -              goodused ? " 'good'" : "", nonsimplicial);
    -  }else {
    -    qh_fprintf(fp, 9312, "\n\
    -Convex hull of %d points in %d-d:\n\n", size, qh hull_dim);
    -    qh_fprintf(fp, 9313, "  Number of vertices: %d\n", numvertices);
    -    if (numcoplanars) {
    -      if (qh KEEPinside && qh KEEPcoplanar)
    -        s= "coplanar and interior";
    -      else if (qh KEEPinside)
    -        s= "interior";
    -      else
    -        s= "coplanar";
    -      qh_fprintf(fp, 9314, "  Number of %s points: %d\n", s, numcoplanars);
    -    }
    -    qh_fprintf(fp, 9315, "  Number of facets: %d\n", qh num_facets - qh num_visible);
    -    if (goodused)
    -      qh_fprintf(fp, 9316, "  Number of 'good' facets: %d\n", qh num_good);
    -    if (nonsimplicial)
    -      qh_fprintf(fp, 9317, "  Number of%s non-simplicial facets: %d\n",
    -              goodused ? " 'good'" : "", nonsimplicial);
    -  }
    -  if (numtricoplanars)
    -      qh_fprintf(fp, 9318, "  Number of triangulated facets: %d\n", numtricoplanars);
    -  qh_fprintf(fp, 9319, "\nStatistics for: %s | %s",
    -                      qh rbox_command, qh qhull_command);
    -  if (qh ROTATErandom != INT_MIN)
    -    qh_fprintf(fp, 9320, " QR%d\n\n", qh ROTATErandom);
    -  else
    -    qh_fprintf(fp, 9321, "\n\n");
    -  qh_fprintf(fp, 9322, "  Number of points processed: %d\n", zzval_(Zprocessed));
    -  qh_fprintf(fp, 9323, "  Number of hyperplanes created: %d\n", zzval_(Zsetplane));
    -  if (qh DELAUNAY)
    -    qh_fprintf(fp, 9324, "  Number of facets in hull: %d\n", qh num_facets - qh num_visible);
    -  qh_fprintf(fp, 9325, "  Number of distance tests for qhull: %d\n", zzval_(Zpartition)+
    -      zzval_(Zpartitionall)+zzval_(Znumvisibility)+zzval_(Zpartcoplanar));
    -#if 0  /* NOTE: must print before printstatistics() */
    -  {realT stddev, ave;
    -  qh_fprintf(fp, 9326, "  average new facet balance: %2.2g\n",
    -          wval_(Wnewbalance)/zval_(Zprocessed));
    -  stddev= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
    -                                 wval_(Wnewbalance2), &ave);
    -  qh_fprintf(fp, 9327, "  new facet standard deviation: %2.2g\n", stddev);
    -  qh_fprintf(fp, 9328, "  average partition balance: %2.2g\n",
    -          wval_(Wpbalance)/zval_(Zpbalance));
    -  stddev= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
    -                                 wval_(Wpbalance2), &ave);
    -  qh_fprintf(fp, 9329, "  partition standard deviation: %2.2g\n", stddev);
    -  }
    -#endif
    -  if (nummerged) {
    -    qh_fprintf(fp, 9330,"  Number of distance tests for merging: %d\n",zzval_(Zbestdist)+
    -          zzval_(Zcentrumtests)+zzval_(Zdistconvex)+zzval_(Zdistcheck)+
    -          zzval_(Zdistzero));
    -    qh_fprintf(fp, 9331,"  Number of distance tests for checking: %d\n",zzval_(Zcheckpart));
    -    qh_fprintf(fp, 9332,"  Number of merged facets: %d\n", nummerged);
    -  }
    -  if (!qh RANDOMoutside && qh QHULLfinished) {
    -    cpu= (float)qh hulltime;
    -    cpu /= (float)qh_SECticks;
    -    wval_(Wcpu)= cpu;
    -    qh_fprintf(fp, 9333, "  CPU seconds to compute hull (after input): %2.4g\n", cpu);
    -  }
    -  if (qh RERUN) {
    -    if (!qh PREmerge && !qh MERGEexact)
    -      qh_fprintf(fp, 9334, "  Percentage of runs with precision errors: %4.1f\n",
    -           zzval_(Zretry)*100.0/qh build_cnt);  /* careful of order */
    -  }else if (qh JOGGLEmax < REALmax/2) {
    -    if (zzval_(Zretry))
    -      qh_fprintf(fp, 9335, "  After %d retries, input joggled by: %2.2g\n",
    -         zzval_(Zretry), qh JOGGLEmax);
    -    else
    -      qh_fprintf(fp, 9336, "  Input joggled by: %2.2g\n", qh JOGGLEmax);
    -  }
    -  if (qh totarea != 0.0)
    -    qh_fprintf(fp, 9337, "  %s facet area:   %2.8g\n",
    -            zzval_(Ztotmerge) ? "Approximate" : "Total", qh totarea);
    -  if (qh totvol != 0.0)
    -    qh_fprintf(fp, 9338, "  %s volume:       %2.8g\n",
    -            zzval_(Ztotmerge) ? "Approximate" : "Total", qh totvol);
    -  if (qh MERGING) {
    -    qh_outerinner(NULL, &outerplane, &innerplane);
    -    if (outerplane > 2 * qh DISTround) {
    -      qh_fprintf(fp, 9339, "  Maximum distance of %spoint above facet: %2.2g",
    -            (qh QHULLfinished ? "" : "merged "), outerplane);
    -      ratio= outerplane/(qh ONEmerge + qh DISTround);
    -      /* don't report ratio if MINoutside is large */
    -      if (ratio > 0.05 && 2* qh ONEmerge > qh MINoutside && qh JOGGLEmax > REALmax/2)
    -        qh_fprintf(fp, 9340, " (%.1fx)\n", ratio);
    -      else
    -        qh_fprintf(fp, 9341, "\n");
    -    }
    -    if (innerplane < -2 * qh DISTround) {
    -      qh_fprintf(fp, 9342, "  Maximum distance of %svertex below facet: %2.2g",
    -            (qh QHULLfinished ? "" : "merged "), innerplane);
    -      ratio= -innerplane/(qh ONEmerge+qh DISTround);
    -      if (ratio > 0.05 && qh JOGGLEmax > REALmax/2)
    -        qh_fprintf(fp, 9343, " (%.1fx)\n", ratio);
    -      else
    -        qh_fprintf(fp, 9344, "\n");
    -    }
    -  }
    -  qh_fprintf(fp, 9345, "\n");
    -} /* printsummary */
    diff --git a/extern/qhull/libqhull.h b/extern/qhull/libqhull.h
    deleted file mode 100644
    index 7a132267bde9..000000000000
    --- a/extern/qhull/libqhull.h
    +++ /dev/null
    @@ -1,1100 +0,0 @@
    -/*
      ---------------------------------
    -
    -   libqhull.h
    -   user-level header file for using qhull.a library
    -
    -   see qh-qhull.htm, qhull_a.h
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/libqhull.h#7 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -
    -   NOTE: access to qh_qh is via the 'qh' macro.  This allows
    -   qh_qh to be either a pointer or a structure.  An example
    -   of using qh is "qh DROPdim" which accesses the DROPdim
    -   field of qh_qh.  Similarly, access to qh_qhstat is via
    -   the 'qhstat' macro.
    -
    -   includes function prototypes for libqhull.c, geom.c, global.c, io.c, user.c
    -
    -   use mem.h for mem.c
    -   use qset.h for qset.c
    -
    -   see unix.c for an example of using libqhull.h
    -
    -   recompile qhull if you change this file
    -*/
    -
    -#ifndef qhDEFlibqhull
    -#define qhDEFlibqhull 1
    -
    -/*=========================== -included files ==============*/
    -
    -#include "user.h"      /* user definable constants (e.g., qh_QHpointer) */
    -
    -#include 
    -#include 
    -#include 
    -#include 
    -
    -#if __MWERKS__ && __POWERPC__
    -#include  
    -#include  
    -#include        
    -#endif
    -
    -#ifndef __STDC__
    -#ifndef __cplusplus
    -#if     !_MSC_VER
    -#error  Neither __STDC__ nor __cplusplus is defined.  Please use strict ANSI C or C++ to compile
    -#error  Qhull.  You may need to turn off compiler extensions in your project configuration.  If
    -#error  your compiler is a standard C compiler, you can delete this warning from libqhull.h
    -#endif
    -#endif
    -#endif
    -
    -/*============ constants and basic types ====================*/
    -
    -extern const char *qh_version; /* defined in global.c */
    -
    -/*----------------------------------
    -
    -  coordT
    -    coordinates and coefficients are stored as realT (i.e., double)
    -
    -  notes:
    -    Qhull works well if realT is 'float'.  If so joggle (QJ) is not effective.
    -
    -    Could use 'float' for data and 'double' for calculations (realT vs. coordT)
    -      This requires many type casts, and adjusted error bounds.
    -      Also C compilers may do expressions in double anyway.
    -*/
    -#define coordT realT
    -
    -/*----------------------------------
    -
    -  pointT
    -    a point is an array of coordinates, usually qh.hull_dim
    -*/
    -#define pointT coordT
    -
    -/*----------------------------------
    -
    -  flagT
    -    Boolean flag as a bit
    -*/
    -#define flagT unsigned int
    -
    -/*----------------------------------
    -
    -  boolT
    -    boolean value, either True or False
    -
    -  notes:
    -    needed for portability
    -    Use qh_False/qh_True as synonyms
    -*/
    -#define boolT unsigned int
    -#ifdef False
    -#undef False
    -#endif
    -#ifdef True
    -#undef True
    -#endif
    -#define False 0
    -#define True 1
    -#define qh_False 0
    -#define qh_True 1
    -
    -/*----------------------------------
    -
    -  qh_CENTER
    -    to distinguish facet->center
    -*/
    -typedef enum
    -{
    -    qh_ASnone = 0, qh_ASvoronoi, qh_AScentrum
    -}
    -qh_CENTER;
    -
    -/*----------------------------------
    -
    -  qh_PRINT
    -    output formats for printing (qh.PRINTout).
    -    'Fa' 'FV' 'Fc' 'FC'
    -
    -
    -   notes:
    -   some of these names are similar to qh names.  The similar names are only
    -   used in switch statements in qh_printbegin() etc.
    -*/
    -typedef enum {qh_PRINTnone= 0,
    -  qh_PRINTarea, qh_PRINTaverage,           /* 'Fa' 'FV' 'Fc' 'FC' */
    -  qh_PRINTcoplanars, qh_PRINTcentrums,
    -  qh_PRINTfacets, qh_PRINTfacets_xridge,   /* 'f' 'FF' 'G' 'FI' 'Fi' 'Fn' */
    -  qh_PRINTgeom, qh_PRINTids, qh_PRINTinner, qh_PRINTneighbors,
    -  qh_PRINTnormals, qh_PRINTouter, qh_PRINTmaple, /* 'n' 'Fo' 'i' 'm' 'Fm' 'FM', 'o' */
    -  qh_PRINTincidences, qh_PRINTmathematica, qh_PRINTmerges, qh_PRINToff,
    -  qh_PRINToptions, qh_PRINTpointintersect, /* 'FO' 'Fp' 'FP' 'p' 'FQ' 'FS' */
    -  qh_PRINTpointnearest, qh_PRINTpoints, qh_PRINTqhull, qh_PRINTsize,
    -  qh_PRINTsummary, qh_PRINTtriangles,      /* 'Fs' 'Ft' 'Fv' 'FN' 'Fx' */
    -  qh_PRINTvertices, qh_PRINTvneighbors, qh_PRINTextremes,
    -  qh_PRINTEND} qh_PRINT;
    -
    -/*----------------------------------
    -
    -  qh_ALL
    -    argument flag for selecting everything
    -*/
    -#define qh_ALL      True
    -#define qh_NOupper  True     /* argument for qh_findbest */
    -#define qh_IScheckmax  True     /* argument for qh_findbesthorizon */
    -#define qh_ISnewfacets  True     /* argument for qh_findbest */
    -#define qh_RESETvisible  True     /* argument for qh_resetlists */
    -
    -/*----------------------------------
    -
    -  qh_ERR
    -    Qhull exit codes, for indicating errors
    -    See: MSG_ERROR and MSG_WARNING [user.h]
    -*/
    -#define qh_ERRnone  0    /* no error occurred during qhull */
    -#define qh_ERRinput 1    /* input inconsistency */
    -#define qh_ERRsingular 2 /* singular input data */
    -#define qh_ERRprec  3    /* precision error */
    -#define qh_ERRmem   4    /* insufficient memory, matches mem.h */
    -#define qh_ERRqhull 5    /* internal error detected, matches mem.h */
    -
    -/*----------------------------------
    -
    -qh_FILEstderr
    -Fake stderr to distinguish error output from normal output
    -For C++ interface.  Must redefine qh_fprintf_qhull
    -*/
    -#define qh_FILEstderr (FILE*)1
    -
    -/* ============ -structures- ====================
    -   each of the following structures is defined by a typedef
    -   all realT and coordT fields occur at the beginning of a structure
    -        (otherwise space may be wasted due to alignment)
    -   define all flags together and pack into 32-bit number
    -*/
    -
    -typedef struct vertexT vertexT;
    -typedef struct ridgeT ridgeT;
    -typedef struct facetT facetT;
    -#ifndef DEFsetT
    -#define DEFsetT 1
    -typedef struct setT setT;          /* defined in qset.h */
    -#endif
    -
    -#ifndef DEFqhstatT
    -#define DEFqhstatT 1
    -typedef struct qhstatT qhstatT;    /* defined in stat.h */
    -#endif
    -
    -/*----------------------------------
    -
    -  facetT
    -    defines a facet
    -
    -  notes:
    -   qhull() generates the hull as a list of facets.
    -
    -  topological information:
    -    f.previous,next     doubly-linked list of facets
    -    f.vertices          set of vertices
    -    f.ridges            set of ridges
    -    f.neighbors         set of neighbors
    -    f.toporient         True if facet has top-orientation (else bottom)
    -
    -  geometric information:
    -    f.offset,normal     hyperplane equation
    -    f.maxoutside        offset to outer plane -- all points inside
    -    f.center            centrum for testing convexity
    -    f.simplicial        True if facet is simplicial
    -    f.flipped           True if facet does not include qh.interior_point
    -
    -  for constructing hull:
    -    f.visible           True if facet on list of visible facets (will be deleted)
    -    f.newfacet          True if facet on list of newly created facets
    -    f.coplanarset       set of points coplanar with this facet
    -                        (includes near-inside points for later testing)
    -    f.outsideset        set of points outside of this facet
    -    f.furthestdist      distance to furthest point of outside set
    -    f.visitid           marks visited facets during a loop
    -    f.replace           replacement facet for to-be-deleted, visible facets
    -    f.samecycle,newcycle cycle of facets for merging into horizon facet
    -
    -  see below for other flags and fields
    -*/
    -struct facetT {
    -#if !qh_COMPUTEfurthest
    -  coordT   furthestdist;/* distance to furthest point of outsideset */
    -#endif
    -#if qh_MAXoutside
    -  coordT   maxoutside;  /* max computed distance of point to facet
    -                        Before QHULLfinished this is an approximation
    -                        since maxdist not always set for mergefacet
    -                        Actual outer plane is +DISTround and
    -                        computed outer plane is +2*DISTround */
    -#endif
    -  coordT   offset;      /* exact offset of hyperplane from origin */
    -  coordT  *normal;      /* normal of hyperplane, hull_dim coefficients */
    -                        /*   if tricoplanar, shared with a neighbor */
    -  union {               /* in order of testing */
    -   realT   area;        /* area of facet, only in io.c if  ->isarea */
    -   facetT *replace;     /*  replacement facet if ->visible and NEWfacets
    -                             is NULL only if qh_mergedegen_redundant or interior */
    -   facetT *samecycle;   /*  cycle of facets from the same visible/horizon intersection,
    -                             if ->newfacet */
    -   facetT *newcycle;    /*  in horizon facet, current samecycle of new facets */
    -   facetT *trivisible;  /* visible facet for ->tricoplanar facets during qh_triangulate() */
    -   facetT *triowner;    /* owner facet for ->tricoplanar, !isarea facets w/ ->keepcentrum */
    -  }f;
    -  coordT  *center;      /*  centrum for convexity, qh CENTERtype == qh_AScentrum */
    -                        /*  Voronoi center, qh CENTERtype == qh_ASvoronoi */
    -                        /*   if tricoplanar, shared with a neighbor */
    -  facetT  *previous;    /* previous facet in the facet_list */
    -  facetT  *next;        /* next facet in the facet_list */
    -  setT    *vertices;    /* vertices for this facet, inverse sorted by ID
    -                           if simplicial, 1st vertex was apex/furthest */
    -  setT    *ridges;      /* explicit ridges for nonsimplicial facets.
    -                           for simplicial facets, neighbors define the ridges */
    -  setT    *neighbors;   /* neighbors of the facet.  If simplicial, the kth
    -                           neighbor is opposite the kth vertex, and the first
    -                           neighbor is the horizon facet for the first vertex*/
    -  setT    *outsideset;  /* set of points outside this facet
    -                           if non-empty, last point is furthest
    -                           if NARROWhull, includes coplanars for partitioning*/
    -  setT    *coplanarset; /* set of points coplanar with this facet
    -                           > qh.min_vertex and <= facet->max_outside
    -                           a point is assigned to the furthest facet
    -                           if non-empty, last point is furthest away */
    -  unsigned visitid;     /* visit_id, for visiting all neighbors,
    -                           all uses are independent */
    -  unsigned id;          /* unique identifier from qh facet_id */
    -  unsigned nummerge:9;  /* number of merges */
    -#define qh_MAXnummerge 511 /*     2^9-1, 32 flags total, see "flags:" in io.c */
    -  flagT    tricoplanar:1; /* True if TRIangulate and simplicial and coplanar with a neighbor */
    -                          /*   all tricoplanars share the same ->center, ->normal, ->offset, ->maxoutside */
    -                          /*   all tricoplanars share the same apex */
    -                          /*   if ->degenerate, does not span facet (one logical ridge) */
    -                          /*   one tricoplanar has ->keepcentrum and ->coplanarset */
    -                          /*   during qh_triangulate, f.trivisible points to original facet */
    -  flagT    newfacet:1;  /* True if facet on qh newfacet_list (new or merged) */
    -  flagT    visible:1;   /* True if visible facet (will be deleted) */
    -  flagT    toporient:1; /* True if created with top orientation
    -                           after merging, use ridge orientation */
    -  flagT    simplicial:1;/* True if simplicial facet, ->ridges may be implicit */
    -  flagT    seen:1;      /* used to perform operations only once, like visitid */
    -  flagT    seen2:1;     /* used to perform operations only once, like visitid */
    -  flagT    flipped:1;   /* True if facet is flipped */
    -  flagT    upperdelaunay:1; /* True if facet is upper envelope of Delaunay triangulation */
    -  flagT    notfurthest:1; /* True if last point of outsideset is not furthest*/
    -
    -/*-------- flags primarily for output ---------*/
    -  flagT    good:1;      /* True if a facet marked good for output */
    -  flagT    isarea:1;    /* True if facet->f.area is defined */
    -
    -/*-------- flags for merging ------------------*/
    -  flagT    dupridge:1;  /* True if duplicate ridge in facet */
    -  flagT    mergeridge:1; /* True if facet or neighbor contains a qh_MERGEridge
    -                            ->normal defined (also defined for mergeridge2) */
    -  flagT    mergeridge2:1; /* True if neighbor contains a qh_MERGEridge (mark_dupridges */
    -  flagT    coplanar:1;  /* True if horizon facet is coplanar at last use */
    -  flagT     mergehorizon:1; /* True if will merge into horizon (->coplanar) */
    -  flagT     cycledone:1;/* True if mergecycle_all already done */
    -  flagT    tested:1;    /* True if facet convexity has been tested (false after merge */
    -  flagT    keepcentrum:1; /* True if keep old centrum after a merge, or marks owner for ->tricoplanar */
    -  flagT    newmerge:1;  /* True if facet is newly merged for reducevertices */
    -  flagT    degenerate:1; /* True if facet is degenerate (degen_mergeset or ->tricoplanar) */
    -  flagT    redundant:1;  /* True if facet is redundant (degen_mergeset) */
    -};
    -
    -
    -/*----------------------------------
    -
    -  ridgeT
    -    defines a ridge
    -
    -  notes:
    -  a ridge is hull_dim-1 simplex between two neighboring facets.  If the
    -  facets are non-simplicial, there may be more than one ridge between
    -  two facets.  E.G. a 4-d hypercube has two triangles between each pair
    -  of neighboring facets.
    -
    -  topological information:
    -    vertices            a set of vertices
    -    top,bottom          neighboring facets with orientation
    -
    -  geometric information:
    -    tested              True if ridge is clearly convex
    -    nonconvex           True if ridge is non-convex
    -*/
    -struct ridgeT {
    -  setT    *vertices;    /* vertices belonging to this ridge, inverse sorted by ID
    -                           NULL if a degen ridge (matchsame) */
    -  facetT  *top;         /* top facet this ridge is part of */
    -  facetT  *bottom;      /* bottom facet this ridge is part of */
    -  unsigned id:24;       /* unique identifier, =>room for 8 flags, bit field matches qh.ridge_id */
    -  flagT    seen:1;      /* used to perform operations only once */
    -  flagT    tested:1;    /* True when ridge is tested for convexity */
    -  flagT    nonconvex:1; /* True if getmergeset detected a non-convex neighbor
    -                           only one ridge between neighbors may have nonconvex */
    -};
    -
    -/*----------------------------------
    -
    -  vertexT
    -     defines a vertex
    -
    -  topological information:
    -    next,previous       doubly-linked list of all vertices
    -    neighbors           set of adjacent facets (only if qh.VERTEXneighbors)
    -
    -  geometric information:
    -    point               array of DIM3 coordinates
    -*/
    -struct vertexT {
    -  vertexT *next;        /* next vertex in vertex_list */
    -  vertexT *previous;    /* previous vertex in vertex_list */
    -  pointT  *point;       /* hull_dim coordinates (coordT) */
    -  setT    *neighbors;   /* neighboring facets of vertex, qh_vertexneighbors()
    -                           inits in io.c or after first merge */
    -  unsigned visitid:31;  /* for use with qh vertex_visit, size must match */
    -  flagT    seen2:1;     /* another seen flag */
    -  unsigned id:24;       /* unique identifier, bit field matches qh.vertex_id */
    -  unsigned dim:4;       /* dimension of point if non-zero, used by cpp */
    -                        /* =>room for 4 flags */
    -  flagT    seen:1;      /* used to perform operations only once */
    -  flagT    delridge:1;  /* vertex was part of a deleted ridge */
    -  flagT    deleted:1;   /* true if vertex on qh del_vertices */
    -  flagT    newlist:1;   /* true if vertex on qh newvertex_list */
    -};
    -
    -#define MAX_vdim 15  /* Maximum size of vertex->dim */
    -
    -/*======= -global variables -qh ============================*/
    -
    -/*----------------------------------
    -
    -  qh
    -   all global variables for qhull are in qh, qhmem, and qhstat
    -
    -  notes:
    -   qhmem is defined in mem.h, qhstat is defined in stat.h, qhrbox is defined in rboxpoints.h
    -   Access to qh_qh is via the "qh" macro.  See qh_QHpointer in user.h
    -
    -   All global variables for qhull are in qh, qhmem, and qhstat
    -   qh must be unique for each instance of qhull
    -   qhstat may be shared between qhull instances.
    -   qhmem may be shared across multiple instances of Qhull.
    -   Rbox uses global variables rbox_inuse and rbox, but does not persist data across calls.
    -
    -   Qhull is not multithreaded.  Global state could be stored in thread-local storage.
    -*/
    -
    -extern int qhull_inuse;
    -
    -typedef struct qhT qhT;
    -#if qh_QHpointer_dllimport
    -#define qh qh_qh->
    -__declspec(dllimport) extern qhT *qh_qh;     /* allocated in global.c */
    -#elif qh_QHpointer
    -#define qh qh_qh->
    -extern qhT *qh_qh;     /* allocated in global.c */
    -#elif qh_dllimport
    -#define qh qh_qh.
    -__declspec(dllimport) extern qhT qh_qh;      /* allocated in global.c */
    -#else
    -#define qh qh_qh.
    -extern qhT qh_qh;
    -#endif
    -
    -struct qhT {
    -
    -/*----------------------------------
    -
    -  qh constants
    -    configuration flags and constants for Qhull
    -
    -  notes:
    -    The user configures Qhull by defining flags.  They are
    -    copied into qh by qh_setflags().  qh-quick.htm#options defines the flags.
    -*/
    -  boolT ALLpoints;        /* true 'Qs' if search all points for initial simplex */
    -  boolT ANGLEmerge;       /* true 'Qa' if sort potential merges by angle */
    -  boolT APPROXhull;       /* true 'Wn' if MINoutside set */
    -  realT   MINoutside;     /*   'Wn' min. distance for an outside point */
    -  boolT ANNOTATEoutput;   /* true 'Ta' if annotate output with message codes */
    -  boolT ATinfinity;       /* true 'Qz' if point num_points-1 is "at-infinity"
    -                             for improving precision in Delaunay triangulations */
    -  boolT AVOIDold;         /* true 'Q4' if avoid old->new merges */
    -  boolT BESToutside;      /* true 'Qf' if partition points into best outsideset */
    -  boolT CDDinput;         /* true 'Pc' if input uses CDD format (1.0/offset first) */
    -  boolT CDDoutput;        /* true 'PC' if print normals in CDD format (offset first) */
    -  boolT CHECKfrequently;  /* true 'Tc' if checking frequently */
    -  realT premerge_cos;     /*   'A-n'   cos_max when pre merging */
    -  realT postmerge_cos;    /*   'An'    cos_max when post merging */
    -  boolT DELAUNAY;         /* true 'd' if computing DELAUNAY triangulation */
    -  boolT DOintersections;  /* true 'Gh' if print hyperplane intersections */
    -  int   DROPdim;          /* drops dim 'GDn' for 4-d -> 3-d output */
    -  boolT FORCEoutput;      /* true 'Po' if forcing output despite degeneracies */
    -  int   GOODpoint;        /* 1+n for 'QGn', good facet if visible/not(-) from point n*/
    -  pointT *GOODpointp;     /*   the actual point */
    -  boolT GOODthreshold;    /* true if qh lower_threshold/upper_threshold defined
    -                             false if qh SPLITthreshold */
    -  int   GOODvertex;       /* 1+n, good facet if vertex for point n */
    -  pointT *GOODvertexp;     /*   the actual point */
    -  boolT HALFspace;        /* true 'Hn,n,n' if halfspace intersection */
    -  int   IStracing;        /* trace execution, 0=none, 1=least, 4=most, -1=events */
    -  int   KEEParea;         /* 'PAn' number of largest facets to keep */
    -  boolT KEEPcoplanar;     /* true 'Qc' if keeping nearest facet for coplanar points */
    -  boolT KEEPinside;       /* true 'Qi' if keeping nearest facet for inside points
    -                              set automatically if 'd Qc' */
    -  int   KEEPmerge;        /* 'PMn' number of facets to keep with most merges */
    -  realT KEEPminArea;      /* 'PFn' minimum facet area to keep */
    -  realT MAXcoplanar;      /* 'Un' max distance below a facet to be coplanar*/
    -  boolT MERGEexact;       /* true 'Qx' if exact merges (coplanar, degen, dupridge, flipped) */
    -  boolT MERGEindependent; /* true 'Q2' if merging independent sets */
    -  boolT MERGING;          /* true if exact-, pre- or post-merging, with angle and centrum tests */
    -  realT   premerge_centrum;  /*   'C-n' centrum_radius when pre merging.  Default is round-off */
    -  realT   postmerge_centrum; /*   'Cn' centrum_radius when post merging.  Default is round-off */
    -  boolT MERGEvertices;    /* true 'Q3' if merging redundant vertices */
    -  realT MINvisible;       /* 'Vn' min. distance for a facet to be visible */
    -  boolT NOnarrow;         /* true 'Q10' if no special processing for narrow distributions */
    -  boolT NOnearinside;     /* true 'Q8' if ignore near-inside points when partitioning */
    -  boolT NOpremerge;       /* true 'Q0' if no defaults for C-0 or Qx */
    -  boolT ONLYgood;         /* true 'Qg' if process points with good visible or horizon facets */
    -  boolT ONLYmax;          /* true 'Qm' if only process points that increase max_outside */
    -  boolT PICKfurthest;     /* true 'Q9' if process furthest of furthest points*/
    -  boolT POSTmerge;        /* true if merging after buildhull (Cn or An) */
    -  boolT PREmerge;         /* true if merging during buildhull (C-n or A-n) */
    -                        /* NOTE: some of these names are similar to qh_PRINT names */
    -  boolT PRINTcentrums;    /* true 'Gc' if printing centrums */
    -  boolT PRINTcoplanar;    /* true 'Gp' if printing coplanar points */
    -  int   PRINTdim;         /* print dimension for Geomview output */
    -  boolT PRINTdots;        /* true 'Ga' if printing all points as dots */
    -  boolT PRINTgood;        /* true 'Pg' if printing good facets */
    -  boolT PRINTinner;       /* true 'Gi' if printing inner planes */
    -  boolT PRINTneighbors;   /* true 'PG' if printing neighbors of good facets */
    -  boolT PRINTnoplanes;    /* true 'Gn' if printing no planes */
    -  boolT PRINToptions1st;  /* true 'FO' if printing options to stderr */
    -  boolT PRINTouter;       /* true 'Go' if printing outer planes */
    -  boolT PRINTprecision;   /* false 'Pp' if not reporting precision problems */
    -  qh_PRINT PRINTout[qh_PRINTEND]; /* list of output formats to print */
    -  boolT PRINTridges;      /* true 'Gr' if print ridges */
    -  boolT PRINTspheres;     /* true 'Gv' if print vertices as spheres */
    -  boolT PRINTstatistics;  /* true 'Ts' if printing statistics to stderr */
    -  boolT PRINTsummary;     /* true 's' if printing summary to stderr */
    -  boolT PRINTtransparent; /* true 'Gt' if print transparent outer ridges */
    -  boolT PROJECTdelaunay;  /* true if DELAUNAY, no readpoints() and
    -                             need projectinput() for Delaunay in qh_init_B */
    -  int   PROJECTinput;     /* number of projected dimensions 'bn:0Bn:0' */
    -  boolT QUICKhelp;        /* true if quick help message for degen input */
    -  boolT RANDOMdist;       /* true if randomly change distplane and setfacetplane */
    -  realT RANDOMfactor;     /*    maximum random perturbation */
    -  realT RANDOMa;          /*    qh_randomfactor is randr * RANDOMa + RANDOMb */
    -  realT RANDOMb;
    -  boolT RANDOMoutside;    /* true if select a random outside point */
    -  int   REPORTfreq;       /* buildtracing reports every n facets */
    -  int   REPORTfreq2;      /* tracemerging reports every REPORTfreq/2 facets */
    -  int   RERUN;            /* 'TRn' rerun qhull n times (qh.build_cnt) */
    -  int   ROTATErandom;     /* 'QRn' seed, 0 time, >= rotate input */
    -  boolT SCALEinput;       /* true 'Qbk' if scaling input */
    -  boolT SCALElast;        /* true 'Qbb' if scale last coord to max prev coord */
    -  boolT SETroundoff;      /* true 'E' if qh DISTround is predefined */
    -  boolT SKIPcheckmax;     /* true 'Q5' if skip qh_check_maxout */
    -  boolT SKIPconvex;       /* true 'Q6' if skip convexity testing during pre-merge */
    -  boolT SPLITthresholds;  /* true if upper_/lower_threshold defines a region
    -                               used only for printing (!for qh ONLYgood) */
    -  int   STOPcone;         /* 'TCn' 1+n for stopping after cone for point n */
    -                          /*       also used by qh_build_withresart for err exit*/
    -  int   STOPpoint;        /* 'TVn' 'TV-n' 1+n for stopping after/before(-)
    -                                        adding point n */
    -  int   TESTpoints;       /* 'QTn' num of test points after qh.num_points.  Test points always coplanar. */
    -  boolT TESTvneighbors;   /*  true 'Qv' if test vertex neighbors at end */
    -  int   TRACElevel;       /* 'Tn' conditional IStracing level */
    -  int   TRACElastrun;     /*  qh.TRACElevel applies to last qh.RERUN */
    -  int   TRACEpoint;       /* 'TPn' start tracing when point n is a vertex */
    -  realT TRACEdist;        /* 'TWn' start tracing when merge distance too big */
    -  int   TRACEmerge;       /* 'TMn' start tracing before this merge */
    -  boolT TRIangulate;      /* true 'Qt' if triangulate non-simplicial facets */
    -  boolT TRInormals;       /* true 'Q11' if triangulate duplicates normals (sets Qt) */
    -  boolT UPPERdelaunay;    /* true 'Qu' if computing furthest-site Delaunay */
    -  boolT USEstdout;        /* true 'Tz' if using stdout instead of stderr */
    -  boolT VERIFYoutput;     /* true 'Tv' if verify output at end of qhull */
    -  boolT VIRTUALmemory;    /* true 'Q7' if depth-first processing in buildhull */
    -  boolT VORONOI;          /* true 'v' if computing Voronoi diagram */
    -
    -  /*--------input constants ---------*/
    -  realT AREAfactor;       /* 1/(hull_dim-1)! for converting det's to area */
    -  boolT DOcheckmax;       /* true if calling qh_check_maxout (qh_initqhull_globals) */
    -  char  *feasible_string;  /* feasible point 'Hn,n,n' for halfspace intersection */
    -  coordT *feasible_point;  /*    as coordinates, both malloc'd */
    -  boolT GETarea;          /* true 'Fa', 'FA', 'FS', 'PAn', 'PFn' if compute facet area/Voronoi volume in io.c */
    -  boolT KEEPnearinside;   /* true if near-inside points in coplanarset */
    -  int   hull_dim;         /* dimension of hull, set by initbuffers */
    -  int   input_dim;        /* dimension of input, set by initbuffers */
    -  int   num_points;       /* number of input points */
    -  pointT *first_point;    /* array of input points, see POINTSmalloc */
    -  boolT POINTSmalloc;     /*   true if qh first_point/num_points allocated */
    -  pointT *input_points;   /* copy of original qh.first_point for input points for qh_joggleinput */
    -  boolT input_malloc;     /* true if qh input_points malloc'd */
    -  char  qhull_command[256];/* command line that invoked this program */
    -  int   qhull_commandsiz2; /*    size of qhull_command at qh_clear_outputflags */
    -  char  rbox_command[256]; /* command line that produced the input points */
    -  char  qhull_options[512];/* descriptive list of options */
    -  int   qhull_optionlen;  /*    length of last line */
    -  int   qhull_optionsiz;  /*    size of qhull_options at qh_build_withrestart */
    -  int   qhull_optionsiz2; /*    size of qhull_options at qh_clear_outputflags */
    -  int   run_id;           /* non-zero, random identifier for this instance of qhull */
    -  boolT VERTEXneighbors;  /* true if maintaining vertex neighbors */
    -  boolT ZEROcentrum;      /* true if 'C-0' or 'C-0 Qx'.  sets ZEROall_ok */
    -  realT *upper_threshold; /* don't print if facet->normal[k]>=upper_threshold[k]
    -                             must set either GOODthreshold or SPLITthreshold
    -                             if Delaunay, default is 0.0 for upper envelope */
    -  realT *lower_threshold; /* don't print if facet->normal[k] <=lower_threshold[k] */
    -  realT *upper_bound;     /* scale point[k] to new upper bound */
    -  realT *lower_bound;     /* scale point[k] to new lower bound
    -                             project if both upper_ and lower_bound == 0 */
    -
    -/*----------------------------------
    -
    -  qh precision constants
    -    precision constants for Qhull
    -
    -  notes:
    -    qh_detroundoff() computes the maximum roundoff error for distance
    -    and other computations.  It also sets default values for the
    -    qh constants above.
    -*/
    -  realT ANGLEround;       /* max round off error for angles */
    -  realT centrum_radius;   /* max centrum radius for convexity (roundoff added) */
    -  realT cos_max;          /* max cosine for convexity (roundoff added) */
    -  realT DISTround;        /* max round off error for distances, 'E' overrides */
    -  realT MAXabs_coord;     /* max absolute coordinate */
    -  realT MAXlastcoord;     /* max last coordinate for qh_scalelast */
    -  realT MAXsumcoord;      /* max sum of coordinates */
    -  realT MAXwidth;         /* max rectilinear width of point coordinates */
    -  realT MINdenom_1;       /* min. abs. value for 1/x */
    -  realT MINdenom;         /*    use divzero if denominator < MINdenom */
    -  realT MINdenom_1_2;     /* min. abs. val for 1/x that allows normalization */
    -  realT MINdenom_2;       /*    use divzero if denominator < MINdenom_2 */
    -  realT MINlastcoord;     /* min. last coordinate for qh_scalelast */
    -  boolT NARROWhull;       /* set in qh_initialhull if angle < qh_MAXnarrow */
    -  realT *NEARzero;        /* hull_dim array for near zero in gausselim */
    -  realT NEARinside;       /* keep points for qh_check_maxout if close to facet */
    -  realT ONEmerge;         /* max distance for merging simplicial facets */
    -  realT outside_err;      /* application's epsilon for coplanar points
    -                             qh_check_bestdist() qh_check_points() reports error if point outside */
    -  realT WIDEfacet;        /* size of wide facet for skipping ridge in
    -                             area computation and locking centrum */
    -
    -/*----------------------------------
    -
    -  qh internal constants
    -    internal constants for Qhull
    -*/
    -  char qhull[sizeof("qhull")]; /* "qhull" for checking ownership while debugging */
    -  jmp_buf errexit;        /* exit label for qh_errexit, defined by setjmp() */
    -  char jmpXtra[40];       /* extra bytes in case jmp_buf is defined wrong by compiler */
    -  jmp_buf restartexit;    /* restart label for qh_errexit, defined by setjmp() */
    -  char jmpXtra2[40];      /* extra bytes in case jmp_buf is defined wrong by compiler*/
    -  FILE *fin;              /* pointer to input file, init by qh_meminit */
    -  FILE *fout;             /* pointer to output file */
    -  FILE *ferr;             /* pointer to error file */
    -  pointT *interior_point; /* center point of the initial simplex*/
    -  int normal_size;     /* size in bytes for facet normals and point coords*/
    -  int center_size;     /* size in bytes for Voronoi centers */
    -  int   TEMPsize;         /* size for small, temporary sets (in quick mem) */
    -
    -/*----------------------------------
    -
    -  qh facet and vertex lists
    -    defines lists of facets, new facets, visible facets, vertices, and
    -    new vertices.  Includes counts, next ids, and trace ids.
    -  see:
    -    qh_resetlists()
    -*/
    -  facetT *facet_list;     /* first facet */
    -  facetT  *facet_tail;     /* end of facet_list (dummy facet) */
    -  facetT *facet_next;     /* next facet for buildhull()
    -                             previous facets do not have outside sets
    -                             NARROWhull: previous facets may have coplanar outside sets for qh_outcoplanar */
    -  facetT *newfacet_list;  /* list of new facets to end of facet_list */
    -  facetT *visible_list;   /* list of visible facets preceeding newfacet_list,
    -                             facet->visible set */
    -  int       num_visible;  /* current number of visible facets */
    -  unsigned tracefacet_id;  /* set at init, then can print whenever */
    -  facetT *tracefacet;     /*   set in newfacet/mergefacet, undone in delfacet*/
    -  unsigned tracevertex_id;  /* set at buildtracing, can print whenever */
    -  vertexT *tracevertex;     /*   set in newvertex, undone in delvertex*/
    -  vertexT *vertex_list;     /* list of all vertices, to vertex_tail */
    -  vertexT  *vertex_tail;    /*      end of vertex_list (dummy vertex) */
    -  vertexT *newvertex_list; /* list of vertices in newfacet_list, to vertex_tail
    -                             all vertices have 'newlist' set */
    -  int   num_facets;       /* number of facets in facet_list
    -                             includes visble faces (num_visible) */
    -  int   num_vertices;     /* number of vertices in facet_list */
    -  int   num_outside;      /* number of points in outsidesets (for tracing and RANDOMoutside)
    -                               includes coplanar outsideset points for NARROWhull/qh_outcoplanar() */
    -  int   num_good;         /* number of good facets (after findgood_all) */
    -  unsigned facet_id;      /* ID of next, new facet from newfacet() */
    -  unsigned ridge_id:24;   /* ID of next, new ridge from newridge() */
    -  unsigned vertex_id:24;  /* ID of next, new vertex from newvertex() */
    -
    -/*----------------------------------
    -
    -  qh global variables
    -    defines minimum and maximum distances, next visit ids, several flags,
    -    and other global variables.
    -    initialize in qh_initbuild or qh_maxmin if used in qh_buildhull
    -*/
    -  unsigned long hulltime; /* ignore time to set up input and randomize */
    -                          /*   use unsigned to avoid wrap-around errors */
    -  boolT ALLOWrestart;     /* true if qh_precision can use qh.restartexit */
    -  int   build_cnt;        /* number of calls to qh_initbuild */
    -  qh_CENTER CENTERtype;   /* current type of facet->center, qh_CENTER */
    -  int   furthest_id;      /* pointid of furthest point, for tracing */
    -  facetT *GOODclosest;    /* closest facet to GOODthreshold in qh_findgood */
    -  boolT hasAreaVolume;    /* true if totarea, totvol was defined by qh_getarea */
    -  boolT hasTriangulation; /* true if triangulation created by qh_triangulate */
    -  realT JOGGLEmax;        /* set 'QJn' if randomly joggle input */
    -  boolT maxoutdone;       /* set qh_check_maxout(), cleared by qh_addpoint() */
    -  realT max_outside;      /* maximum distance from a point to a facet,
    -                               before roundoff, not simplicial vertices
    -                               actual outer plane is +DISTround and
    -                               computed outer plane is +2*DISTround */
    -  realT max_vertex;       /* maximum distance (>0) from vertex to a facet,
    -                               before roundoff, due to a merge */
    -  realT min_vertex;       /* minimum distance (<0) from vertex to a facet,
    -                               before roundoff, due to a merge
    -                               if qh.JOGGLEmax, qh_makenewplanes sets it
    -                               recomputed if qh.DOcheckmax, default -qh.DISTround */
    -  boolT NEWfacets;        /* true while visible facets invalid due to new or merge
    -                              from makecone/attachnewfacets to deletevisible */
    -  boolT findbestnew;      /* true if partitioning calls qh_findbestnew */
    -  boolT findbest_notsharp; /* true if new facets are at least 90 degrees */
    -  boolT NOerrexit;        /* true if qh.errexit is not available */
    -  realT PRINTcradius;     /* radius for printing centrums */
    -  realT PRINTradius;      /* radius for printing vertex spheres and points */
    -  boolT POSTmerging;      /* true when post merging */
    -  int   printoutvar;      /* temporary variable for qh_printbegin, etc. */
    -  int   printoutnum;      /* number of facets printed */
    -  boolT QHULLfinished;    /* True after qhull() is finished */
    -  realT totarea;          /* 'FA': total facet area computed by qh_getarea, hasAreaVolume */
    -  realT totvol;           /* 'FA': total volume computed by qh_getarea, hasAreaVolume */
    -  unsigned int visit_id;  /* unique ID for searching neighborhoods, */
    -  unsigned int vertex_visit:31; /* unique ID for searching vertices, reset with qh_buildtracing */
    -  boolT ZEROall_ok;       /* True if qh_checkzero always succeeds */
    -  boolT WAScoplanar;      /* True if qh_partitioncoplanar (qh_check_maxout) */
    -
    -/*----------------------------------
    -
    -  qh global sets
    -    defines sets for merging, initial simplex, hashing, extra input points,
    -    and deleted vertices
    -*/
    -  setT *facet_mergeset;   /* temporary set of merges to be done */
    -  setT *degen_mergeset;   /* temporary set of degenerate and redundant merges */
    -  setT *hash_table;       /* hash table for matching ridges in qh_matchfacets
    -                             size is setsize() */
    -  setT *other_points;     /* additional points */
    -  setT *del_vertices;     /* vertices to partition and delete with visible
    -                             facets.  Have deleted set for checkfacet */
    -
    -/*----------------------------------
    -
    -  qh global buffers
    -    defines buffers for maxtrix operations, input, and error messages
    -*/
    -  coordT *gm_matrix;      /* (dim+1)Xdim matrix for geom.c */
    -  coordT **gm_row;        /* array of gm_matrix rows */
    -  char* line;             /* malloc'd input line of maxline+1 chars */
    -  int maxline;
    -  coordT *half_space;     /* malloc'd input array for halfspace (qh normal_size+coordT) */
    -  coordT *temp_malloc;    /* malloc'd input array for points */
    -
    -/*----------------------------------
    -
    -  qh static variables
    -    defines static variables for individual functions
    -
    -  notes:
    -    do not use 'static' within a function.  Multiple instances of qhull
    -    may exist.
    -
    -    do not assume zero initialization, 'QPn' may cause a restart
    -*/
    -  boolT ERREXITcalled;    /* true during qh_errexit (prevents duplicate calls */
    -  boolT firstcentrum;     /* for qh_printcentrum */
    -  boolT old_randomdist;   /* save RANDOMdist flag during io, tracing, or statistics */
    -  setT *coplanarfacetset;  /* set of coplanar facets for searching qh_findbesthorizon() */
    -  realT last_low;         /* qh_scalelast parameters for qh_setdelaunay */
    -  realT last_high;
    -  realT last_newhigh;
    -  unsigned lastreport;    /* for qh_buildtracing */
    -  int mergereport;        /* for qh_tracemerging */
    -  qhstatT *old_qhstat;    /* for saving qh_qhstat in save_qhull() and UsingLibQhull.  Free with qh_free() */
    -  setT *old_tempstack;    /* for saving qhmem.tempstack in save_qhull */
    -  int   ridgeoutnum;      /* number of ridges for 4OFF output (qh_printbegin,etc) */
    -};
    -
    -/*=========== -macros- =========================*/
    -
    -/*----------------------------------
    -
    -  otherfacet_(ridge, facet)
    -    return neighboring facet for a ridge in facet
    -*/
    -#define otherfacet_(ridge, facet) \
    -                        (((ridge)->top == (facet)) ? (ridge)->bottom : (ridge)->top)
    -
    -/*----------------------------------
    -
    -  getid_(p)
    -    return int ID for facet, ridge, or vertex
    -    return -1 if NULL
    -*/
    -#define getid_(p)       ((p) ? (int)((p)->id) : -1)
    -
    -/*============== FORALL macros ===================*/
    -
    -/*----------------------------------
    -
    -  FORALLfacets { ... }
    -    assign 'facet' to each facet in qh.facet_list
    -
    -  notes:
    -    uses 'facetT *facet;'
    -    assumes last facet is a sentinel
    -
    -  see:
    -    FORALLfacet_( facetlist )
    -*/
    -#define FORALLfacets for (facet=qh facet_list;facet && facet->next;facet=facet->next)
    -
    -/*----------------------------------
    -
    -  FORALLpoints { ... }
    -    assign 'point' to each point in qh.first_point, qh.num_points
    -
    -  declare:
    -    coordT *point, *pointtemp;
    -*/
    -#define FORALLpoints FORALLpoint_(qh first_point, qh num_points)
    -
    -/*----------------------------------
    -
    -  FORALLpoint_( points, num) { ... }
    -    assign 'point' to each point in points array of num points
    -
    -  declare:
    -    coordT *point, *pointtemp;
    -*/
    -#define FORALLpoint_(points, num) for (point= (points), \
    -      pointtemp= (points)+qh hull_dim*(num); point < pointtemp; point += qh hull_dim)
    -
    -/*----------------------------------
    -
    -  FORALLvertices { ... }
    -    assign 'vertex' to each vertex in qh.vertex_list
    -
    -  declare:
    -    vertexT *vertex;
    -
    -  notes:
    -    assumes qh.vertex_list terminated with a sentinel
    -*/
    -#define FORALLvertices for (vertex=qh vertex_list;vertex && vertex->next;vertex= vertex->next)
    -
    -/*----------------------------------
    -
    -  FOREACHfacet_( facets ) { ... }
    -    assign 'facet' to each facet in facets
    -
    -  declare:
    -    facetT *facet, **facetp;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHfacet_(facets)    FOREACHsetelement_(facetT, facets, facet)
    -
    -/*----------------------------------
    -
    -  FOREACHneighbor_( facet ) { ... }
    -    assign 'neighbor' to each neighbor in facet->neighbors
    -
    -  FOREACHneighbor_( vertex ) { ... }
    -    assign 'neighbor' to each neighbor in vertex->neighbors
    -
    -  declare:
    -    facetT *neighbor, **neighborp;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHneighbor_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighbor)
    -
    -/*----------------------------------
    -
    -  FOREACHpoint_( points ) { ... }
    -    assign 'point' to each point in points set
    -
    -  declare:
    -    pointT *point, **pointp;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHpoint_(points)    FOREACHsetelement_(pointT, points, point)
    -
    -/*----------------------------------
    -
    -  FOREACHridge_( ridges ) { ... }
    -    assign 'ridge' to each ridge in ridges set
    -
    -  declare:
    -    ridgeT *ridge, **ridgep;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHridge_(ridges)    FOREACHsetelement_(ridgeT, ridges, ridge)
    -
    -/*----------------------------------
    -
    -  FOREACHvertex_( vertices ) { ... }
    -    assign 'vertex' to each vertex in vertices set
    -
    -  declare:
    -    vertexT *vertex, **vertexp;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHvertex_(vertices) FOREACHsetelement_(vertexT, vertices,vertex)
    -
    -/*----------------------------------
    -
    -  FOREACHfacet_i_( facets ) { ... }
    -    assign 'facet' and 'facet_i' for each facet in facets set
    -
    -  declare:
    -    facetT *facet;
    -    int     facet_n, facet_i;
    -
    -  see:
    -    FOREACHsetelement_i_
    -*/
    -#define FOREACHfacet_i_(facets)    FOREACHsetelement_i_(facetT, facets, facet)
    -
    -/*----------------------------------
    -
    -  FOREACHneighbor_i_( facet ) { ... }
    -    assign 'neighbor' and 'neighbor_i' for each neighbor in facet->neighbors
    -
    -  FOREACHneighbor_i_( vertex ) { ... }
    -    assign 'neighbor' and 'neighbor_i' for each neighbor in vertex->neighbors
    -
    -  declare:
    -    facetT *neighbor;
    -    int     neighbor_n, neighbor_i;
    -
    -  see:
    -    FOREACHsetelement_i_
    -*/
    -#define FOREACHneighbor_i_(facet)  FOREACHsetelement_i_(facetT, facet->neighbors, neighbor)
    -
    -/*----------------------------------
    -
    -  FOREACHpoint_i_( points ) { ... }
    -    assign 'point' and 'point_i' for each point in points set
    -
    -  declare:
    -    pointT *point;
    -    int     point_n, point_i;
    -
    -  see:
    -    FOREACHsetelement_i_
    -*/
    -#define FOREACHpoint_i_(points)    FOREACHsetelement_i_(pointT, points, point)
    -
    -/*----------------------------------
    -
    -  FOREACHridge_i_( ridges ) { ... }
    -    assign 'ridge' and 'ridge_i' for each ridge in ridges set
    -
    -  declare:
    -    ridgeT *ridge;
    -    int     ridge_n, ridge_i;
    -
    -  see:
    -    FOREACHsetelement_i_
    -*/
    -#define FOREACHridge_i_(ridges)    FOREACHsetelement_i_(ridgeT, ridges, ridge)
    -
    -/*----------------------------------
    -
    -  FOREACHvertex_i_( vertices ) { ... }
    -    assign 'vertex' and 'vertex_i' for each vertex in vertices set
    -
    -  declare:
    -    vertexT *vertex;
    -    int     vertex_n, vertex_i;
    -
    -  see:
    -    FOREACHsetelement_i_
    -*/
    -#define FOREACHvertex_i_(vertices) FOREACHsetelement_i_(vertexT, vertices,vertex)
    -
    -/********* -libqhull.c prototypes (duplicated from qhull_a.h) **********************/
    -
    -void    qh_qhull(void);
    -boolT   qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
    -void    qh_printsummary(FILE *fp);
    -
    -/********* -user.c prototypes (alphabetical) **********************/
    -
    -void    qh_errexit(int exitcode, facetT *facet, ridgeT *ridge);
    -void    qh_errprint(const char* string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex);
    -int     qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc,
    -                char *qhull_cmd, FILE *outfile, FILE *errfile);
    -void    qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall);
    -void    qh_printhelp_degenerate(FILE *fp);
    -void    qh_printhelp_narrowhull(FILE *fp, realT minangle);
    -void    qh_printhelp_singular(FILE *fp);
    -void    qh_user_memsizes(void);
    -
    -/********* -usermem.c prototypes (alphabetical) **********************/
    -void    qh_exit(int exitcode);
    -void    qh_free(void *mem);
    -void   *qh_malloc(size_t size);
    -
    -/********* -userprintf.c and userprintf_rbox.c prototypes **********************/
    -void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
    -void    qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
    -
    -/***** -geom.c/geom2.c/random.c prototypes (duplicated from geom.h, random.h) ****************/
    -
    -facetT *qh_findbest(pointT *point, facetT *startfacet,
    -                     boolT bestoutside, boolT newfacets, boolT noupper,
    -                     realT *dist, boolT *isoutside, int *numpart);
    -facetT *qh_findbestnew(pointT *point, facetT *startfacet,
    -                     realT *dist, boolT bestoutside, boolT *isoutside, int *numpart);
    -boolT   qh_gram_schmidt(int dim, realT **rows);
    -void    qh_outerinner(facetT *facet, realT *outerplane, realT *innerplane);
    -void    qh_printsummary(FILE *fp);
    -void    qh_projectinput(void);
    -void    qh_randommatrix(realT *buffer, int dim, realT **row);
    -void    qh_rotateinput(realT **rows);
    -void    qh_scaleinput(void);
    -void    qh_setdelaunay(int dim, int count, pointT *points);
    -coordT  *qh_sethalfspace_all(int dim, int count, coordT *halfspaces, pointT *feasible);
    -
    -/***** -global.c prototypes (alphabetical) ***********************/
    -
    -unsigned long qh_clock(void);
    -void    qh_checkflags(char *command, char *hiddenflags);
    -void    qh_clear_outputflags(void);
    -void    qh_freebuffers(void);
    -void    qh_freeqhull(boolT allmem);
    -void    qh_freeqhull2(boolT allmem);
    -void    qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]);
    -void    qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc);
    -void    qh_init_qhull_command(int argc, char *argv[]);
    -void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    -void    qh_initflags(char *command);
    -void    qh_initqhull_buffers(void);
    -void    qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc);
    -void    qh_initqhull_mem(void);
    -void    qh_initqhull_outputflags(void);
    -void    qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile);
    -void    qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile);
    -void    qh_initthresholds(char *command);
    -void    qh_option(const char *option, int *i, realT *r);
    -#if qh_QHpointer
    -void    qh_restore_qhull(qhT **oldqh);
    -qhT    *qh_save_qhull(void);
    -#endif
    -
    -/***** -io.c prototypes (duplicated from io.h) ***********************/
    -
    -void    dfacet( unsigned id);
    -void    dvertex( unsigned id);
    -void    qh_printneighborhood(FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall);
    -void    qh_produce_output(void);
    -coordT *qh_readpoints(int *numpoints, int *dimension, boolT *ismalloc);
    -
    -
    -/********* -mem.c prototypes (duplicated from mem.h) **********************/
    -
    -void qh_meminit(FILE *ferr);
    -void qh_memfreeshort(int *curlong, int *totlong);
    -
    -/********* -poly.c/poly2.c prototypes (duplicated from poly.h) **********************/
    -
    -void    qh_check_output(void);
    -void    qh_check_points(void);
    -setT   *qh_facetvertices(facetT *facetlist, setT *facets, boolT allfacets);
    -facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    -           realT *bestdist, boolT *isoutside);
    -vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
    -pointT *qh_point(int id);
    -setT   *qh_pointfacet(void /*qh.facet_list*/);
    -int     qh_pointid(pointT *point);
    -setT   *qh_pointvertex(void /*qh.facet_list*/);
    -void    qh_setvoronoi_all(void);
    -void    qh_triangulate(void /*qh facet_list*/);
    -
    -/********* -rboxpoints.c prototypes **********************/
    -int     qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command);
    -void    qh_errexit_rbox(int exitcode);
    -
    -/********* -stat.c prototypes (duplicated from stat.h) **********************/
    -
    -void    qh_collectstatistics(void);
    -void    qh_printallstatistics(FILE *fp, const char *string);
    -
    -#endif /* qhDEFlibqhull */
    diff --git a/extern/qhull/mem.c b/extern/qhull/mem.c
    deleted file mode 100644
    index f0a8a0fc3273..000000000000
    --- a/extern/qhull/mem.c
    +++ /dev/null
    @@ -1,542 +0,0 @@
    -/*
      ---------------------------------
    -
    -  mem.c
    -    memory management routines for qhull
    -
    -  This is a standalone program.
    -
    -  To initialize memory:
    -
    -    qh_meminit(stderr);
    -    qh_meminitbuffers(qh IStracing, qh_MEMalign, 7, qh_MEMbufsize,qh_MEMinitbuf);
    -    qh_memsize((int)sizeof(facetT));
    -    qh_memsize((int)sizeof(facetT));
    -    ...
    -    qh_memsetup();
    -
    -  To free up all memory buffers:
    -    qh_memfreeshort(&curlong, &totlong);
    -
    -  if qh_NOmem,
    -    malloc/free is used instead of mem.c
    -
    -  notes:
    -    uses Quickfit algorithm (freelists for commonly allocated sizes)
    -    assumes small sizes for freelists (it discards the tail of memory buffers)
    -
    -  see:
    -    qh-mem.htm and mem.h
    -    global.c (qh_initbuffers) for an example of using mem.c
    -
    -  Copyright (c) 1993-2012 The Geometry Center.
    -  $Id: //main/2011/qhull/src/libqhull/mem.c#4 $$Change: 1464 $
    -  $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#include "mem.h"
    -#include 
    -#include 
    -#include 
    -
    -#ifndef qhDEFlibqhull
    -typedef struct ridgeT ridgeT;
    -typedef struct facetT facetT;
    -#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    -#pragma warning( disable : 4127)  /* conditional expression is constant */
    -#pragma warning( disable : 4706)  /* assignment within conditional function */
    -#endif
    -void    qh_errexit(int exitcode, facetT *, ridgeT *);
    -void    qh_exit(int exitcode);
    -void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
    -void    qh_free(void *mem);
    -void   *qh_malloc(size_t size);
    -#endif
    -
    -/*============ -global data structure ==============
    -    see mem.h for definition
    -*/
    -
    -qhmemT qhmem= {0,0,0,0,0,0,0,0,0,0,0,
    -               0,0,0,0,0,0,0,0,0,0,0,
    -               0,0,0,0,0,0,0};     /* remove "= {0}" if this causes a compiler error */
    -
    -#ifndef qh_NOmem
    -
    -/*============= internal functions ==============*/
    -
    -static int qh_intcompare(const void *i, const void *j);
    -
    -/*========== functions in alphabetical order ======== */
    -
    -/*---------------------------------
    -
    -  qh_intcompare( i, j )
    -    used by qsort and bsearch to compare two integers
    -*/
    -static int qh_intcompare(const void *i, const void *j) {
    -  return(*((const int *)i) - *((const int *)j));
    -} /* intcompare */
    -
    -
    -/*----------------------------------
    -
    -  qh_memalloc( insize )
    -    returns object of insize bytes
    -    qhmem is the global memory structure
    -
    -  returns:
    -    pointer to allocated memory
    -    errors if insufficient memory
    -
    -  notes:
    -    use explicit type conversion to avoid type warnings on some compilers
    -    actual object may be larger than insize
    -    use qh_memalloc_() for inline code for quick allocations
    -    logs allocations if 'T5'
    -
    -  design:
    -    if size < qhmem.LASTsize
    -      if qhmem.freelists[size] non-empty
    -        return first object on freelist
    -      else
    -        round up request to size of qhmem.freelists[size]
    -        allocate new allocation buffer if necessary
    -        allocate object from allocation buffer
    -    else
    -      allocate object with qh_malloc() in user.c
    -*/
    -void *qh_memalloc(int insize) {
    -  void **freelistp, *newbuffer;
    -  int idx, size, n;
    -  int outsize, bufsize;
    -  void *object;
    -
    -  if (insize<0) {
    -      qh_fprintf(qhmem.ferr, 6235, "qhull error (qh_memalloc): negative request size (%d).  Did int overflow due to high-D?\n", insize); /* WARN64 */
    -      qh_errexit(qhmem_ERRmem, NULL, NULL);
    -  }
    -  if (insize>=0 && insize <= qhmem.LASTsize) {
    -    idx= qhmem.indextable[insize];
    -    outsize= qhmem.sizetable[idx];
    -    qhmem.totshort += outsize;
    -    freelistp= qhmem.freelists+idx;
    -    if ((object= *freelistp)) {
    -      qhmem.cntquick++;
    -      qhmem.totfree -= outsize;
    -      *freelistp= *((void **)*freelistp);  /* replace freelist with next object */
    -#ifdef qh_TRACEshort
    -      n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    -      if (qhmem.IStracing >= 5)
    -          qh_fprintf(qhmem.ferr, 8141, "qh_mem %p n %8d alloc quick: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    -#endif
    -      return(object);
    -    }else {
    -      qhmem.cntshort++;
    -      if (outsize > qhmem .freesize) {
    -        qhmem .totdropped += qhmem .freesize;
    -        if (!qhmem.curbuffer)
    -          bufsize= qhmem.BUFinit;
    -        else
    -          bufsize= qhmem.BUFsize;
    -        if (!(newbuffer= qh_malloc((size_t)bufsize))) {
    -          qh_fprintf(qhmem.ferr, 6080, "qhull error (qh_memalloc): insufficient memory to allocate short memory buffer (%d bytes)\n", bufsize);
    -          qh_errexit(qhmem_ERRmem, NULL, NULL);
    -        }
    -        *((void **)newbuffer)= qhmem.curbuffer;  /* prepend newbuffer to curbuffer
    -                                                    list */
    -        qhmem.curbuffer= newbuffer;
    -        size= (sizeof(void **) + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
    -        qhmem.freemem= (void *)((char *)newbuffer+size);
    -        qhmem.freesize= bufsize - size;
    -        qhmem.totbuffer += bufsize - size; /* easier to check */
    -        /* Periodically test totbuffer.  It matches at beginning and exit of every call */
    -        n = qhmem.totshort + qhmem.totfree + qhmem.totdropped + qhmem.freesize - outsize;
    -        if (qhmem.totbuffer != n) {
    -            qh_fprintf(qhmem.ferr, 6212, "qh_memalloc internal error: short totbuffer %d != totshort+totfree... %d\n", qhmem.totbuffer, n);
    -            qh_errexit(qhmem_ERRmem, NULL, NULL);
    -        }
    -      }
    -      object= qhmem.freemem;
    -      qhmem.freemem= (void *)((char *)qhmem.freemem + outsize);
    -      qhmem.freesize -= outsize;
    -      qhmem.totunused += outsize - insize;
    -#ifdef qh_TRACEshort
    -      n= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    -      if (qhmem.IStracing >= 5)
    -          qh_fprintf(qhmem.ferr, 8140, "qh_mem %p n %8d alloc short: %d bytes (tot %d cnt %d)\n", object, n, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    -#endif
    -      return object;
    -    }
    -  }else {                     /* long allocation */
    -    if (!qhmem.indextable) {
    -      qh_fprintf(qhmem.ferr, 6081, "qhull internal error (qh_memalloc): qhmem has not been initialized.\n");
    -      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -    }
    -    outsize= insize;
    -    qhmem .cntlong++;
    -    qhmem .totlong += outsize;
    -    if (qhmem.maxlong < qhmem.totlong)
    -      qhmem.maxlong= qhmem.totlong;
    -    if (!(object= qh_malloc((size_t)outsize))) {
    -      qh_fprintf(qhmem.ferr, 6082, "qhull error (qh_memalloc): insufficient memory to allocate %d bytes\n", outsize);
    -      qh_errexit(qhmem_ERRmem, NULL, NULL);
    -    }
    -    if (qhmem.IStracing >= 5)
    -      qh_fprintf(qhmem.ferr, 8057, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, outsize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    -  }
    -  return(object);
    -} /* memalloc */
    -
    -
    -/*----------------------------------
    -
    -  qh_memfree( object, insize )
    -    free up an object of size bytes
    -    size is insize from qh_memalloc
    -
    -  notes:
    -    object may be NULL
    -    type checking warns if using (void **)object
    -    use qh_memfree_() for quick free's of small objects
    -
    -  design:
    -    if size <= qhmem.LASTsize
    -      append object to corresponding freelist
    -    else
    -      call qh_free(object)
    -*/
    -void qh_memfree(void *object, int insize) {
    -  void **freelistp;
    -  int idx, outsize;
    -
    -  if (!object)
    -    return;
    -  if (insize <= qhmem.LASTsize) {
    -    qhmem .freeshort++;
    -    idx= qhmem.indextable[insize];
    -    outsize= qhmem.sizetable[idx];
    -    qhmem .totfree += outsize;
    -    qhmem .totshort -= outsize;
    -    freelistp= qhmem.freelists + idx;
    -    *((void **)object)= *freelistp;
    -    *freelistp= object;
    -#ifdef qh_TRACEshort
    -    idx= qhmem.cntshort+qhmem.cntquick+qhmem.freeshort;
    -    if (qhmem.IStracing >= 5)
    -        qh_fprintf(qhmem.ferr, 8142, "qh_mem %p n %8d free short: %d bytes (tot %d cnt %d)\n", object, idx, outsize, qhmem.totshort, qhmem.cntshort+qhmem.cntquick-qhmem.freeshort);
    -#endif
    -  }else {
    -    qhmem .freelong++;
    -    qhmem .totlong -= insize;
    -    qh_free(object);
    -    if (qhmem.IStracing >= 5)
    -      qh_fprintf(qhmem.ferr, 8058, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    -  }
    -} /* memfree */
    -
    -
    -/*---------------------------------
    -
    -  qh_memfreeshort( curlong, totlong )
    -    frees up all short and qhmem memory allocations
    -
    -  returns:
    -    number and size of current long allocations
    -
    -  see:
    -    qh_freeqhull(allMem)
    -    qh_memtotal(curlong, totlong, curshort, totshort, maxlong, totbuffer);
    -*/
    -void qh_memfreeshort(int *curlong, int *totlong) {
    -  void *buffer, *nextbuffer;
    -  FILE *ferr;
    -
    -  *curlong= qhmem .cntlong - qhmem .freelong;
    -  *totlong= qhmem .totlong;
    -  for (buffer= qhmem.curbuffer; buffer; buffer= nextbuffer) {
    -    nextbuffer= *((void **) buffer);
    -    qh_free(buffer);
    -  }
    -  qhmem.curbuffer= NULL;
    -  if (qhmem .LASTsize) {
    -    qh_free(qhmem .indextable);
    -    qh_free(qhmem .freelists);
    -    qh_free(qhmem .sizetable);
    -  }
    -  ferr= qhmem.ferr;
    -  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    -  qhmem.ferr= ferr;
    -} /* memfreeshort */
    -
    -
    -/*----------------------------------
    -
    -  qh_meminit( ferr )
    -    initialize qhmem and test sizeof( void*)
    -*/
    -void qh_meminit(FILE *ferr) {
    -
    -  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    -  qhmem.ferr= ferr;
    -  if (sizeof(void*) < sizeof(int)) {
    -    qh_fprintf(ferr, 6083, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
    -    qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
    -  }
    -  if (sizeof(void*) > sizeof(ptr_intT)) {
    -      qh_fprintf(ferr, 6084, "qhull internal error (qh_meminit): sizeof(void*) %d > sizeof(ptr_intT) %d. Change ptr_intT in mem.h to 'long long'\n", (int)sizeof(void*), (int)sizeof(ptr_intT));
    -      qh_exit(qhmem_ERRqhull);  /* can not use qh_errexit() */
    -  }
    -} /* meminit */
    -
    -/*---------------------------------
    -
    -  qh_meminitbuffers( tracelevel, alignment, numsizes, bufsize, bufinit )
    -    initialize qhmem
    -    if tracelevel >= 5, trace memory allocations
    -    alignment= desired address alignment for memory allocations
    -    numsizes= number of freelists
    -    bufsize=  size of additional memory buffers for short allocations
    -    bufinit=  size of initial memory buffer for short allocations
    -*/
    -void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
    -
    -  qhmem.IStracing= tracelevel;
    -  qhmem.NUMsizes= numsizes;
    -  qhmem.BUFsize= bufsize;
    -  qhmem.BUFinit= bufinit;
    -  qhmem.ALIGNmask= alignment-1;
    -  if (qhmem.ALIGNmask & ~qhmem.ALIGNmask) {
    -    qh_fprintf(qhmem.ferr, 6085, "qhull internal error (qh_meminit): memory alignment %d is not a power of 2\n", alignment);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  qhmem.sizetable= (int *) calloc((size_t)numsizes, sizeof(int));
    -  qhmem.freelists= (void **) calloc((size_t)numsizes, sizeof(void *));
    -  if (!qhmem.sizetable || !qhmem.freelists) {
    -    qh_fprintf(qhmem.ferr, 6086, "qhull error (qh_meminit): insufficient memory\n");
    -    qh_errexit(qhmem_ERRmem, NULL, NULL);
    -  }
    -  if (qhmem.IStracing >= 1)
    -    qh_fprintf(qhmem.ferr, 8059, "qh_meminitbuffers: memory initialized with alignment %d\n", alignment);
    -} /* meminitbuffers */
    -
    -/*---------------------------------
    -
    -  qh_memsetup()
    -    set up memory after running memsize()
    -*/
    -void qh_memsetup(void) {
    -  int k,i;
    -
    -  qsort(qhmem.sizetable, (size_t)qhmem.TABLEsize, sizeof(int), qh_intcompare);
    -  qhmem.LASTsize= qhmem.sizetable[qhmem.TABLEsize-1];
    -  if (qhmem .LASTsize >= qhmem .BUFsize || qhmem.LASTsize >= qhmem .BUFinit) {
    -    qh_fprintf(qhmem.ferr, 6087, "qhull error (qh_memsetup): largest mem size %d is >= buffer size %d or initial buffer size %d\n",
    -            qhmem .LASTsize, qhmem .BUFsize, qhmem .BUFinit);
    -    qh_errexit(qhmem_ERRmem, NULL, NULL);
    -  }
    -  if (!(qhmem.indextable= (int *)qh_malloc((qhmem.LASTsize+1) * sizeof(int)))) {
    -    qh_fprintf(qhmem.ferr, 6088, "qhull error (qh_memsetup): insufficient memory\n");
    -    qh_errexit(qhmem_ERRmem, NULL, NULL);
    -  }
    -  for (k=qhmem.LASTsize+1; k--; )
    -    qhmem.indextable[k]= k;
    -  i= 0;
    -  for (k=0; k <= qhmem.LASTsize; k++) {
    -    if (qhmem.indextable[k] <= qhmem.sizetable[i])
    -      qhmem.indextable[k]= i;
    -    else
    -      qhmem.indextable[k]= ++i;
    -  }
    -} /* memsetup */
    -
    -/*---------------------------------
    -
    -  qh_memsize( size )
    -    define a free list for this size
    -*/
    -void qh_memsize(int size) {
    -  int k;
    -
    -  if (qhmem .LASTsize) {
    -    qh_fprintf(qhmem.ferr, 6089, "qhull error (qh_memsize): called after qhmem_setup\n");
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  size= (size + qhmem.ALIGNmask) & ~qhmem.ALIGNmask;
    -  for (k=qhmem.TABLEsize; k--; ) {
    -    if (qhmem.sizetable[k] == size)
    -      return;
    -  }
    -  if (qhmem.TABLEsize < qhmem.NUMsizes)
    -    qhmem.sizetable[qhmem.TABLEsize++]= size;
    -  else
    -    qh_fprintf(qhmem.ferr, 7060, "qhull warning (memsize): free list table has room for only %d sizes\n", qhmem.NUMsizes);
    -} /* memsize */
    -
    -
    -/*---------------------------------
    -
    -  qh_memstatistics( fp )
    -    print out memory statistics
    -
    -    Verifies that qhmem.totfree == sum of freelists
    -*/
    -void qh_memstatistics(FILE *fp) {
    -  int i, count, totfree= 0;
    -  void *object;
    -
    -  for (i=0; i < qhmem.TABLEsize; i++) {
    -    count=0;
    -    for (object= qhmem .freelists[i]; object; object= *((void **)object))
    -      count++;
    -    totfree += qhmem.sizetable[i] * count;
    -  }
    -  if (totfree != qhmem .totfree) {
    -      qh_fprintf(qhmem.ferr, 6211, "qh_memstatistics internal error: totfree %d not equal to freelist total %d\n", qhmem.totfree, totfree);
    -      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  qh_fprintf(fp, 9278, "\nmemory statistics:\n\
    -%7d quick allocations\n\
    -%7d short allocations\n\
    -%7d long allocations\n\
    -%7d short frees\n\
    -%7d long frees\n\
    -%7d bytes of short memory in use\n\
    -%7d bytes of short memory in freelists\n\
    -%7d bytes of dropped short memory\n\
    -%7d bytes of unused short memory (estimated)\n\
    -%7d bytes of long memory allocated (max, except for input)\n\
    -%7d bytes of long memory in use (in %d pieces)\n\
    -%7d bytes of short memory buffers (minus links)\n\
    -%7d bytes per short memory buffer (initially %d bytes)\n",
    -           qhmem .cntquick, qhmem .cntshort, qhmem .cntlong,
    -           qhmem .freeshort, qhmem .freelong,
    -           qhmem .totshort, qhmem .totfree,
    -           qhmem .totdropped + qhmem .freesize, qhmem .totunused,
    -           qhmem .maxlong, qhmem .totlong, qhmem .cntlong - qhmem .freelong,
    -           qhmem .totbuffer, qhmem .BUFsize, qhmem .BUFinit);
    -  if (qhmem.cntlarger) {
    -    qh_fprintf(fp, 9279, "%7d calls to qh_setlarger\n%7.2g     average copy size\n",
    -           qhmem.cntlarger, ((float)qhmem.totlarger)/(float)qhmem.cntlarger);
    -    qh_fprintf(fp, 9280, "  freelists(bytes->count):");
    -  }
    -  for (i=0; i < qhmem.TABLEsize; i++) {
    -    count=0;
    -    for (object= qhmem .freelists[i]; object; object= *((void **)object))
    -      count++;
    -    qh_fprintf(fp, 9281, " %d->%d", qhmem.sizetable[i], count);
    -  }
    -  qh_fprintf(fp, 9282, "\n\n");
    -} /* memstatistics */
    -
    -
    -/*---------------------------------
    -
    -  qh_NOmem
    -    turn off quick-fit memory allocation
    -
    -  notes:
    -    uses qh_malloc() and qh_free() instead
    -*/
    -#else /* qh_NOmem */
    -
    -void *qh_memalloc(int insize) {
    -  void *object;
    -
    -  if (!(object= qh_malloc((size_t)insize))) {
    -    qh_fprintf(qhmem.ferr, 6090, "qhull error (qh_memalloc): insufficient memory\n");
    -    qh_errexit(qhmem_ERRmem, NULL, NULL);
    -  }
    -  qhmem .cntlong++;
    -  qhmem .totlong += insize;
    -  if (qhmem.maxlong < qhmem.totlong)
    -      qhmem.maxlong= qhmem.totlong;
    -  if (qhmem.IStracing >= 5)
    -    qh_fprintf(qhmem.ferr, 8060, "qh_mem %p n %8d alloc long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    -  return object;
    -}
    -
    -void qh_memfree(void *object, int insize) {
    -
    -  if (!object)
    -    return;
    -  qh_free(object);
    -  qhmem .freelong++;
    -  qhmem .totlong -= insize;
    -  if (qhmem.IStracing >= 5)
    -    qh_fprintf(qhmem.ferr, 8061, "qh_mem %p n %8d free long: %d bytes (tot %d cnt %d)\n", object, qhmem.cntlong+qhmem.freelong, insize, qhmem.totlong, qhmem.cntlong-qhmem.freelong);
    -}
    -
    -void qh_memfreeshort(int *curlong, int *totlong) {
    -  *totlong= qhmem .totlong;
    -  *curlong= qhmem .cntlong - qhmem .freelong;
    -  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    -}
    -
    -void qh_meminit(FILE *ferr) {
    -
    -  memset((char *)&qhmem, 0, sizeof(qhmem));  /* every field is 0, FALSE, NULL */
    -  qhmem.ferr= ferr;
    -  if (sizeof(void*) < sizeof(int)) {
    -    qh_fprintf(ferr, 6091, "qhull internal error (qh_meminit): sizeof(void*) %d < sizeof(int) %d.  qset.c will not work\n", (int)sizeof(void*), (int)sizeof(int));
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -}
    -
    -void qh_meminitbuffers(int tracelevel, int alignment, int numsizes, int bufsize, int bufinit) {
    -
    -  qhmem.IStracing= tracelevel;
    -}
    -
    -void qh_memsetup(void) {
    -
    -}
    -
    -void qh_memsize(int size) {
    -
    -}
    -
    -void qh_memstatistics(FILE *fp) {
    -
    -  qh_fprintf(fp, 9409, "\nmemory statistics:\n\
    -%7d long allocations\n\
    -%7d long frees\n\
    -%7d bytes of long memory allocated (max, except for input)\n\
    -%7d bytes of long memory in use (in %d pieces)\n",
    -           qhmem .cntlong,
    -           qhmem .freelong,
    -           qhmem .maxlong, qhmem .totlong, qhmem .cntlong - qhmem .freelong);
    -}
    -
    -#endif /* qh_NOmem */
    -
    -/*---------------------------------
    -
    -  qh_memtotal( totlong, curlong, totshort, curshort, maxlong, totbuffer )
    -    Return the total, allocated long and short memory
    -
    -  returns:
    -    Returns the total current bytes of long and short allocations
    -    Returns the current count of long and short allocations
    -    Returns the maximum long memory and total short buffer (minus one link per buffer)
    -    Does not error (UsingLibQhull.cpp)
    -*/
    -void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer) {
    -    *totlong= qhmem .totlong;
    -    *curlong= qhmem .cntlong - qhmem .freelong;
    -    *totshort= qhmem .totshort;
    -    *curshort= qhmem .cntshort + qhmem .cntquick - qhmem .freeshort;
    -    *maxlong= qhmem .maxlong;
    -    *totbuffer= qhmem .totbuffer;
    -} /* memtotlong */
    diff --git a/extern/qhull/mem.h b/extern/qhull/mem.h
    deleted file mode 100644
    index d65cbe128e48..000000000000
    --- a/extern/qhull/mem.h
    +++ /dev/null
    @@ -1,219 +0,0 @@
    -/*
      ---------------------------------
    -
    -   mem.h
    -     prototypes for memory management functions
    -
    -   see qh-mem.htm, mem.c and qset.h
    -
    -   for error handling, writes message and calls
    -     qh_errexit(qhmem_ERRmem, NULL, NULL) if insufficient memory
    -       and
    -     qh_errexit(qhmem_ERRqhull, NULL, NULL) otherwise
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/mem.h#4 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFmem
    -#define qhDEFmem 1
    -
    -#include 
    -
    -/*---------------------------------
    -
    -  qh_NOmem
    -    turn off quick-fit memory allocation
    -
    -  notes:
    -    mem.c implements Quickfit memory allocation for about 20% time
    -    savings.  If it fails on your machine, try to locate the
    -    problem, and send the answer to qhull@qhull.org.  If this can
    -    not be done, define qh_NOmem to use malloc/free instead.
    -
    -   #define qh_NOmem
    -*/
    -
    -/*---------------------------------
    -
    -qh_TRACEshort
    -Trace short and quick memory allocations at T5
    -
    -*/
    -#define qh_TRACEshort
    -
    -/*-------------------------------------------
    -    to avoid bus errors, memory allocation must consider alignment requirements.
    -    malloc() automatically takes care of alignment.   Since mem.c manages
    -    its own memory, we need to explicitly specify alignment in
    -    qh_meminitbuffers().
    -
    -    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    -    do not occur in data structures and pointers are the same size.  Be careful
    -    of machines (e.g., DEC Alpha) with large pointers.  If gcc is available,
    -    use __alignof__(double) or fmax_(__alignof__(float), __alignof__(void *)).
    -
    -   see qh_MEMalign in user.h for qhull's alignment
    -*/
    -
    -#define qhmem_ERRmem 4    /* matches qh_ERRmem in libqhull.h */
    -#define qhmem_ERRqhull 5  /* matches qh_ERRqhull in libqhull.h */
    -
    -/*----------------------------------
    -
    -  ptr_intT
    -    for casting a void * to an integer-type that holds a pointer
    -    Used for integer expressions (e.g., computing qh_gethash() in poly.c)
    -
    -  notes:
    -    WARN64 -- these notes indicate 64-bit issues
    -    On 64-bit machines, a pointer may be larger than an 'int'.
    -    qh_meminit()/mem.c checks that 'ptr_intT' holds a 'void*'
    -    ptr_intT is typically a signed value, but not necessarily so
    -    size_t is typically unsigned, but should match the parameter type
    -    Qhull uses int instead of size_t except for system calls such as malloc, qsort, qh_malloc, etc.
    -    This matches Qt convention and is easier to work with.
    -*/
    -#if _MSC_VER && defined(_WIN64)
    -typedef long long ptr_intT;
    -#else
    -typedef long ptr_intT;
    -#endif
    -
    -/*----------------------------------
    -
    -  qhmemT
    -    global memory structure for mem.c
    -
    - notes:
    -   users should ignore qhmem except for writing extensions
    -   qhmem is allocated in mem.c
    -
    -   qhmem could be swapable like qh and qhstat, but then
    -   multiple qh's and qhmem's would need to keep in synch.
    -   A swapable qhmem would also waste memory buffers.  As long
    -   as memory operations are atomic, there is no problem with
    -   multiple qh structures being active at the same time.
    -   If you need separate address spaces, you can swap the
    -   contents of qhmem.
    -*/
    -typedef struct qhmemT qhmemT;
    -extern qhmemT qhmem;
    -
    -#ifndef DEFsetT
    -#define DEFsetT 1
    -typedef struct setT setT;          /* defined in qset.h */
    -#endif
    -
    -/* Update qhmem in mem.c if add or remove fields */
    -struct qhmemT {               /* global memory management variables */
    -  int      BUFsize;           /* size of memory allocation buffer */
    -  int      BUFinit;           /* initial size of memory allocation buffer */
    -  int      TABLEsize;         /* actual number of sizes in free list table */
    -  int      NUMsizes;          /* maximum number of sizes in free list table */
    -  int      LASTsize;          /* last size in free list table */
    -  int      ALIGNmask;         /* worst-case alignment, must be 2^n-1 */
    -  void   **freelists;          /* free list table, linked by offset 0 */
    -  int     *sizetable;         /* size of each freelist */
    -  int     *indextable;        /* size->index table */
    -  void    *curbuffer;         /* current buffer, linked by offset 0 */
    -  void    *freemem;           /*   free memory in curbuffer */
    -  int      freesize;          /*   size of freemem in bytes */
    -  setT    *tempstack;         /* stack of temporary memory, managed by users */
    -  FILE    *ferr;              /* file for reporting errors, only user is qh_fprintf() */
    -  int      IStracing;         /* =5 if tracing memory allocations */
    -  int      cntquick;          /* count of quick allocations */
    -                              /* Note: removing statistics doesn't effect speed */
    -  int      cntshort;          /* count of short allocations */
    -  int      cntlong;           /* count of long allocations */
    -  int      freeshort;         /* count of short memfrees */
    -  int      freelong;          /* count of long memfrees */
    -  int      totbuffer;         /* total short memory buffers minus buffer links */
    -  int      totdropped;        /* total dropped memory at end of short memory buffers (e.g., freesize) */
    -  int      totfree;           /* total size of free, short memory on freelists */
    -  int      totlong;           /* total size of long memory in use */
    -  int      maxlong;           /*   maximum totlong */
    -  int      totshort;          /* total size of short memory in use */
    -  int      totunused;         /* total unused short memory (estimated, short size - request size of first allocations) */
    -  int      cntlarger;         /* count of setlarger's */
    -  int      totlarger;         /* total copied by setlarger */
    -};
    -
    -
    -/*==================== -macros ====================*/
    -
    -/*----------------------------------
    -
    -  qh_memalloc_(insize, object, type)
    -    returns object of size bytes
    -        assumes size<=qhmem.LASTsize and void **freelistp is a temp
    -*/
    -
    -#if defined qh_NOmem
    -#define qh_memalloc_(insize, freelistp, object, type) {\
    -  object= (type*)qh_memalloc(insize); }
    -#elif defined qh_TRACEshort
    -#define qh_memalloc_(insize, freelistp, object, type) {\
    -    freelistp= NULL; /* Avoid warnings */ \
    -    object= (type*)qh_memalloc(insize); }
    -#else /* !qh_NOmem */
    -
    -#define qh_memalloc_(insize, freelistp, object, type) {\
    -  freelistp= qhmem.freelists + qhmem.indextable[insize];\
    -  if ((object= (type*)*freelistp)) {\
    -    qhmem.totshort += qhmem.sizetable[qhmem.indextable[insize]]; \
    -    qhmem.totfree -= qhmem.sizetable[qhmem.indextable[insize]]; \
    -    qhmem.cntquick++;  \
    -    *freelistp= *((void **)*freelistp);\
    -  }else object= (type*)qh_memalloc(insize);}
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_memfree_(object, insize)
    -    free up an object
    -
    -  notes:
    -    object may be NULL
    -    assumes size<=qhmem.LASTsize and void **freelistp is a temp
    -*/
    -#if defined qh_NOmem
    -#define qh_memfree_(object, insize, freelistp) {\
    -  qh_memfree(object, insize); }
    -#elif defined qh_TRACEshort
    -#define qh_memfree_(object, insize, freelistp) {\
    -    freelistp= NULL; /* Avoid warnings */ \
    -    qh_memfree(object, insize); }
    -#else /* !qh_NOmem */
    -
    -#define qh_memfree_(object, insize, freelistp) {\
    -  if (object) { \
    -    qhmem .freeshort++;\
    -    freelistp= qhmem.freelists + qhmem.indextable[insize];\
    -    qhmem.totshort -= qhmem.sizetable[qhmem.indextable[insize]]; \
    -    qhmem.totfree += qhmem.sizetable[qhmem.indextable[insize]]; \
    -    *((void **)object)= *freelistp;\
    -    *freelistp= object;}}
    -#endif
    -
    -/*=============== prototypes in alphabetical order ============*/
    -
    -void *qh_memalloc(int insize);
    -void qh_memfree(void *object, int insize);
    -void qh_memfreeshort(int *curlong, int *totlong);
    -void qh_meminit(FILE *ferr);
    -void qh_meminitbuffers(int tracelevel, int alignment, int numsizes,
    -                        int bufsize, int bufinit);
    -void qh_memsetup(void);
    -void qh_memsize(int size);
    -void qh_memstatistics(FILE *fp);
    -void qh_memtotal(int *totlong, int *curlong, int *totshort, int *curshort, int *maxlong, int *totbuffer);
    -
    -#endif /* qhDEFmem */
    diff --git a/extern/qhull/merge.c b/extern/qhull/merge.c
    deleted file mode 100644
    index 71215d772a4b..000000000000
    --- a/extern/qhull/merge.c
    +++ /dev/null
    @@ -1,3622 +0,0 @@
    -/*
      ---------------------------------
    -
    -   merge.c
    -   merges non-convex facets
    -
    -   see qh-merge.htm and merge.h
    -
    -   other modules call qh_premerge() and qh_postmerge()
    -
    -   the user may call qh_postmerge() to perform additional merges.
    -
    -   To remove deleted facets and vertices (qhull() in libqhull.c):
    -     qh_partitionvisible(!qh_ALL, &numoutside);  // visible_list, newfacet_list
    -     qh_deletevisible();         // qh.visible_list
    -     qh_resetlists(False, qh_RESETvisible);       // qh.visible_list newvertex_list newfacet_list
    -
    -   assumes qh.CENTERtype= centrum
    -
    -   merges occur in qh_mergefacet and in qh_mergecycle
    -   vertex->neighbors not set until the first merge occurs
    -
    -   Copyright (c) 1993-2012 C.B. Barber.
    -   $Id: //main/2011/qhull/src/libqhull/merge.c#4 $$Change: 1490 $
    -   $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -#ifndef qh_NOmerge
    -
    -/*===== functions(alphabetical after premerge and postmerge) ======*/
    -
    -/*---------------------------------
    -
    -  qh_premerge( apex, maxcentrum )
    -    pre-merge nonconvex facets in qh.newfacet_list for apex
    -    maxcentrum defines coplanar and concave (qh_test_appendmerge)
    -
    -  returns:
    -    deleted facets added to qh.visible_list with facet->visible set
    -
    -  notes:
    -    uses globals, qh.MERGEexact, qh.PREmerge
    -
    -  design:
    -    mark duplicate ridges in qh.newfacet_list
    -    merge facet cycles in qh.newfacet_list
    -    merge duplicate ridges and concave facets in qh.newfacet_list
    -    check merged facet cycles for degenerate and redundant facets
    -    merge degenerate and redundant facets
    -    collect coplanar and concave facets
    -    merge concave, coplanar, degenerate, and redundant facets
    -*/
    -void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
    -  boolT othermerge= False;
    -  facetT *newfacet;
    -
    -  if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
    -    return;
    -  trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %2.2g for apex v%d facetlist f%d\n",
    -            maxcentrum, maxangle, apex->id, getid_(qh newfacet_list)));
    -  if (qh IStracing >= 4 && qh num_facets < 50)
    -    qh_printlists();
    -  qh centrum_radius= maxcentrum;
    -  qh cos_max= maxangle;
    -  qh degen_mergeset= qh_settemp(qh TEMPsize);
    -  qh facet_mergeset= qh_settemp(qh TEMPsize);
    -  if (qh hull_dim >=3) {
    -    qh_mark_dupridges(qh newfacet_list); /* facet_mergeset */
    -    qh_mergecycle_all(qh newfacet_list, &othermerge);
    -    qh_forcedmerges(&othermerge /* qh facet_mergeset */);
    -    FORALLnew_facets {  /* test samecycle merges */
    -      if (!newfacet->simplicial && !newfacet->mergeridge)
    -        qh_degen_redundant_neighbors(newfacet, NULL);
    -    }
    -    if (qh_merge_degenredundant())
    -      othermerge= True;
    -  }else /* qh hull_dim == 2 */
    -    qh_mergecycle_all(qh newfacet_list, &othermerge);
    -  qh_flippedmerges(qh newfacet_list, &othermerge);
    -  if (!qh MERGEexact || zzval_(Ztotmerge)) {
    -    zinc_(Zpremergetot);
    -    qh POSTmerging= False;
    -    qh_getmergeset_initial(qh newfacet_list);
    -    qh_all_merges(othermerge, False);
    -  }
    -  qh_settempfree(&qh facet_mergeset);
    -  qh_settempfree(&qh degen_mergeset);
    -} /* premerge */
    -
    -/*---------------------------------
    -
    -  qh_postmerge( reason, maxcentrum, maxangle, vneighbors )
    -    post-merge nonconvex facets as defined by maxcentrum and maxangle
    -    'reason' is for reporting progress
    -    if vneighbors,
    -      calls qh_test_vneighbors at end of qh_all_merge
    -    if firstmerge,
    -      calls qh_reducevertices before qh_getmergeset
    -
    -  returns:
    -    if first call (qh.visible_list != qh.facet_list),
    -      builds qh.facet_newlist, qh.newvertex_list
    -    deleted facets added to qh.visible_list with facet->visible
    -    qh.visible_list == qh.facet_list
    -
    -  notes:
    -
    -
    -  design:
    -    if first call
    -      set qh.visible_list and qh.newfacet_list to qh.facet_list
    -      add all facets to qh.newfacet_list
    -      mark non-simplicial facets, facet->newmerge
    -      set qh.newvertext_list to qh.vertex_list
    -      add all vertices to qh.newvertex_list
    -      if a pre-merge occured
    -        set vertex->delridge {will retest the ridge}
    -        if qh.MERGEexact
    -          call qh_reducevertices()
    -      if no pre-merging
    -        merge flipped facets
    -    determine non-convex facets
    -    merge all non-convex facets
    -*/
    -void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
    -                      boolT vneighbors) {
    -  facetT *newfacet;
    -  boolT othermerges= False;
    -  vertexT *vertex;
    -
    -  if (qh REPORTfreq || qh IStracing) {
    -    qh_buildtracing(NULL, NULL);
    -    qh_printsummary(qh ferr);
    -    if (qh PRINTstatistics)
    -      qh_printallstatistics(qh ferr, "reason");
    -    qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
    -        reason, maxcentrum, maxangle);
    -  }
    -  trace2((qh ferr, 2009, "qh_postmerge: postmerge.  test vneighbors? %d\n",
    -            vneighbors));
    -  qh centrum_radius= maxcentrum;
    -  qh cos_max= maxangle;
    -  qh POSTmerging= True;
    -  qh degen_mergeset= qh_settemp(qh TEMPsize);
    -  qh facet_mergeset= qh_settemp(qh TEMPsize);
    -  if (qh visible_list != qh facet_list) {  /* first call */
    -    qh NEWfacets= True;
    -    qh visible_list= qh newfacet_list= qh facet_list;
    -    FORALLnew_facets {
    -      newfacet->newfacet= True;
    -       if (!newfacet->simplicial)
    -        newfacet->newmerge= True;
    -     zinc_(Zpostfacets);
    -    }
    -    qh newvertex_list= qh vertex_list;
    -    FORALLvertices
    -      vertex->newlist= True;
    -    if (qh VERTEXneighbors) { /* a merge has occurred */
    -      FORALLvertices
    -        vertex->delridge= True; /* test for redundant, needed? */
    -      if (qh MERGEexact) {
    -        if (qh hull_dim <= qh_DIMreduceBuild)
    -          qh_reducevertices(); /* was skipped during pre-merging */
    -      }
    -    }
    -    if (!qh PREmerge && !qh MERGEexact)
    -      qh_flippedmerges(qh newfacet_list, &othermerges);
    -  }
    -  qh_getmergeset_initial(qh newfacet_list);
    -  qh_all_merges(False, vneighbors);
    -  qh_settempfree(&qh facet_mergeset);
    -  qh_settempfree(&qh degen_mergeset);
    -} /* post_merge */
    -
    -/*---------------------------------
    -
    -  qh_all_merges( othermerge, vneighbors )
    -    merge all non-convex facets
    -
    -    set othermerge if already merged facets (for qh_reducevertices)
    -    if vneighbors
    -      tests vertex neighbors for convexity at end
    -    qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
    -    qh.degen_mergeset is defined
    -    if qh.MERGEexact && !qh.POSTmerging,
    -      does not merge coplanar facets
    -
    -  returns:
    -    deleted facets added to qh.visible_list with facet->visible
    -    deleted vertices added qh.delvertex_list with vertex->delvertex
    -
    -  notes:
    -    unless !qh.MERGEindependent,
    -      merges facets in independent sets
    -    uses qh.newfacet_list as argument since merges call qh_removefacet()
    -
    -  design:
    -    while merges occur
    -      for each merge in qh.facet_mergeset
    -        unless one of the facets was already merged in this pass
    -          merge the facets
    -        test merged facets for additional merges
    -        add merges to qh.facet_mergeset
    -      if vertices record neighboring facets
    -        rename redundant vertices
    -          update qh.facet_mergeset
    -    if vneighbors ??
    -      tests vertex neighbors for convexity at end
    -*/
    -void qh_all_merges(boolT othermerge, boolT vneighbors) {
    -  facetT *facet1, *facet2;
    -  mergeT *merge;
    -  boolT wasmerge= True, isreduce;
    -  void **freelistp;  /* used !qh_NOmem */
    -  vertexT *vertex;
    -  mergeType mergetype;
    -  int numcoplanar=0, numconcave=0, numdegenredun= 0, numnewmerges= 0;
    -
    -  trace2((qh ferr, 2010, "qh_all_merges: starting to merge facets beginning from f%d\n",
    -            getid_(qh newfacet_list)));
    -  while (True) {
    -    wasmerge= False;
    -    while (qh_setsize(qh facet_mergeset)) {
    -      while ((merge= (mergeT*)qh_setdellast(qh facet_mergeset))) {
    -        facet1= merge->facet1;
    -        facet2= merge->facet2;
    -        mergetype= merge->type;
    -        qh_memfree_(merge, (int)sizeof(mergeT), freelistp);
    -        if (facet1->visible || facet2->visible) /*deleted facet*/
    -          continue;
    -        if ((facet1->newfacet && !facet1->tested)
    -                || (facet2->newfacet && !facet2->tested)) {
    -          if (qh MERGEindependent && mergetype <= MRGanglecoplanar)
    -            continue;      /* perform independent sets of merges */
    -        }
    -        qh_merge_nonconvex(facet1, facet2, mergetype);
    -        numdegenredun += qh_merge_degenredundant();
    -        numnewmerges++;
    -        wasmerge= True;
    -        if (mergetype == MRGconcave)
    -          numconcave++;
    -        else /* MRGcoplanar or MRGanglecoplanar */
    -          numcoplanar++;
    -      } /* while setdellast */
    -      if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild
    -      && numnewmerges > qh_MAXnewmerges) {
    -        numnewmerges= 0;
    -        qh_reducevertices();  /* otherwise large post merges too slow */
    -      }
    -      qh_getmergeset(qh newfacet_list); /* facet_mergeset */
    -    } /* while mergeset */
    -    if (qh VERTEXneighbors) {
    -      isreduce= False;
    -      if (qh hull_dim >=4 && qh POSTmerging) {
    -        FORALLvertices
    -          vertex->delridge= True;
    -        isreduce= True;
    -      }
    -      if ((wasmerge || othermerge) && (!qh MERGEexact || qh POSTmerging)
    -          && qh hull_dim <= qh_DIMreduceBuild) {
    -        othermerge= False;
    -        isreduce= True;
    -      }
    -      if (isreduce) {
    -        if (qh_reducevertices()) {
    -          qh_getmergeset(qh newfacet_list); /* facet_mergeset */
    -          continue;
    -        }
    -      }
    -    }
    -    if (vneighbors && qh_test_vneighbors(/* qh newfacet_list */))
    -      continue;
    -    break;
    -  } /* while (True) */
    -  if (qh CHECKfrequently && !qh MERGEexact) {
    -    qh old_randomdist= qh RANDOMdist;
    -    qh RANDOMdist= False;
    -    qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault);
    -    /* qh_checkconnect(); [this is slow and it changes the facet order] */
    -    qh RANDOMdist= qh old_randomdist;
    -  }
    -  trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar facets %d concave facets and %d degen or redundant facets.\n",
    -    numcoplanar, numconcave, numdegenredun));
    -  if (qh IStracing >= 4 && qh num_facets < 50)
    -    qh_printlists();
    -} /* all_merges */
    -
    -
    -/*---------------------------------
    -
    -  qh_appendmergeset( facet, neighbor, mergetype, angle )
    -    appends an entry to qh.facet_mergeset or qh.degen_mergeset
    -
    -    angle ignored if NULL or !qh.ANGLEmerge
    -
    -  returns:
    -    merge appended to facet_mergeset or degen_mergeset
    -      sets ->degenerate or ->redundant if degen_mergeset
    -
    -  see:
    -    qh_test_appendmerge()
    -
    -  design:
    -    allocate merge entry
    -    if regular merge
    -      append to qh.facet_mergeset
    -    else if degenerate merge and qh.facet_mergeset is all degenerate
    -      append to qh.degen_mergeset
    -    else if degenerate merge
    -      prepend to qh.degen_mergeset
    -    else if redundant merge
    -      append to qh.degen_mergeset
    -*/
    -void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle) {
    -  mergeT *merge, *lastmerge;
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  if (facet->redundant)
    -    return;
    -  if (facet->degenerate && mergetype == MRGdegen)
    -    return;
    -  qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT);
    -  merge->facet1= facet;
    -  merge->facet2= neighbor;
    -  merge->type= mergetype;
    -  if (angle && qh ANGLEmerge)
    -    merge->angle= *angle;
    -  if (mergetype < MRGdegen)
    -    qh_setappend(&(qh facet_mergeset), merge);
    -  else if (mergetype == MRGdegen) {
    -    facet->degenerate= True;
    -    if (!(lastmerge= (mergeT*)qh_setlast(qh degen_mergeset))
    -    || lastmerge->type == MRGdegen)
    -      qh_setappend(&(qh degen_mergeset), merge);
    -    else
    -      qh_setaddnth(&(qh degen_mergeset), 0, merge);
    -  }else if (mergetype == MRGredundant) {
    -    facet->redundant= True;
    -    qh_setappend(&(qh degen_mergeset), merge);
    -  }else /* mergetype == MRGmirror */ {
    -    if (facet->redundant || neighbor->redundant) {
    -      qh_fprintf(qh ferr, 6092, "qhull error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet\n",
    -           facet->id, neighbor->id);
    -      qh_errexit2 (qh_ERRqhull, facet, neighbor);
    -    }
    -    if (!qh_setequal(facet->vertices, neighbor->vertices)) {
    -      qh_fprintf(qh ferr, 6093, "qhull error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
    -           facet->id, neighbor->id);
    -      qh_errexit2 (qh_ERRqhull, facet, neighbor);
    -    }
    -    facet->redundant= True;
    -    neighbor->redundant= True;
    -    qh_setappend(&(qh degen_mergeset), merge);
    -  }
    -} /* appendmergeset */
    -
    -
    -/*---------------------------------
    -
    -  qh_basevertices( samecycle )
    -    return temporary set of base vertices for samecycle
    -    samecycle is first facet in the cycle
    -    assumes apex is SETfirst_( samecycle->vertices )
    -
    -  returns:
    -    vertices(settemp)
    -    all ->seen are cleared
    -
    -  notes:
    -    uses qh_vertex_visit;
    -
    -  design:
    -    for each facet in samecycle
    -      for each unseen vertex in facet->vertices
    -        append to result
    -*/
    -setT *qh_basevertices(facetT *samecycle) {
    -  facetT *same;
    -  vertexT *apex, *vertex, **vertexp;
    -  setT *vertices= qh_settemp(qh TEMPsize);
    -
    -  apex= SETfirstt_(samecycle->vertices, vertexT);
    -  apex->visitid= ++qh vertex_visit;
    -  FORALLsame_cycle_(samecycle) {
    -    if (same->mergeridge)
    -      continue;
    -    FOREACHvertex_(same->vertices) {
    -      if (vertex->visitid != qh vertex_visit) {
    -        qh_setappend(&vertices, vertex);
    -        vertex->visitid= qh vertex_visit;
    -        vertex->seen= False;
    -      }
    -    }
    -  }
    -  trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n",
    -         qh_setsize(vertices)));
    -  return vertices;
    -} /* basevertices */
    -
    -/*---------------------------------
    -
    -  qh_checkconnect()
    -    check that new facets are connected
    -    new facets are on qh.newfacet_list
    -
    -  notes:
    -    this is slow and it changes the order of the facets
    -    uses qh.visit_id
    -
    -  design:
    -    move first new facet to end of qh.facet_list
    -    for all newly appended facets
    -      append unvisited neighbors to end of qh.facet_list
    -    for all new facets
    -      report error if unvisited
    -*/
    -void qh_checkconnect(void /* qh newfacet_list */) {
    -  facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;
    -
    -  facet= qh newfacet_list;
    -  qh_removefacet(facet);
    -  qh_appendfacet(facet);
    -  facet->visitid= ++qh visit_id;
    -  FORALLfacet_(facet) {
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid != qh visit_id) {
    -        qh_removefacet(neighbor);
    -        qh_appendfacet(neighbor);
    -        neighbor->visitid= qh visit_id;
    -      }
    -    }
    -  }
    -  FORALLnew_facets {
    -    if (newfacet->visitid == qh visit_id)
    -      break;
    -    qh_fprintf(qh ferr, 6094, "qhull error: f%d is not attached to the new facets\n",
    -         newfacet->id);
    -    errfacet= newfacet;
    -  }
    -  if (errfacet)
    -    qh_errexit(qh_ERRqhull, errfacet, NULL);
    -} /* checkconnect */
    -
    -/*---------------------------------
    -
    -  qh_checkzero( testall )
    -    check that facets are clearly convex for qh.DISTround with qh.MERGEexact
    -
    -    if testall,
    -      test all facets for qh.MERGEexact post-merging
    -    else
    -      test qh.newfacet_list
    -
    -    if qh.MERGEexact,
    -      allows coplanar ridges
    -      skips convexity test while qh.ZEROall_ok
    -
    -  returns:
    -    True if all facets !flipped, !dupridge, normal
    -         if all horizon facets are simplicial
    -         if all vertices are clearly below neighbor
    -         if all opposite vertices of horizon are below
    -    clears qh.ZEROall_ok if any problems or coplanar facets
    -
    -  notes:
    -    uses qh.vertex_visit
    -    horizon facets may define multiple new facets
    -
    -  design:
    -    for all facets in qh.newfacet_list or qh.facet_list
    -      check for flagged faults (flipped, etc.)
    -    for all facets in qh.newfacet_list or qh.facet_list
    -      for each neighbor of facet
    -        skip horizon facets for qh.newfacet_list
    -        test the opposite vertex
    -      if qh.newfacet_list
    -        test the other vertices in the facet's horizon facet
    -*/
    -boolT qh_checkzero(boolT testall) {
    -  facetT *facet, *neighbor, **neighborp;
    -  facetT *horizon, *facetlist;
    -  int neighbor_i;
    -  vertexT *vertex, **vertexp;
    -  realT dist;
    -
    -  if (testall)
    -    facetlist= qh facet_list;
    -  else {
    -    facetlist= qh newfacet_list;
    -    FORALLfacet_(facetlist) {
    -      horizon= SETfirstt_(facet->neighbors, facetT);
    -      if (!horizon->simplicial)
    -        goto LABELproblem;
    -      if (facet->flipped || facet->dupridge || !facet->normal)
    -        goto LABELproblem;
    -    }
    -    if (qh MERGEexact && qh ZEROall_ok) {
    -      trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
    -      return True;
    -    }
    -  }
    -  FORALLfacet_(facetlist) {
    -    qh vertex_visit++;
    -    neighbor_i= 0;
    -    horizon= NULL;
    -    FOREACHneighbor_(facet) {
    -      if (!neighbor_i && !testall) {
    -        horizon= neighbor;
    -        neighbor_i++;
    -        continue; /* horizon facet tested in qh_findhorizon */
    -      }
    -      vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
    -      vertex->visitid= qh vertex_visit;
    -      zzinc_(Zdistzero);
    -      qh_distplane(vertex->point, neighbor, &dist);
    -      if (dist >= -qh DISTround) {
    -        qh ZEROall_ok= False;
    -        if (!qh MERGEexact || testall || dist > qh DISTround)
    -          goto LABELnonconvex;
    -      }
    -    }
    -    if (!testall) {
    -      FOREACHvertex_(horizon->vertices) {
    -        if (vertex->visitid != qh vertex_visit) {
    -          zzinc_(Zdistzero);
    -          qh_distplane(vertex->point, facet, &dist);
    -          if (dist >= -qh DISTround) {
    -            qh ZEROall_ok= False;
    -            if (!qh MERGEexact || dist > qh DISTround)
    -              goto LABELnonconvex;
    -          }
    -          break;
    -        }
    -      }
    -    }
    -  }
    -  trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
    -        (qh MERGEexact && !testall) ?
    -           "not concave, flipped, or duplicate ridged" : "clearly convex"));
    -  return True;
    -
    - LABELproblem:
    -  qh ZEROall_ok= False;
    -  trace2((qh ferr, 2013, "qh_checkzero: facet f%d needs pre-merging\n",
    -       facet->id));
    -  return False;
    -
    - LABELnonconvex:
    -  trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex.  v%d dist %.2g\n",
    -         facet->id, neighbor->id, vertex->id, dist));
    -  return False;
    -} /* checkzero */
    -
    -/*---------------------------------
    -
    -  qh_compareangle( angle1, angle2 )
    -    used by qsort() to order merges by angle
    -*/
    -int qh_compareangle(const void *p1, const void *p2) {
    -  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
    -
    -  return((a->angle > b->angle) ? 1 : -1);
    -} /* compareangle */
    -
    -/*---------------------------------
    -
    -  qh_comparemerge( merge1, merge2 )
    -    used by qsort() to order merges
    -*/
    -int qh_comparemerge(const void *p1, const void *p2) {
    -  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);
    -
    -  return(a->type - b->type);
    -} /* comparemerge */
    -
    -/*---------------------------------
    -
    -  qh_comparevisit( vertex1, vertex2 )
    -    used by qsort() to order vertices by their visitid
    -*/
    -int qh_comparevisit(const void *p1, const void *p2) {
    -  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);
    -
    -  return(a->visitid - b->visitid);
    -} /* comparevisit */
    -
    -/*---------------------------------
    -
    -  qh_copynonconvex( atridge )
    -    set non-convex flag on other ridges (if any) between same neighbors
    -
    -  notes:
    -    may be faster if use smaller ridge set
    -
    -  design:
    -    for each ridge of atridge's top facet
    -      if ridge shares the same neighbor
    -        set nonconvex flag
    -*/
    -void qh_copynonconvex(ridgeT *atridge) {
    -  facetT *facet, *otherfacet;
    -  ridgeT *ridge, **ridgep;
    -
    -  facet= atridge->top;
    -  otherfacet= atridge->bottom;
    -  FOREACHridge_(facet->ridges) {
    -    if (otherfacet == otherfacet_(ridge, facet) && ridge != atridge) {
    -      ridge->nonconvex= True;
    -      trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d\n",
    -              atridge->id, ridge->id));
    -      break;
    -    }
    -  }
    -} /* copynonconvex */
    -
    -/*---------------------------------
    -
    -  qh_degen_redundant_facet( facet )
    -    check facet for degen. or redundancy
    -
    -  notes:
    -    bumps vertex_visit
    -    called if a facet was redundant but no longer is (qh_merge_degenredundant)
    -    qh_appendmergeset() only appends first reference to facet (i.e., redundant)
    -
    -  see:
    -    qh_degen_redundant_neighbors()
    -
    -  design:
    -    test for redundant neighbor
    -    test for degenerate facet
    -*/
    -void qh_degen_redundant_facet(facetT *facet) {
    -  vertexT *vertex, **vertexp;
    -  facetT *neighbor, **neighborp;
    -
    -  trace4((qh ferr, 4021, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
    -          facet->id));
    -  FOREACHneighbor_(facet) {
    -    qh vertex_visit++;
    -    FOREACHvertex_(neighbor->vertices)
    -      vertex->visitid= qh vertex_visit;
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->visitid != qh vertex_visit)
    -        break;
    -    }
    -    if (!vertex) {
    -      qh_appendmergeset(facet, neighbor, MRGredundant, NULL);
    -      trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d.  merge\n", facet->id, neighbor->id));
    -      return;
    -    }
    -  }
    -  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    -    qh_appendmergeset(facet, facet, MRGdegen, NULL);
    -    trace2((qh ferr, 2016, "qh_degen_redundant_neighbors: f%d is degenerate.\n", facet->id));
    -  }
    -} /* degen_redundant_facet */
    -
    -
    -/*---------------------------------
    -
    -  qh_degen_redundant_neighbors( facet, delfacet,  )
    -    append degenerate and redundant neighbors to facet_mergeset
    -    if delfacet,
    -      only checks neighbors of both delfacet and facet
    -    also checks current facet for degeneracy
    -
    -  notes:
    -    bumps vertex_visit
    -    called for each qh_mergefacet() and qh_mergecycle()
    -    merge and statistics occur in merge_nonconvex
    -    qh_appendmergeset() only appends first reference to facet (i.e., redundant)
    -      it appends redundant facets after degenerate ones
    -
    -    a degenerate facet has fewer than hull_dim neighbors
    -    a redundant facet's vertices is a subset of its neighbor's vertices
    -    tests for redundant merges first (appendmergeset is nop for others)
    -    in a merge, only needs to test neighbors of merged facet
    -
    -  see:
    -    qh_merge_degenredundant() and qh_degen_redundant_facet()
    -
    -  design:
    -    test for degenerate facet
    -    test for redundant neighbor
    -    test for degenerate neighbor
    -*/
    -void qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet) {
    -  vertexT *vertex, **vertexp;
    -  facetT *neighbor, **neighborp;
    -  int size;
    -
    -  trace4((qh ferr, 4022, "qh_degen_redundant_neighbors: test neighbors of f%d with delfacet f%d\n",
    -          facet->id, getid_(delfacet)));
    -  if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) {
    -    qh_appendmergeset(facet, facet, MRGdegen, NULL);
    -    trace2((qh ferr, 2017, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
    -  }
    -  if (!delfacet)
    -    delfacet= facet;
    -  qh vertex_visit++;
    -  FOREACHvertex_(facet->vertices)
    -    vertex->visitid= qh vertex_visit;
    -  FOREACHneighbor_(delfacet) {
    -    /* uses early out instead of checking vertex count */
    -    if (neighbor == facet)
    -      continue;
    -    FOREACHvertex_(neighbor->vertices) {
    -      if (vertex->visitid != qh vertex_visit)
    -        break;
    -    }
    -    if (!vertex) {
    -      qh_appendmergeset(neighbor, facet, MRGredundant, NULL);
    -      trace2((qh ferr, 2018, "qh_degen_redundant_neighbors: f%d is contained in f%d.  merge\n", neighbor->id, facet->id));
    -    }
    -  }
    -  FOREACHneighbor_(delfacet) {   /* redundant merges occur first */
    -    if (neighbor == facet)
    -      continue;
    -    if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) {
    -      qh_appendmergeset(neighbor, neighbor, MRGdegen, NULL);
    -      trace2((qh ferr, 2019, "qh_degen_redundant_neighbors: f%d is degenerate with %d neighbors.  Neighbor of f%d.\n", neighbor->id, size, facet->id));
    -    }
    -  }
    -} /* degen_redundant_neighbors */
    -
    -
    -/*---------------------------------
    -
    -  qh_find_newvertex( oldvertex, vertices, ridges )
    -    locate new vertex for renaming old vertex
    -    vertices is a set of possible new vertices
    -      vertices sorted by number of deleted ridges
    -
    -  returns:
    -    newvertex or NULL
    -      each ridge includes both vertex and oldvertex
    -    vertices sorted by number of deleted ridges
    -
    -  notes:
    -    modifies vertex->visitid
    -    new vertex is in one of the ridges
    -    renaming will not cause a duplicate ridge
    -    renaming will minimize the number of deleted ridges
    -    newvertex may not be adjacent in the dual (though unlikely)
    -
    -  design:
    -    for each vertex in vertices
    -      set vertex->visitid to number of references in ridges
    -    remove unvisited vertices
    -    set qh.vertex_visit above all possible values
    -    sort vertices by number of references in ridges
    -    add each ridge to qh.hash_table
    -    for each vertex in vertices
    -      look for a vertex that would not cause a duplicate ridge after a rename
    -*/
    -vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) {
    -  vertexT *vertex, **vertexp;
    -  setT *newridges;
    -  ridgeT *ridge, **ridgep;
    -  int size, hashsize;
    -  int hash;
    -
    -#ifndef qh_NOtrace
    -  if (qh IStracing >= 4) {
    -    qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
    -             oldvertex->id);
    -    FOREACHvertex_(vertices)
    -      qh_fprintf(qh ferr, 8064, "v%d ", vertex->id);
    -    FOREACHridge_(ridges)
    -      qh_fprintf(qh ferr, 8065, "r%d ", ridge->id);
    -    qh_fprintf(qh ferr, 8066, "\n");
    -  }
    -#endif
    -  FOREACHvertex_(vertices)
    -    vertex->visitid= 0;
    -  FOREACHridge_(ridges) {
    -    FOREACHvertex_(ridge->vertices)
    -      vertex->visitid++;
    -  }
    -  FOREACHvertex_(vertices) {
    -    if (!vertex->visitid) {
    -      qh_setdelnth(vertices, SETindex_(vertices,vertex));
    -      vertexp--; /* repeat since deleted this vertex */
    -    }
    -  }
    -  qh vertex_visit += (unsigned int)qh_setsize(ridges);
    -  if (!qh_setsize(vertices)) {
    -    trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
    -            oldvertex->id));
    -    return NULL;
    -  }
    -  qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices),
    -                sizeof(vertexT *), qh_comparevisit);
    -  /* can now use qh vertex_visit */
    -  if (qh PRINTstatistics) {
    -    size= qh_setsize(vertices);
    -    zinc_(Zintersect);
    -    zadd_(Zintersecttot, size);
    -    zmax_(Zintersectmax, size);
    -  }
    -  hashsize= qh_newhashtable(qh_setsize(ridges));
    -  FOREACHridge_(ridges)
    -    qh_hashridge(qh hash_table, hashsize, ridge, oldvertex);
    -  FOREACHvertex_(vertices) {
    -    newridges= qh_vertexridges(vertex);
    -    FOREACHridge_(newridges) {
    -      if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
    -        zinc_(Zdupridge);
    -        break;
    -      }
    -    }
    -    qh_settempfree(&newridges);
    -    if (!ridge)
    -      break;  /* found a rename */
    -  }
    -  if (vertex) {
    -    /* counted in qh_renamevertex */
    -    trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
    -      vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges)));
    -  }else {
    -    zinc_(Zfindfail);
    -    trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d(all duplicated ridges) during p%d\n",
    -      oldvertex->id, qh furthest_id));
    -  }
    -  qh_setfree(&qh hash_table);
    -  return vertex;
    -} /* find_newvertex */
    -
    -/*---------------------------------
    -
    -  qh_findbest_test( testcentrum, facet, neighbor, bestfacet, dist, mindist, maxdist )
    -    test neighbor of facet for qh_findbestneighbor()
    -    if testcentrum,
    -      tests centrum (assumes it is defined)
    -    else
    -      tests vertices
    -
    -  returns:
    -    if a better facet (i.e., vertices/centrum of facet closer to neighbor)
    -      updates bestfacet, dist, mindist, and maxdist
    -*/
    -void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
    -      facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
    -  realT dist, mindist, maxdist;
    -
    -  if (testcentrum) {
    -    zzinc_(Zbestdist);
    -    qh_distplane(facet->center, neighbor, &dist);
    -    dist *= qh hull_dim; /* estimate furthest vertex */
    -    if (dist < 0) {
    -      maxdist= 0;
    -      mindist= dist;
    -      dist= -dist;
    -    }else {
    -      mindist= 0;
    -      maxdist= dist;
    -    }
    -  }else
    -    dist= qh_getdistance(facet, neighbor, &mindist, &maxdist);
    -  if (dist < *distp) {
    -    *bestfacet= neighbor;
    -    *mindistp= mindist;
    -    *maxdistp= maxdist;
    -    *distp= dist;
    -  }
    -} /* findbest_test */
    -
    -/*---------------------------------
    -
    -  qh_findbestneighbor( facet, dist, mindist, maxdist )
    -    finds best neighbor (least dist) of a facet for merging
    -
    -  returns:
    -    returns min and max distances and their max absolute value
    -
    -  notes:
    -    avoids merging old into new
    -    assumes ridge->nonconvex only set on one ridge between a pair of facets
    -    could use an early out predicate but not worth it
    -
    -  design:
    -    if a large facet
    -      will test centrum
    -    else
    -      will test vertices
    -    if a large facet
    -      test nonconvex neighbors for best merge
    -    else
    -      test all neighbors for the best merge
    -    if testing centrum
    -      get distance information
    -*/
    -facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
    -  facetT *neighbor, **neighborp, *bestfacet= NULL;
    -  ridgeT *ridge, **ridgep;
    -  boolT nonconvex= True, testcentrum= False;
    -  int size= qh_setsize(facet->vertices);
    -
    -  *distp= REALmax;
    -  if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) {
    -    testcentrum= True;
    -    zinc_(Zbestcentrum);
    -    if (!facet->center)
    -       facet->center= qh_getcentrum(facet);
    -  }
    -  if (size > qh hull_dim + qh_BESTnonconvex) {
    -    FOREACHridge_(facet->ridges) {
    -      if (ridge->nonconvex) {
    -        neighbor= otherfacet_(ridge, facet);
    -        qh_findbest_test(testcentrum, facet, neighbor,
    -                          &bestfacet, distp, mindistp, maxdistp);
    -      }
    -    }
    -  }
    -  if (!bestfacet) {
    -    nonconvex= False;
    -    FOREACHneighbor_(facet)
    -      qh_findbest_test(testcentrum, facet, neighbor,
    -                        &bestfacet, distp, mindistp, maxdistp);
    -  }
    -  if (!bestfacet) {
    -    qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
    -
    -    qh_errexit(qh_ERRqhull, facet, NULL);
    -  }
    -  if (testcentrum)
    -    qh_getdistance(facet, bestfacet, mindistp, maxdistp);
    -  trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
    -     bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
    -  return(bestfacet);
    -} /* findbestneighbor */
    -
    -
    -/*---------------------------------
    -
    -  qh_flippedmerges( facetlist, wasmerge )
    -    merge flipped facets into best neighbor
    -    assumes qh.facet_mergeset at top of temporary stack
    -
    -  returns:
    -    no flipped facets on facetlist
    -    sets wasmerge if merge occurred
    -    degen/redundant merges passed through
    -
    -  notes:
    -    othermerges not needed since qh.facet_mergeset is empty before & after
    -      keep it in case of change
    -
    -  design:
    -    append flipped facets to qh.facetmergeset
    -    for each flipped merge
    -      find best neighbor
    -      merge facet into neighbor
    -      merge degenerate and redundant facets
    -    remove flipped merges from qh.facet_mergeset
    -*/
    -void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) {
    -  facetT *facet, *neighbor, *facet1;
    -  realT dist, mindist, maxdist;
    -  mergeT *merge, **mergep;
    -  setT *othermerges;
    -  int nummerge=0;
    -
    -  trace4((qh ferr, 4024, "qh_flippedmerges: begin\n"));
    -  FORALLfacet_(facetlist) {
    -    if (facet->flipped && !facet->visible)
    -      qh_appendmergeset(facet, facet, MRGflip, NULL);
    -  }
    -  othermerges= qh_settemppop(); /* was facet_mergeset */
    -  qh facet_mergeset= qh_settemp(qh TEMPsize);
    -  qh_settemppush(othermerges);
    -  FOREACHmerge_(othermerges) {
    -    facet1= merge->facet1;
    -    if (merge->type != MRGflip || facet1->visible)
    -      continue;
    -    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    -      qhmem.IStracing= qh IStracing= qh TRACElevel;
    -    neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
    -    trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
    -      facet1->id, neighbor->id, dist, qh furthest_id));
    -    qh_mergefacet(facet1, neighbor, &mindist, &maxdist, !qh_MERGEapex);
    -    nummerge++;
    -    if (qh PRINTstatistics) {
    -      zinc_(Zflipped);
    -      wadd_(Wflippedtot, dist);
    -      wmax_(Wflippedmax, dist);
    -    }
    -    qh_merge_degenredundant();
    -  }
    -  FOREACHmerge_(othermerges) {
    -    if (merge->facet1->visible || merge->facet2->visible)
    -      qh_memfree(merge, (int)sizeof(mergeT));
    -    else
    -      qh_setappend(&qh facet_mergeset, merge);
    -  }
    -  qh_settempfree(&othermerges);
    -  if (nummerge)
    -    *wasmerge= True;
    -  trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped facets into a good neighbor\n", nummerge));
    -} /* flippedmerges */
    -
    -
    -/*---------------------------------
    -
    -  qh_forcedmerges( wasmerge )
    -    merge duplicated ridges
    -
    -  returns:
    -    removes all duplicate ridges on facet_mergeset
    -    wasmerge set if merge
    -    qh.facet_mergeset may include non-forced merges(none for now)
    -    qh.degen_mergeset includes degen/redun merges
    -
    -  notes:
    -    duplicate ridges occur when the horizon is pinched,
    -        i.e. a subridge occurs in more than two horizon ridges.
    -     could rename vertices that pinch the horizon
    -    assumes qh_merge_degenredundant() has not be called
    -    othermerges isn't needed since facet_mergeset is empty afterwards
    -      keep it in case of change
    -
    -  design:
    -    for each duplicate ridge
    -      find current facets by chasing f.replace links
    -      determine best direction for facet
    -      merge one facet into the other
    -      remove duplicate ridges from qh.facet_mergeset
    -*/
    -void qh_forcedmerges(boolT *wasmerge) {
    -  facetT *facet1, *facet2;
    -  mergeT *merge, **mergep;
    -  realT dist1, dist2, mindist1, mindist2, maxdist1, maxdist2;
    -  setT *othermerges;
    -  int nummerge=0, numflip=0;
    -
    -  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    -    qhmem.IStracing= qh IStracing= qh TRACElevel;
    -  trace4((qh ferr, 4025, "qh_forcedmerges: begin\n"));
    -  othermerges= qh_settemppop(); /* was facet_mergeset */
    -  qh facet_mergeset= qh_settemp(qh TEMPsize);
    -  qh_settemppush(othermerges);
    -  FOREACHmerge_(othermerges) {
    -    if (merge->type != MRGridge)
    -        continue;
    -    facet1= merge->facet1;
    -    facet2= merge->facet2;
    -    while (facet1->visible)      /* must exist, no qh_merge_degenredunant */
    -      facet1= facet1->f.replace; /* previously merged facet */
    -    while (facet2->visible)
    -      facet2= facet2->f.replace; /* previously merged facet */
    -    if (facet1 == facet2)
    -      continue;
    -    if (!qh_setin(facet2->neighbors, facet1)) {
    -      qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a duplicate ridge but as f%d and f%d they are no longer neighbors\n",
    -               merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
    -      qh_errexit2 (qh_ERRqhull, facet1, facet2);
    -    }
    -    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    -      qhmem.IStracing= qh IStracing= qh TRACElevel;
    -    dist1= qh_getdistance(facet1, facet2, &mindist1, &maxdist1);
    -    dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2);
    -    trace0((qh ferr, 16, "qh_forcedmerges: duplicate ridge between f%d and f%d, dist %2.2g and reverse dist %2.2g during p%d\n",
    -            facet1->id, facet2->id, dist1, dist2, qh furthest_id));
    -    if (dist1 < dist2)
    -      qh_mergefacet(facet1, facet2, &mindist1, &maxdist1, !qh_MERGEapex);
    -    else {
    -      qh_mergefacet(facet2, facet1, &mindist2, &maxdist2, !qh_MERGEapex);
    -      dist1= dist2;
    -      facet1= facet2;
    -    }
    -    if (facet1->flipped) {
    -      zinc_(Zmergeflipdup);
    -      numflip++;
    -    }else
    -      nummerge++;
    -    if (qh PRINTstatistics) {
    -      zinc_(Zduplicate);
    -      wadd_(Wduplicatetot, dist1);
    -      wmax_(Wduplicatemax, dist1);
    -    }
    -  }
    -  FOREACHmerge_(othermerges) {
    -    if (merge->type == MRGridge)
    -      qh_memfree(merge, (int)sizeof(mergeT));
    -    else
    -      qh_setappend(&qh facet_mergeset, merge);
    -  }
    -  qh_settempfree(&othermerges);
    -  if (nummerge)
    -    *wasmerge= True;
    -  trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets and %d flipped facets across duplicated ridges\n",
    -                nummerge, numflip));
    -} /* forcedmerges */
    -
    -
    -/*---------------------------------
    -
    -  qh_getmergeset( facetlist )
    -    determines nonconvex facets on facetlist
    -    tests !tested ridges and nonconvex ridges of !tested facets
    -
    -  returns:
    -    returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
    -    all ridges tested
    -
    -  notes:
    -    assumes no nonconvex ridges with both facets tested
    -    uses facet->tested/ridge->tested to prevent duplicate tests
    -    can not limit tests to modified ridges since the centrum changed
    -    uses qh.visit_id
    -
    -  see:
    -    qh_getmergeset_initial()
    -
    -  design:
    -    for each facet on facetlist
    -      for each ridge of facet
    -        if untested ridge
    -          test ridge for convexity
    -          if non-convex
    -            append ridge to qh.facet_mergeset
    -    sort qh.facet_mergeset by angle
    -*/
    -void qh_getmergeset(facetT *facetlist) {
    -  facetT *facet, *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -  int nummerges;
    -
    -  nummerges= qh_setsize(qh facet_mergeset);
    -  trace4((qh ferr, 4026, "qh_getmergeset: started.\n"));
    -  qh visit_id++;
    -  FORALLfacet_(facetlist) {
    -    if (facet->tested)
    -      continue;
    -    facet->visitid= qh visit_id;
    -    facet->tested= True;  /* must be non-simplicial due to merge */
    -    FOREACHneighbor_(facet)
    -      neighbor->seen= False;
    -    FOREACHridge_(facet->ridges) {
    -      if (ridge->tested && !ridge->nonconvex)
    -        continue;
    -      /* if tested & nonconvex, need to append merge */
    -      neighbor= otherfacet_(ridge, facet);
    -      if (neighbor->seen) {
    -        ridge->tested= True;
    -        ridge->nonconvex= False;
    -      }else if (neighbor->visitid != qh visit_id) {
    -        ridge->tested= True;
    -        ridge->nonconvex= False;
    -        neighbor->seen= True;      /* only one ridge is marked nonconvex */
    -        if (qh_test_appendmerge(facet, neighbor))
    -          ridge->nonconvex= True;
    -      }
    -    }
    -  }
    -  nummerges= qh_setsize(qh facet_mergeset);
    -  if (qh ANGLEmerge)
    -    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
    -  else
    -    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
    -  if (qh POSTmerging) {
    -    zadd_(Zmergesettot2, nummerges);
    -  }else {
    -    zadd_(Zmergesettot, nummerges);
    -    zmax_(Zmergesetmax, nummerges);
    -  }
    -  trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
    -} /* getmergeset */
    -
    -
    -/*---------------------------------
    -
    -  qh_getmergeset_initial( facetlist )
    -    determine initial qh.facet_mergeset for facets
    -    tests all facet/neighbor pairs on facetlist
    -
    -  returns:
    -    sorted qh.facet_mergeset with nonconvex ridges
    -    sets facet->tested, ridge->tested, and ridge->nonconvex
    -
    -  notes:
    -    uses visit_id, assumes ridge->nonconvex is False
    -
    -  see:
    -    qh_getmergeset()
    -
    -  design:
    -    for each facet on facetlist
    -      for each untested neighbor of facet
    -        test facet and neighbor for convexity
    -        if non-convex
    -          append merge to qh.facet_mergeset
    -          mark one of the ridges as nonconvex
    -    sort qh.facet_mergeset by angle
    -*/
    -void qh_getmergeset_initial(facetT *facetlist) {
    -  facetT *facet, *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -  int nummerges;
    -
    -  qh visit_id++;
    -  FORALLfacet_(facetlist) {
    -    facet->visitid= qh visit_id;
    -    facet->tested= True;
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->visitid != qh visit_id) {
    -        if (qh_test_appendmerge(facet, neighbor)) {
    -          FOREACHridge_(neighbor->ridges) {
    -            if (facet == otherfacet_(ridge, neighbor)) {
    -              ridge->nonconvex= True;
    -              break;    /* only one ridge is marked nonconvex */
    -            }
    -          }
    -        }
    -      }
    -    }
    -    FOREACHridge_(facet->ridges)
    -      ridge->tested= True;
    -  }
    -  nummerges= qh_setsize(qh facet_mergeset);
    -  if (qh ANGLEmerge)
    -    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compareangle);
    -  else
    -    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_comparemerge);
    -  if (qh POSTmerging) {
    -    zadd_(Zmergeinittot2, nummerges);
    -  }else {
    -    zadd_(Zmergeinittot, nummerges);
    -    zmax_(Zmergeinitmax, nummerges);
    -  }
    -  trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
    -} /* getmergeset_initial */
    -
    -
    -/*---------------------------------
    -
    -  qh_hashridge( hashtable, hashsize, ridge, oldvertex )
    -    add ridge to hashtable without oldvertex
    -
    -  notes:
    -    assumes hashtable is large enough
    -
    -  design:
    -    determine hash value for ridge without oldvertex
    -    find next empty slot for ridge
    -*/
    -void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
    -  int hash;
    -  ridgeT *ridgeA;
    -
    -  hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex);
    -  while (True) {
    -    if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
    -      SETelem_(hashtable, hash)= ridge;
    -      break;
    -    }else if (ridgeA == ridge)
    -      break;
    -    if (++hash == hashsize)
    -      hash= 0;
    -  }
    -} /* hashridge */
    -
    -
    -/*---------------------------------
    -
    -  qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
    -    returns matching ridge without oldvertex in hashtable
    -      for ridge without vertex
    -    if oldvertex is NULL
    -      matches with any one skip
    -
    -  returns:
    -    matching ridge or NULL
    -    if no match,
    -      if ridge already in   table
    -        hashslot= -1
    -      else
    -        hashslot= next NULL index
    -
    -  notes:
    -    assumes hashtable is large enough
    -    can't match ridge to itself
    -
    -  design:
    -    get hash value for ridge without vertex
    -    for each hashslot
    -      return match if ridge matches ridgeA without oldvertex
    -*/
    -ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
    -              vertexT *vertex, vertexT *oldvertex, int *hashslot) {
    -  int hash;
    -  ridgeT *ridgeA;
    -
    -  *hashslot= 0;
    -  zinc_(Zhashridge);
    -  hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex);
    -  while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
    -    if (ridgeA == ridge)
    -      *hashslot= -1;
    -    else {
    -      zinc_(Zhashridgetest);
    -      if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
    -        return ridgeA;
    -    }
    -    if (++hash == hashsize)
    -      hash= 0;
    -  }
    -  if (!*hashslot)
    -    *hashslot= hash;
    -  return NULL;
    -} /* hashridge_find */
    -
    -
    -/*---------------------------------
    -
    -  qh_makeridges( facet )
    -    creates explicit ridges between simplicial facets
    -
    -  returns:
    -    facet with ridges and without qh_MERGEridge
    -    ->simplicial is False
    -
    -  notes:
    -    allows qh_MERGEridge flag
    -    uses existing ridges
    -    duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)
    -
    -  see:
    -    qh_mergecycle_ridges()
    -
    -  design:
    -    look for qh_MERGEridge neighbors
    -    mark neighbors that already have ridges
    -    for each unprocessed neighbor of facet
    -      create a ridge for neighbor and facet
    -    if any qh_MERGEridge neighbors
    -      delete qh_MERGEridge flags (already handled by qh_mark_dupridges)
    -*/
    -void qh_makeridges(facetT *facet) {
    -  facetT *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -  int neighbor_i, neighbor_n;
    -  boolT toporient, mergeridge= False;
    -
    -  if (!facet->simplicial)
    -    return;
    -  trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
    -  facet->simplicial= False;
    -  FOREACHneighbor_(facet) {
    -    if (neighbor == qh_MERGEridge)
    -      mergeridge= True;
    -    else
    -      neighbor->seen= False;
    -  }
    -  FOREACHridge_(facet->ridges)
    -    otherfacet_(ridge, facet)->seen= True;
    -  FOREACHneighbor_i_(facet) {
    -    if (neighbor == qh_MERGEridge)
    -      continue;  /* fixed by qh_mark_dupridges */
    -    else if (!neighbor->seen) {  /* no current ridges */
    -      ridge= qh_newridge();
    -      ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
    -                                                          neighbor_i, 0);
    -      toporient= facet->toporient ^ (neighbor_i & 0x1);
    -      if (toporient) {
    -        ridge->top= facet;
    -        ridge->bottom= neighbor;
    -      }else {
    -        ridge->top= neighbor;
    -        ridge->bottom= facet;
    -      }
    -#if 0 /* this also works */
    -      flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
    -      if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
    -        ridge->top= neighbor;
    -        ridge->bottom= facet;
    -      }else {
    -        ridge->top= facet;
    -        ridge->bottom= neighbor;
    -      }
    -#endif
    -      qh_setappend(&(facet->ridges), ridge);
    -      qh_setappend(&(neighbor->ridges), ridge);
    -    }
    -  }
    -  if (mergeridge) {
    -    while (qh_setdel(facet->neighbors, qh_MERGEridge))
    -      ; /* delete each one */
    -  }
    -} /* makeridges */
    -
    -
    -/*---------------------------------
    -
    -  qh_mark_dupridges( facetlist )
    -    add duplicated ridges to qh.facet_mergeset
    -    facet->dupridge is true
    -
    -  returns:
    -    duplicate ridges on qh.facet_mergeset
    -    ->mergeridge/->mergeridge2 set
    -    duplicate ridges marked by qh_MERGEridge and both sides facet->dupridge
    -    no MERGEridges in neighbor sets
    -
    -  notes:
    -    duplicate ridges occur when the horizon is pinched,
    -        i.e. a subridge occurs in more than two horizon ridges.
    -    could rename vertices that pinch the horizon
    -    uses qh.visit_id
    -
    -  design:
    -    for all facets on facetlist
    -      if facet contains a duplicate ridge
    -        for each neighbor of facet
    -          if neighbor marked qh_MERGEridge (one side of the merge)
    -            set facet->mergeridge
    -          else
    -            if neighbor contains a duplicate ridge
    -            and the back link is qh_MERGEridge
    -              append duplicate ridge to qh.facet_mergeset
    -   for each duplicate ridge
    -     make ridge sets in preparation for merging
    -     remove qh_MERGEridge from neighbor set
    -   for each duplicate ridge
    -     restore the missing neighbor from the neighbor set that was qh_MERGEridge
    -     add the missing ridge for this neighbor
    -*/
    -void qh_mark_dupridges(facetT *facetlist) {
    -  facetT *facet, *neighbor, **neighborp;
    -  int nummerge=0;
    -  mergeT *merge, **mergep;
    -
    -
    -  trace4((qh ferr, 4028, "qh_mark_dupridges: identify duplicate ridges\n"));
    -  FORALLfacet_(facetlist) {
    -    if (facet->dupridge) {
    -      FOREACHneighbor_(facet) {
    -        if (neighbor == qh_MERGEridge) {
    -          facet->mergeridge= True;
    -          continue;
    -        }
    -        if (neighbor->dupridge
    -        && !qh_setin(neighbor->neighbors, facet)) { /* qh_MERGEridge */
    -          qh_appendmergeset(facet, neighbor, MRGridge, NULL);
    -          facet->mergeridge2= True;
    -          facet->mergeridge= True;
    -          nummerge++;
    -        }
    -      }
    -    }
    -  }
    -  if (!nummerge)
    -    return;
    -  FORALLfacet_(facetlist) {            /* gets rid of qh_MERGEridge */
    -    if (facet->mergeridge && !facet->mergeridge2)
    -      qh_makeridges(facet);
    -  }
    -  FOREACHmerge_(qh facet_mergeset) {   /* restore the missing neighbors */
    -    if (merge->type == MRGridge) {
    -      qh_setappend(&merge->facet2->neighbors, merge->facet1);
    -      qh_makeridges(merge->facet1);   /* and the missing ridges */
    -    }
    -  }
    -  trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges\n",
    -                nummerge));
    -} /* mark_dupridges */
    -
    -/*---------------------------------
    -
    -  qh_maydropneighbor( facet )
    -    drop neighbor relationship if no ridge between facet and neighbor
    -
    -  returns:
    -    neighbor sets updated
    -    appends degenerate facets to qh.facet_mergeset
    -
    -  notes:
    -    won't cause redundant facets since vertex inclusion is the same
    -    may drop vertex and neighbor if no ridge
    -    uses qh.visit_id
    -
    -  design:
    -    visit all neighbors with ridges
    -    for each unvisited neighbor of facet
    -      delete neighbor and facet from the neighbor sets
    -      if neighbor becomes degenerate
    -        append neighbor to qh.degen_mergeset
    -    if facet is degenerate
    -      append facet to qh.degen_mergeset
    -*/
    -void qh_maydropneighbor(facetT *facet) {
    -  ridgeT *ridge, **ridgep;
    -  realT angledegen= qh_ANGLEdegen;
    -  facetT *neighbor, **neighborp;
    -
    -  qh visit_id++;
    -  trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
    -          facet->id));
    -  FOREACHridge_(facet->ridges) {
    -    ridge->top->visitid= qh visit_id;
    -    ridge->bottom->visitid= qh visit_id;
    -  }
    -  FOREACHneighbor_(facet) {
    -    if (neighbor->visitid != qh visit_id) {
    -      trace0((qh ferr, 17, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors during p%d\n",
    -            facet->id, neighbor->id, qh furthest_id));
    -      zinc_(Zdropneighbor);
    -      qh_setdel(facet->neighbors, neighbor);
    -      neighborp--;  /* repeat, deleted a neighbor */
    -      qh_setdel(neighbor->neighbors, facet);
    -      if (qh_setsize(neighbor->neighbors) < qh hull_dim) {
    -        zinc_(Zdropdegen);
    -        qh_appendmergeset(neighbor, neighbor, MRGdegen, &angledegen);
    -        trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
    -      }
    -    }
    -  }
    -  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    -    zinc_(Zdropdegen);
    -    qh_appendmergeset(facet, facet, MRGdegen, &angledegen);
    -    trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
    -  }
    -} /* maydropneighbor */
    -
    -
    -/*---------------------------------
    -
    -  qh_merge_degenredundant()
    -    merge all degenerate and redundant facets
    -    qh.degen_mergeset contains merges from qh_degen_redundant_neighbors()
    -
    -  returns:
    -    number of merges performed
    -    resets facet->degenerate/redundant
    -    if deleted (visible) facet has no neighbors
    -      sets ->f.replace to NULL
    -
    -  notes:
    -    redundant merges happen before degenerate ones
    -    merging and renaming vertices can result in degen/redundant facets
    -
    -  design:
    -    for each merge on qh.degen_mergeset
    -      if redundant merge
    -        if non-redundant facet merged into redundant facet
    -          recheck facet for redundancy
    -        else
    -          merge redundant facet into other facet
    -*/
    -int qh_merge_degenredundant(void) {
    -  int size;
    -  mergeT *merge;
    -  facetT *bestneighbor, *facet1, *facet2;
    -  realT dist, mindist, maxdist;
    -  vertexT *vertex, **vertexp;
    -  int nummerges= 0;
    -  mergeType mergetype;
    -
    -  while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
    -    facet1= merge->facet1;
    -    facet2= merge->facet2;
    -    mergetype= merge->type;
    -    qh_memfree(merge, (int)sizeof(mergeT));
    -    if (facet1->visible)
    -      continue;
    -    facet1->degenerate= False;
    -    facet1->redundant= False;
    -    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    -      qhmem.IStracing= qh IStracing= qh TRACElevel;
    -    if (mergetype == MRGredundant) {
    -      zinc_(Zneighbor);
    -      while (facet2->visible) {
    -        if (!facet2->f.replace) {
    -          qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d redundant but f%d has no replacement\n",
    -               facet1->id, facet2->id);
    -          qh_errexit2 (qh_ERRqhull, facet1, facet2);
    -        }
    -        facet2= facet2->f.replace;
    -      }
    -      if (facet1 == facet2) {
    -        qh_degen_redundant_facet(facet1); /* in case of others */
    -        continue;
    -      }
    -      trace2((qh ferr, 2025, "qh_merge_degenredundant: facet f%d is contained in f%d, will merge\n",
    -            facet1->id, facet2->id));
    -      qh_mergefacet(facet1, facet2, NULL, NULL, !qh_MERGEapex);
    -      /* merge distance is already accounted for */
    -      nummerges++;
    -    }else {  /* mergetype == MRGdegen, other merges may have fixed */
    -      if (!(size= qh_setsize(facet1->neighbors))) {
    -        zinc_(Zdelfacetdup);
    -        trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors.  Deleted\n", facet1->id));
    -        qh_willdelete(facet1, NULL);
    -        FOREACHvertex_(facet1->vertices) {
    -          qh_setdel(vertex->neighbors, facet1);
    -          if (!SETfirst_(vertex->neighbors)) {
    -            zinc_(Zdegenvertex);
    -            trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
    -                 vertex->id, facet1->id));
    -            vertex->deleted= True;
    -            qh_setappend(&qh del_vertices, vertex);
    -          }
    -        }
    -        nummerges++;
    -      }else if (size < qh hull_dim) {
    -        bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
    -        trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
    -              facet1->id, size, bestneighbor->id, dist));
    -        qh_mergefacet(facet1, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
    -        nummerges++;
    -        if (qh PRINTstatistics) {
    -          zinc_(Zdegen);
    -          wadd_(Wdegentot, dist);
    -          wmax_(Wdegenmax, dist);
    -        }
    -      } /* else, another merge fixed the degeneracy and redundancy tested */
    -    }
    -  }
    -  return nummerges;
    -} /* merge_degenredundant */
    -
    -/*---------------------------------
    -
    -  qh_merge_nonconvex( facet1, facet2, mergetype )
    -    remove non-convex ridge between facet1 into facet2
    -    mergetype gives why the facet's are non-convex
    -
    -  returns:
    -    merges one of the facets into the best neighbor
    -
    -  design:
    -    if one of the facets is a new facet
    -      prefer merging new facet into old facet
    -    find best neighbors for both facets
    -    merge the nearest facet into its best neighbor
    -    update the statistics
    -*/
    -void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) {
    -  facetT *bestfacet, *bestneighbor, *neighbor;
    -  realT dist, dist2, mindist, mindist2, maxdist, maxdist2;
    -
    -  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    -    qhmem.IStracing= qh IStracing= qh TRACElevel;
    -  trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
    -      zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
    -  /* concave or coplanar */
    -  if (!facet1->newfacet) {
    -    bestfacet= facet2;   /* avoid merging old facet if new is ok */
    -    facet2= facet1;
    -    facet1= bestfacet;
    -  }else
    -    bestfacet= facet1;
    -  bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist);
    -  neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
    -  if (dist < dist2) {
    -    qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
    -  }else if (qh AVOIDold && !facet2->newfacet
    -  && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside)
    -       || dist * 1.5 < dist2)) {
    -    zinc_(Zavoidold);
    -    wadd_(Wavoidoldtot, dist);
    -    wmax_(Wavoidoldmax, dist);
    -    trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g.  Use f%d dist %2.2g instead\n",
    -           facet2->id, dist2, facet1->id, dist2));
    -    qh_mergefacet(bestfacet, bestneighbor, &mindist, &maxdist, !qh_MERGEapex);
    -  }else {
    -    qh_mergefacet(facet2, neighbor, &mindist2, &maxdist2, !qh_MERGEapex);
    -    dist= dist2;
    -  }
    -  if (qh PRINTstatistics) {
    -    if (mergetype == MRGanglecoplanar) {
    -      zinc_(Zacoplanar);
    -      wadd_(Wacoplanartot, dist);
    -      wmax_(Wacoplanarmax, dist);
    -    }else if (mergetype == MRGconcave) {
    -      zinc_(Zconcave);
    -      wadd_(Wconcavetot, dist);
    -      wmax_(Wconcavemax, dist);
    -    }else { /* MRGcoplanar */
    -      zinc_(Zcoplanar);
    -      wadd_(Wcoplanartot, dist);
    -      wmax_(Wcoplanarmax, dist);
    -    }
    -  }
    -} /* merge_nonconvex */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle( samecycle, newfacet )
    -    merge a cycle of facets starting at samecycle into a newfacet
    -    newfacet is a horizon facet with ->normal
    -    samecycle facets are simplicial from an apex
    -
    -  returns:
    -    initializes vertex neighbors on first merge
    -    samecycle deleted (placed on qh.visible_list)
    -    newfacet at end of qh.facet_list
    -    deleted vertices on qh.del_vertices
    -
    -  see:
    -    qh_mergefacet()
    -    called by qh_mergecycle_all() for multiple, same cycle facets
    -
    -  design:
    -    make vertex neighbors if necessary
    -    make ridges for newfacet
    -    merge neighbor sets of samecycle into newfacet
    -    merge ridges of samecycle into newfacet
    -    merge vertex neighbors of samecycle into newfacet
    -    make apex of samecycle the apex of newfacet
    -    if newfacet wasn't a new facet
    -      add its vertices to qh.newvertex_list
    -    delete samecycle facets a make newfacet a newfacet
    -*/
    -void qh_mergecycle(facetT *samecycle, facetT *newfacet) {
    -  int traceonce= False, tracerestore= 0;
    -  vertexT *apex;
    -#ifndef qh_NOtrace
    -  facetT *same;
    -#endif
    -
    -  if (newfacet->tricoplanar) {
    -    if (!qh TRInormals) {
    -      qh_fprintf(qh ferr, 6224, "Qhull internal error (qh_mergecycle): does not work for tricoplanar facets.  Use option 'Q11'\n");
    -      qh_errexit(qh_ERRqhull, newfacet, NULL);
    -    }
    -    newfacet->tricoplanar= False;
    -    newfacet->keepcentrum= False;
    -  }
    -  if (!qh VERTEXneighbors)
    -    qh_vertexneighbors();
    -  zzinc_(Ztotmerge);
    -  if (qh REPORTfreq2 && qh POSTmerging) {
    -    if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
    -      qh_tracemerging();
    -  }
    -#ifndef qh_NOtrace
    -  if (qh TRACEmerge == zzval_(Ztotmerge))
    -    qhmem.IStracing= qh IStracing= qh TRACElevel;
    -  trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
    -        zzval_(Ztotmerge), samecycle->id, newfacet->id));
    -  if (newfacet == qh tracefacet) {
    -    tracerestore= qh IStracing;
    -    qh IStracing= 4;
    -    qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
    -               zzval_(Ztotmerge), samecycle->id, newfacet->id,  qh furthest_id);
    -    traceonce= True;
    -  }
    -  if (qh IStracing >=4) {
    -    qh_fprintf(qh ferr, 8069, "  same cycle:");
    -    FORALLsame_cycle_(samecycle)
    -      qh_fprintf(qh ferr, 8070, " f%d", same->id);
    -    qh_fprintf(qh ferr, 8071, "\n");
    -  }
    -  if (qh IStracing >=4)
    -    qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
    -#endif /* !qh_NOtrace */
    -  apex= SETfirstt_(samecycle->vertices, vertexT);
    -  qh_makeridges(newfacet);
    -  qh_mergecycle_neighbors(samecycle, newfacet);
    -  qh_mergecycle_ridges(samecycle, newfacet);
    -  qh_mergecycle_vneighbors(samecycle, newfacet);
    -  if (SETfirstt_(newfacet->vertices, vertexT) != apex)
    -    qh_setaddnth(&newfacet->vertices, 0, apex);  /* apex has last id */
    -  if (!newfacet->newfacet)
    -    qh_newvertices(newfacet->vertices);
    -  qh_mergecycle_facets(samecycle, newfacet);
    -  qh_tracemerge(samecycle, newfacet);
    -  /* check for degen_redundant_neighbors after qh_forcedmerges() */
    -  if (traceonce) {
    -    qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n");
    -    qh IStracing= tracerestore;
    -  }
    -} /* mergecycle */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle_all( facetlist, wasmerge )
    -    merge all samecycles of coplanar facets into horizon
    -    don't merge facets with ->mergeridge (these already have ->normal)
    -    all facets are simplicial from apex
    -    all facet->cycledone == False
    -
    -  returns:
    -    all newfacets merged into coplanar horizon facets
    -    deleted vertices on  qh.del_vertices
    -    sets wasmerge if any merge
    -
    -  see:
    -    calls qh_mergecycle for multiple, same cycle facets
    -
    -  design:
    -    for each facet on facetlist
    -      skip facets with duplicate ridges and normals
    -      check that facet is in a samecycle (->mergehorizon)
    -      if facet only member of samecycle
    -        sets vertex->delridge for all vertices except apex
    -        merge facet into horizon
    -      else
    -        mark all facets in samecycle
    -        remove facets with duplicate ridges from samecycle
    -        merge samecycle into horizon (deletes facets from facetlist)
    -*/
    -void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) {
    -  facetT *facet, *same, *prev, *horizon;
    -  facetT *samecycle= NULL, *nextfacet, *nextsame;
    -  vertexT *apex, *vertex, **vertexp;
    -  int cycles=0, total=0, facets, nummerge;
    -
    -  trace2((qh ferr, 2031, "qh_mergecycle_all: begin\n"));
    -  for (facet= facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
    -    if (facet->normal)
    -      continue;
    -    if (!facet->mergehorizon) {
    -      qh_fprintf(qh ferr, 6225, "Qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -    horizon= SETfirstt_(facet->neighbors, facetT);
    -    if (facet->f.samecycle == facet) {
    -      zinc_(Zonehorizon);
    -      /* merge distance done in qh_findhorizon */
    -      apex= SETfirstt_(facet->vertices, vertexT);
    -      FOREACHvertex_(facet->vertices) {
    -        if (vertex != apex)
    -          vertex->delridge= True;
    -      }
    -      horizon->f.newcycle= NULL;
    -      qh_mergefacet(facet, horizon, NULL, NULL, qh_MERGEapex);
    -    }else {
    -      samecycle= facet;
    -      facets= 0;
    -      prev= facet;
    -      for (same= facet->f.samecycle; same;  /* FORALLsame_cycle_(facet) */
    -           same= (same == facet ? NULL :nextsame)) { /* ends at facet */
    -        nextsame= same->f.samecycle;
    -        if (same->cycledone || same->visible)
    -          qh_infiniteloop(same);
    -        same->cycledone= True;
    -        if (same->normal) {
    -          prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
    -          same->f.samecycle= NULL;
    -        }else {
    -          prev= same;
    -          facets++;
    -        }
    -      }
    -      while (nextfacet && nextfacet->cycledone)  /* will delete samecycle */
    -        nextfacet= nextfacet->next;
    -      horizon->f.newcycle= NULL;
    -      qh_mergecycle(samecycle, horizon);
    -      nummerge= horizon->nummerge + facets;
    -      if (nummerge > qh_MAXnummerge)
    -        horizon->nummerge= qh_MAXnummerge;
    -      else
    -        horizon->nummerge= (short unsigned int)nummerge;
    -      zzinc_(Zcyclehorizon);
    -      total += facets;
    -      zzadd_(Zcyclefacettot, facets);
    -      zmax_(Zcyclefacetmax, facets);
    -    }
    -    cycles++;
    -  }
    -  if (cycles)
    -    *wasmerge= True;
    -  trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons\n", cycles));
    -} /* mergecycle_all */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle_facets( samecycle, newfacet )
    -    finish merge of samecycle into newfacet
    -
    -  returns:
    -    samecycle prepended to visible_list for later deletion and partitioning
    -      each facet->f.replace == newfacet
    -
    -    newfacet moved to end of qh.facet_list
    -      makes newfacet a newfacet (get's facet1->id if it was old)
    -      sets newfacet->newmerge
    -      clears newfacet->center (unless merging into a large facet)
    -      clears newfacet->tested and ridge->tested for facet1
    -
    -    adds neighboring facets to facet_mergeset if redundant or degenerate
    -
    -  design:
    -    make newfacet a new facet and set its flags
    -    move samecycle facets to qh.visible_list for later deletion
    -    unless newfacet is large
    -      remove its centrum
    -*/
    -void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) {
    -  facetT *same, *next;
    -
    -  trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
    -  qh_removefacet(newfacet);  /* append as a newfacet to end of qh facet_list */
    -  qh_appendfacet(newfacet);
    -  newfacet->newfacet= True;
    -  newfacet->simplicial= False;
    -  newfacet->newmerge= True;
    -
    -  for (same= samecycle->f.samecycle; same; same= (same == samecycle ?  NULL : next)) {
    -    next= same->f.samecycle;  /* reused by willdelete */
    -    qh_willdelete(same, newfacet);
    -  }
    -  if (newfacet->center
    -      && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
    -    qh_memfree(newfacet->center, qh normal_size);
    -    newfacet->center= NULL;
    -  }
    -  trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
    -             samecycle->id, newfacet->id));
    -} /* mergecycle_facets */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle_neighbors( samecycle, newfacet )
    -    add neighbors for samecycle facets to newfacet
    -
    -  returns:
    -    newfacet with updated neighbors and vice-versa
    -    newfacet has ridges
    -    all neighbors of newfacet marked with qh.visit_id
    -    samecycle facets marked with qh.visit_id-1
    -    ridges updated for simplicial neighbors of samecycle with a ridge
    -
    -  notes:
    -    assumes newfacet not in samecycle
    -    usually, samecycle facets are new, simplicial facets without internal ridges
    -      not so if horizon facet is coplanar to two different samecycles
    -
    -  see:
    -    qh_mergeneighbors()
    -
    -  design:
    -    check samecycle
    -    delete neighbors from newfacet that are also in samecycle
    -    for each neighbor of a facet in samecycle
    -      if neighbor is simplicial
    -        if first visit
    -          move the neighbor relation to newfacet
    -          update facet links for its ridges
    -        else
    -          make ridges for neighbor
    -          remove samecycle reference
    -      else
    -        update neighbor sets
    -*/
    -void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) {
    -  facetT *same, *neighbor, **neighborp;
    -  int delneighbors= 0, newneighbors= 0;
    -  unsigned int samevisitid;
    -  ridgeT *ridge, **ridgep;
    -
    -  samevisitid= ++qh visit_id;
    -  FORALLsame_cycle_(samecycle) {
    -    if (same->visitid == samevisitid || same->visible)
    -      qh_infiniteloop(samecycle);
    -    same->visitid= samevisitid;
    -  }
    -  newfacet->visitid= ++qh visit_id;
    -  trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
    -  FOREACHneighbor_(newfacet) {
    -    if (neighbor->visitid == samevisitid) {
    -      SETref_(neighbor)= NULL;  /* samecycle neighbors deleted */
    -      delneighbors++;
    -    }else
    -      neighbor->visitid= qh visit_id;
    -  }
    -  qh_setcompact(newfacet->neighbors);
    -
    -  trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
    -  FORALLsame_cycle_(samecycle) {
    -    FOREACHneighbor_(same) {
    -      if (neighbor->visitid == samevisitid)
    -        continue;
    -      if (neighbor->simplicial) {
    -        if (neighbor->visitid != qh visit_id) {
    -          qh_setappend(&newfacet->neighbors, neighbor);
    -          qh_setreplace(neighbor->neighbors, same, newfacet);
    -          newneighbors++;
    -          neighbor->visitid= qh visit_id;
    -          FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
    -            if (ridge->top == same) {
    -              ridge->top= newfacet;
    -              break;
    -            }else if (ridge->bottom == same) {
    -              ridge->bottom= newfacet;
    -              break;
    -            }
    -          }
    -        }else {
    -          qh_makeridges(neighbor);
    -          qh_setdel(neighbor->neighbors, same);
    -          /* same can't be horizon facet for neighbor */
    -        }
    -      }else { /* non-simplicial neighbor */
    -        qh_setdel(neighbor->neighbors, same);
    -        if (neighbor->visitid != qh visit_id) {
    -          qh_setappend(&neighbor->neighbors, newfacet);
    -          qh_setappend(&newfacet->neighbors, neighbor);
    -          neighbor->visitid= qh visit_id;
    -          newneighbors++;
    -        }
    -      }
    -    }
    -  }
    -  trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
    -             delneighbors, newneighbors));
    -} /* mergecycle_neighbors */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle_ridges( samecycle, newfacet )
    -    add ridges/neighbors for facets in samecycle to newfacet
    -    all new/old neighbors of newfacet marked with qh.visit_id
    -    facets in samecycle marked with qh.visit_id-1
    -    newfacet marked with qh.visit_id
    -
    -  returns:
    -    newfacet has merged ridges
    -
    -  notes:
    -    ridge already updated for simplicial neighbors of samecycle with a ridge
    -
    -  see:
    -    qh_mergeridges()
    -    qh_makeridges()
    -
    -  design:
    -    remove ridges between newfacet and samecycle
    -    for each facet in samecycle
    -      for each ridge in facet
    -        update facet pointers in ridge
    -        skip ridges processed in qh_mergecycle_neighors
    -        free ridges between newfacet and samecycle
    -        free ridges between facets of samecycle (on 2nd visit)
    -        append remaining ridges to newfacet
    -      if simpilicial facet
    -        for each neighbor of facet
    -          if simplicial facet
    -          and not samecycle facet or newfacet
    -            make ridge between neighbor and newfacet
    -*/
    -void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) {
    -  facetT *same, *neighbor= NULL;
    -  int numold=0, numnew=0;
    -  int neighbor_i, neighbor_n;
    -  unsigned int samevisitid;
    -  ridgeT *ridge, **ridgep;
    -  boolT toporient;
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
    -  samevisitid= qh visit_id -1;
    -  FOREACHridge_(newfacet->ridges) {
    -    neighbor= otherfacet_(ridge, newfacet);
    -    if (neighbor->visitid == samevisitid)
    -      SETref_(ridge)= NULL; /* ridge free'd below */
    -  }
    -  qh_setcompact(newfacet->ridges);
    -
    -  trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
    -  FORALLsame_cycle_(samecycle) {
    -    FOREACHridge_(same->ridges) {
    -      if (ridge->top == same) {
    -        ridge->top= newfacet;
    -        neighbor= ridge->bottom;
    -      }else if (ridge->bottom == same) {
    -        ridge->bottom= newfacet;
    -        neighbor= ridge->top;
    -      }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
    -        qh_setappend(&newfacet->ridges, ridge);
    -        numold++;  /* already set by qh_mergecycle_neighbors */
    -        continue;
    -      }else {
    -        qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
    -        qh_errexit(qh_ERRqhull, NULL, ridge);
    -      }
    -      if (neighbor == newfacet) {
    -        qh_setfree(&(ridge->vertices));
    -        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    -        numold++;
    -      }else if (neighbor->visitid == samevisitid) {
    -        qh_setdel(neighbor->ridges, ridge);
    -        qh_setfree(&(ridge->vertices));
    -        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    -        numold++;
    -      }else {
    -        qh_setappend(&newfacet->ridges, ridge);
    -        numold++;
    -      }
    -    }
    -    if (same->ridges)
    -      qh_settruncate(same->ridges, 0);
    -    if (!same->simplicial)
    -      continue;
    -    FOREACHneighbor_i_(same) {       /* note: !newfact->simplicial */
    -      if (neighbor->visitid != samevisitid && neighbor->simplicial) {
    -        ridge= qh_newridge();
    -        ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim,
    -                                                          neighbor_i, 0);
    -        toporient= same->toporient ^ (neighbor_i & 0x1);
    -        if (toporient) {
    -          ridge->top= newfacet;
    -          ridge->bottom= neighbor;
    -        }else {
    -          ridge->top= neighbor;
    -          ridge->bottom= newfacet;
    -        }
    -        qh_setappend(&(newfacet->ridges), ridge);
    -        qh_setappend(&(neighbor->ridges), ridge);
    -        numnew++;
    -      }
    -    }
    -  }
    -
    -  trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
    -             numold, numnew));
    -} /* mergecycle_ridges */
    -
    -/*---------------------------------
    -
    -  qh_mergecycle_vneighbors( samecycle, newfacet )
    -    create vertex neighbors for newfacet from vertices of facets in samecycle
    -    samecycle marked with visitid == qh.visit_id - 1
    -
    -  returns:
    -    newfacet vertices with updated neighbors
    -    marks newfacet with qh.visit_id-1
    -    deletes vertices that are merged away
    -    sets delridge on all vertices (faster here than in mergecycle_ridges)
    -
    -  see:
    -    qh_mergevertex_neighbors()
    -
    -  design:
    -    for each vertex of samecycle facet
    -      set vertex->delridge
    -      delete samecycle facets from vertex neighbors
    -      append newfacet to vertex neighbors
    -      if vertex only in newfacet
    -        delete it from newfacet
    -        add it to qh.del_vertices for later deletion
    -*/
    -void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) {
    -  facetT *neighbor, **neighborp;
    -  unsigned int mergeid;
    -  vertexT *vertex, **vertexp, *apex;
    -  setT *vertices;
    -
    -  trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
    -  mergeid= qh visit_id - 1;
    -  newfacet->visitid= mergeid;
    -  vertices= qh_basevertices(samecycle); /* temp */
    -  apex= SETfirstt_(samecycle->vertices, vertexT);
    -  qh_setappend(&vertices, apex);
    -  FOREACHvertex_(vertices) {
    -    vertex->delridge= True;
    -    FOREACHneighbor_(vertex) {
    -      if (neighbor->visitid == mergeid)
    -        SETref_(neighbor)= NULL;
    -    }
    -    qh_setcompact(vertex->neighbors);
    -    qh_setappend(&vertex->neighbors, newfacet);
    -    if (!SETsecond_(vertex->neighbors)) {
    -      zinc_(Zcyclevertex);
    -      trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
    -        vertex->id, samecycle->id, newfacet->id));
    -      qh_setdelsorted(newfacet->vertices, vertex);
    -      vertex->deleted= True;
    -      qh_setappend(&qh del_vertices, vertex);
    -    }
    -  }
    -  qh_settempfree(&vertices);
    -  trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
    -             samecycle->id, newfacet->id));
    -} /* mergecycle_vneighbors */
    -
    -/*---------------------------------
    -
    -  qh_mergefacet( facet1, facet2, mindist, maxdist, mergeapex )
    -    merges facet1 into facet2
    -    mergeapex==qh_MERGEapex if merging new facet into coplanar horizon
    -
    -  returns:
    -    qh.max_outside and qh.min_vertex updated
    -    initializes vertex neighbors on first merge
    -
    -  returns:
    -    facet2 contains facet1's vertices, neighbors, and ridges
    -      facet2 moved to end of qh.facet_list
    -      makes facet2 a newfacet
    -      sets facet2->newmerge set
    -      clears facet2->center (unless merging into a large facet)
    -      clears facet2->tested and ridge->tested for facet1
    -
    -    facet1 prepended to visible_list for later deletion and partitioning
    -      facet1->f.replace == facet2
    -
    -    adds neighboring facets to facet_mergeset if redundant or degenerate
    -
    -  notes:
    -    mindist/maxdist may be NULL (only if both NULL)
    -    traces merge if fmax_(maxdist,-mindist) > TRACEdist
    -
    -  see:
    -    qh_mergecycle()
    -
    -  design:
    -    trace merge and check for degenerate simplex
    -    make ridges for both facets
    -    update qh.max_outside, qh.max_vertex, qh.min_vertex
    -    update facet2->maxoutside and keepcentrum
    -    update facet2->nummerge
    -    update tested flags for facet2
    -    if facet1 is simplicial
    -      merge facet1 into facet2
    -    else
    -      merge facet1's neighbors into facet2
    -      merge facet1's ridges into facet2
    -      merge facet1's vertices into facet2
    -      merge facet1's vertex neighbors into facet2
    -      add facet2's vertices to qh.new_vertexlist
    -      unless qh_MERGEapex
    -        test facet2 for degenerate or redundant neighbors
    -      move facet1 to qh.visible_list for later deletion
    -      move facet2 to end of qh.newfacet_list
    -*/
    -void qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex) {
    -  boolT traceonce= False;
    -  vertexT *vertex, **vertexp;
    -  int tracerestore=0, nummerge;
    -
    -  if (facet1->tricoplanar || facet2->tricoplanar) {
    -    if (!qh TRInormals) {
    -      qh_fprintf(qh ferr, 6226, "Qhull internal error (qh_mergefacet): does not work for tricoplanar facets.  Use option 'Q11'\n");
    -      qh_errexit2 (qh_ERRqhull, facet1, facet2);
    -    }
    -    if (facet2->tricoplanar) {
    -      facet2->tricoplanar= False;
    -      facet2->keepcentrum= False;
    -    }
    -  }
    -  zzinc_(Ztotmerge);
    -  if (qh REPORTfreq2 && qh POSTmerging) {
    -    if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
    -      qh_tracemerging();
    -  }
    -#ifndef qh_NOtrace
    -  if (qh build_cnt >= qh RERUN) {
    -    if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) {
    -      tracerestore= 0;
    -      qh IStracing= qh TRACElevel;
    -      traceonce= True;
    -      qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d, last point was p%d\n", zzval_(Ztotmerge),
    -             fmax_(-*mindist, *maxdist), facet1->id, facet2->id, qh furthest_id);
    -    }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) {
    -      tracerestore= qh IStracing;
    -      qh IStracing= 4;
    -      traceonce= True;
    -      qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d involving f%d, furthest is p%d\n",
    -                 zzval_(Ztotmerge), qh tracefacet_id,  qh furthest_id);
    -    }
    -  }
    -  if (qh IStracing >= 2) {
    -    realT mergemin= -2;
    -    realT mergemax= -2;
    -
    -    if (mindist) {
    -      mergemin= *mindist;
    -      mergemax= *maxdist;
    -    }
    -    qh_fprintf(qh ferr, 8077, "qh_mergefacet: #%d merge f%d into f%d, mindist= %2.2g, maxdist= %2.2g\n",
    -    zzval_(Ztotmerge), facet1->id, facet2->id, mergemin, mergemax);
    -  }
    -#endif /* !qh_NOtrace */
    -  if (facet1 == facet2 || facet1->visible || facet2->visible) {
    -    qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet\n",
    -             facet1->id, facet2->id);
    -    qh_errexit2 (qh_ERRqhull, facet1, facet2);
    -  }
    -  if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
    -    qh_fprintf(qh ferr, 6227, "\n\
    -qhull precision error: Only %d facets remain.  Can not merge another\n\
    -pair.  The input is too degenerate or the convexity constraints are\n\
    -too strong.\n", qh hull_dim+1);
    -    if (qh hull_dim >= 5 && !qh MERGEexact)
    -      qh_fprintf(qh ferr, 8079, "Option 'Qx' may avoid this problem.\n");
    -    qh_errexit(qh_ERRprec, NULL, NULL);
    -  }
    -  if (!qh VERTEXneighbors)
    -    qh_vertexneighbors();
    -  qh_makeridges(facet1);
    -  qh_makeridges(facet2);
    -  if (qh IStracing >=4)
    -    qh_errprint("MERGING", facet1, facet2, NULL, NULL);
    -  if (mindist) {
    -    maximize_(qh max_outside, *maxdist);
    -    maximize_(qh max_vertex, *maxdist);
    -#if qh_MAXoutside
    -    maximize_(facet2->maxoutside, *maxdist);
    -#endif
    -    minimize_(qh min_vertex, *mindist);
    -    if (!facet2->keepcentrum
    -    && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) {
    -      facet2->keepcentrum= True;
    -      zinc_(Zwidefacet);
    -    }
    -  }
    -  nummerge= facet1->nummerge + facet2->nummerge + 1;
    -  if (nummerge >= qh_MAXnummerge)
    -    facet2->nummerge= qh_MAXnummerge;
    -  else
    -    facet2->nummerge= (short unsigned int)nummerge;
    -  facet2->newmerge= True;
    -  facet2->dupridge= False;
    -  qh_updatetested  (facet1, facet2);
    -  if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim)
    -    qh_mergesimplex(facet1, facet2, mergeapex);
    -  else {
    -    qh vertex_visit++;
    -    FOREACHvertex_(facet2->vertices)
    -      vertex->visitid= qh vertex_visit;
    -    if (qh hull_dim == 2)
    -      qh_mergefacet2d(facet1, facet2);
    -    else {
    -      qh_mergeneighbors(facet1, facet2);
    -      qh_mergevertices(facet1->vertices, &facet2->vertices);
    -    }
    -    qh_mergeridges(facet1, facet2);
    -    qh_mergevertex_neighbors(facet1, facet2);
    -    if (!facet2->newfacet)
    -      qh_newvertices(facet2->vertices);
    -  }
    -  if (!mergeapex)
    -    qh_degen_redundant_neighbors(facet2, facet1);
    -  if (facet2->coplanar || !facet2->newfacet) {
    -    zinc_(Zmergeintohorizon);
    -  }else if (!facet1->newfacet && facet2->newfacet) {
    -    zinc_(Zmergehorizon);
    -  }else {
    -    zinc_(Zmergenew);
    -  }
    -  qh_willdelete(facet1, facet2);
    -  qh_removefacet(facet2);  /* append as a newfacet to end of qh facet_list */
    -  qh_appendfacet(facet2);
    -  facet2->newfacet= True;
    -  facet2->tested= False;
    -  qh_tracemerge(facet1, facet2);
    -  if (traceonce) {
    -    qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n");
    -    qh IStracing= tracerestore;
    -  }
    -} /* mergefacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_mergefacet2d( facet1, facet2 )
    -    in 2d, merges neighbors and vertices of facet1 into facet2
    -
    -  returns:
    -    build ridges for neighbors if necessary
    -    facet2 looks like a simplicial facet except for centrum, ridges
    -      neighbors are opposite the corresponding vertex
    -      maintains orientation of facet2
    -
    -  notes:
    -    qh_mergefacet() retains non-simplicial structures
    -      they are not needed in 2d, but later routines may use them
    -    preserves qh.vertex_visit for qh_mergevertex_neighbors()
    -
    -  design:
    -    get vertices and neighbors
    -    determine new vertices and neighbors
    -    set new vertices and neighbors and adjust orientation
    -    make ridges for new neighbor if needed
    -*/
    -void qh_mergefacet2d(facetT *facet1, facetT *facet2) {
    -  vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
    -  facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;
    -
    -  vertex1A= SETfirstt_(facet1->vertices, vertexT);
    -  vertex1B= SETsecondt_(facet1->vertices, vertexT);
    -  vertex2A= SETfirstt_(facet2->vertices, vertexT);
    -  vertex2B= SETsecondt_(facet2->vertices, vertexT);
    -  neighbor1A= SETfirstt_(facet1->neighbors, facetT);
    -  neighbor1B= SETsecondt_(facet1->neighbors, facetT);
    -  neighbor2A= SETfirstt_(facet2->neighbors, facetT);
    -  neighbor2B= SETsecondt_(facet2->neighbors, facetT);
    -  if (vertex1A == vertex2A) {
    -    vertexA= vertex1B;
    -    vertexB= vertex2B;
    -    neighborA= neighbor2A;
    -    neighborB= neighbor1A;
    -  }else if (vertex1A == vertex2B) {
    -    vertexA= vertex1B;
    -    vertexB= vertex2A;
    -    neighborA= neighbor2B;
    -    neighborB= neighbor1A;
    -  }else if (vertex1B == vertex2A) {
    -    vertexA= vertex1A;
    -    vertexB= vertex2B;
    -    neighborA= neighbor2A;
    -    neighborB= neighbor1B;
    -  }else { /* 1B == 2B */
    -    vertexA= vertex1A;
    -    vertexB= vertex2A;
    -    neighborA= neighbor2B;
    -    neighborB= neighbor1B;
    -  }
    -  /* vertexB always from facet2, neighborB always from facet1 */
    -  if (vertexA->id > vertexB->id) {
    -    SETfirst_(facet2->vertices)= vertexA;
    -    SETsecond_(facet2->vertices)= vertexB;
    -    if (vertexB == vertex2A)
    -      facet2->toporient= !facet2->toporient;
    -    SETfirst_(facet2->neighbors)= neighborA;
    -    SETsecond_(facet2->neighbors)= neighborB;
    -  }else {
    -    SETfirst_(facet2->vertices)= vertexB;
    -    SETsecond_(facet2->vertices)= vertexA;
    -    if (vertexB == vertex2B)
    -      facet2->toporient= !facet2->toporient;
    -    SETfirst_(facet2->neighbors)= neighborB;
    -    SETsecond_(facet2->neighbors)= neighborA;
    -  }
    -  qh_makeridges(neighborB);
    -  qh_setreplace(neighborB->neighbors, facet1, facet2);
    -  trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
    -       vertexA->id, neighborB->id, facet1->id, facet2->id));
    -} /* mergefacet2d */
    -
    -
    -/*---------------------------------
    -
    -  qh_mergeneighbors( facet1, facet2 )
    -    merges the neighbors of facet1 into facet2
    -
    -  see:
    -    qh_mergecycle_neighbors()
    -
    -  design:
    -    for each neighbor of facet1
    -      if neighbor is also a neighbor of facet2
    -        if neighbor is simpilicial
    -          make ridges for later deletion as a degenerate facet
    -        update its neighbor set
    -      else
    -        move the neighbor relation to facet2
    -    remove the neighbor relation for facet1 and facet2
    -*/
    -void qh_mergeneighbors(facetT *facet1, facetT *facet2) {
    -  facetT *neighbor, **neighborp;
    -
    -  trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
    -          facet1->id, facet2->id));
    -  qh visit_id++;
    -  FOREACHneighbor_(facet2) {
    -    neighbor->visitid= qh visit_id;
    -  }
    -  FOREACHneighbor_(facet1) {
    -    if (neighbor->visitid == qh visit_id) {
    -      if (neighbor->simplicial)    /* is degen, needs ridges */
    -        qh_makeridges(neighbor);
    -      if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
    -        qh_setdel(neighbor->neighbors, facet1);
    -      else {
    -        qh_setdel(neighbor->neighbors, facet2);
    -        qh_setreplace(neighbor->neighbors, facet1, facet2);
    -      }
    -    }else if (neighbor != facet2) {
    -      qh_setappend(&(facet2->neighbors), neighbor);
    -      qh_setreplace(neighbor->neighbors, facet1, facet2);
    -    }
    -  }
    -  qh_setdel(facet1->neighbors, facet2);  /* here for makeridges */
    -  qh_setdel(facet2->neighbors, facet1);
    -} /* mergeneighbors */
    -
    -
    -/*---------------------------------
    -
    -  qh_mergeridges( facet1, facet2 )
    -    merges the ridge set of facet1 into facet2
    -
    -  returns:
    -    may delete all ridges for a vertex
    -    sets vertex->delridge on deleted ridges
    -
    -  see:
    -    qh_mergecycle_ridges()
    -
    -  design:
    -    delete ridges between facet1 and facet2
    -      mark (delridge) vertices on these ridges for later testing
    -    for each remaining ridge
    -      rename facet1 to facet2
    -*/
    -void qh_mergeridges(facetT *facet1, facetT *facet2) {
    -  ridgeT *ridge, **ridgep;
    -  vertexT *vertex, **vertexp;
    -
    -  trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d and f%d\n",
    -          facet1->id, facet2->id));
    -  FOREACHridge_(facet2->ridges) {
    -    if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
    -      FOREACHvertex_(ridge->vertices)
    -        vertex->delridge= True;
    -      qh_delridge(ridge);  /* expensive in high-d, could rebuild */
    -      ridgep--; /*repeat*/
    -    }
    -  }
    -  FOREACHridge_(facet1->ridges) {
    -    if (ridge->top == facet1)
    -      ridge->top= facet2;
    -    else
    -      ridge->bottom= facet2;
    -    qh_setappend(&(facet2->ridges), ridge);
    -  }
    -} /* mergeridges */
    -
    -
    -/*---------------------------------
    -
    -  qh_mergesimplex( facet1, facet2, mergeapex )
    -    merge simplicial facet1 into facet2
    -    mergeapex==qh_MERGEapex if merging samecycle into horizon facet
    -      vertex id is latest (most recently created)
    -    facet1 may be contained in facet2
    -    ridges exist for both facets
    -
    -  returns:
    -    facet2 with updated vertices, ridges, neighbors
    -    updated neighbors for facet1's vertices
    -    facet1 not deleted
    -    sets vertex->delridge on deleted ridges
    -
    -  notes:
    -    special case code since this is the most common merge
    -    called from qh_mergefacet()
    -
    -  design:
    -    if qh_MERGEapex
    -      add vertices of facet2 to qh.new_vertexlist if necessary
    -      add apex to facet2
    -    else
    -      for each ridge between facet1 and facet2
    -        set vertex->delridge
    -      determine the apex for facet1 (i.e., vertex to be merged)
    -      unless apex already in facet2
    -        insert apex into vertices for facet2
    -      add vertices of facet2 to qh.new_vertexlist if necessary
    -      add apex to qh.new_vertexlist if necessary
    -      for each vertex of facet1
    -        if apex
    -          rename facet1 to facet2 in its vertex neighbors
    -        else
    -          delete facet1 from vertex neighors
    -          if only in facet2
    -            add vertex to qh.del_vertices for later deletion
    -      for each ridge of facet1
    -        delete ridges between facet1 and facet2
    -        append other ridges to facet2 after renaming facet to facet2
    -*/
    -void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) {
    -  vertexT *vertex, **vertexp, *apex;
    -  ridgeT *ridge, **ridgep;
    -  boolT issubset= False;
    -  int vertex_i= -1, vertex_n;
    -  facetT *neighbor, **neighborp, *otherfacet;
    -
    -  if (mergeapex) {
    -    if (!facet2->newfacet)
    -      qh_newvertices(facet2->vertices);  /* apex is new */
    -    apex= SETfirstt_(facet1->vertices, vertexT);
    -    if (SETfirstt_(facet2->vertices, vertexT) != apex)
    -      qh_setaddnth(&facet2->vertices, 0, apex);  /* apex has last id */
    -    else
    -      issubset= True;
    -  }else {
    -    zinc_(Zmergesimplex);
    -    FOREACHvertex_(facet1->vertices)
    -      vertex->seen= False;
    -    FOREACHridge_(facet1->ridges) {
    -      if (otherfacet_(ridge, facet1) == facet2) {
    -        FOREACHvertex_(ridge->vertices) {
    -          vertex->seen= True;
    -          vertex->delridge= True;
    -        }
    -        break;
    -      }
    -    }
    -    FOREACHvertex_(facet1->vertices) {
    -      if (!vertex->seen)
    -        break;  /* must occur */
    -    }
    -    apex= vertex;
    -    trace4((qh ferr, 4039, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
    -          apex->id, facet1->id, facet2->id));
    -    FOREACHvertex_i_(facet2->vertices) {
    -      if (vertex->id < apex->id) {
    -        break;
    -      }else if (vertex->id == apex->id) {
    -        issubset= True;
    -        break;
    -      }
    -    }
    -    if (!issubset)
    -      qh_setaddnth(&facet2->vertices, vertex_i, apex);
    -    if (!facet2->newfacet)
    -      qh_newvertices(facet2->vertices);
    -    else if (!apex->newlist) {
    -      qh_removevertex(apex);
    -      qh_appendvertex(apex);
    -    }
    -  }
    -  trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
    -          facet1->id));
    -  FOREACHvertex_(facet1->vertices) {
    -    if (vertex == apex && !issubset)
    -      qh_setreplace(vertex->neighbors, facet1, facet2);
    -    else {
    -      qh_setdel(vertex->neighbors, facet1);
    -      if (!SETsecond_(vertex->neighbors))
    -        qh_mergevertex_del(vertex, facet1, facet2);
    -    }
    -  }
    -  trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
    -          facet1->id, facet2->id));
    -  qh visit_id++;
    -  FOREACHneighbor_(facet2)
    -    neighbor->visitid= qh visit_id;
    -  FOREACHridge_(facet1->ridges) {
    -    otherfacet= otherfacet_(ridge, facet1);
    -    if (otherfacet == facet2) {
    -      qh_setdel(facet2->ridges, ridge);
    -      qh_setfree(&(ridge->vertices));
    -      qh_memfree(ridge, (int)sizeof(ridgeT));
    -      qh_setdel(facet2->neighbors, facet1);
    -    }else {
    -      qh_setappend(&facet2->ridges, ridge);
    -      if (otherfacet->visitid != qh visit_id) {
    -        qh_setappend(&facet2->neighbors, otherfacet);
    -        qh_setreplace(otherfacet->neighbors, facet1, facet2);
    -        otherfacet->visitid= qh visit_id;
    -      }else {
    -        if (otherfacet->simplicial)    /* is degen, needs ridges */
    -          qh_makeridges(otherfacet);
    -        if (SETfirstt_(otherfacet->neighbors, facetT) != facet1)
    -          qh_setdel(otherfacet->neighbors, facet1);
    -        else {   /*keep newfacet->neighbors->horizon*/
    -          qh_setdel(otherfacet->neighbors, facet2);
    -          qh_setreplace(otherfacet->neighbors, facet1, facet2);
    -        }
    -      }
    -      if (ridge->top == facet1) /* wait until after qh_makeridges */
    -        ridge->top= facet2;
    -      else
    -        ridge->bottom= facet2;
    -    }
    -  }
    -  SETfirst_(facet1->ridges)= NULL; /* it will be deleted */
    -  trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d apex v%d into facet f%d\n",
    -          facet1->id, getid_(apex), facet2->id));
    -} /* mergesimplex */
    -
    -/*---------------------------------
    -
    -  qh_mergevertex_del( vertex, facet1, facet2 )
    -    delete a vertex because of merging facet1 into facet2
    -
    -  returns:
    -    deletes vertex from facet2
    -    adds vertex to qh.del_vertices for later deletion
    -*/
    -void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) {
    -
    -  zinc_(Zmergevertex);
    -  trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
    -          vertex->id, facet1->id, facet2->id));
    -  qh_setdelsorted(facet2->vertices, vertex);
    -  vertex->deleted= True;
    -  qh_setappend(&qh del_vertices, vertex);
    -} /* mergevertex_del */
    -
    -/*---------------------------------
    -
    -  qh_mergevertex_neighbors( facet1, facet2 )
    -    merge the vertex neighbors of facet1 to facet2
    -
    -  returns:
    -    if vertex is current qh.vertex_visit
    -      deletes facet1 from vertex->neighbors
    -    else
    -      renames facet1 to facet2 in vertex->neighbors
    -    deletes vertices if only one neighbor
    -
    -  notes:
    -    assumes vertex neighbor sets are good
    -*/
    -void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) {
    -  vertexT *vertex, **vertexp;
    -
    -  trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighbors of f%d and f%d\n",
    -          facet1->id, facet2->id));
    -  if (qh tracevertex) {
    -    qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d and f%d at furthest p%d f0= %p\n",
    -             facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p);
    -    qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
    -  }
    -  FOREACHvertex_(facet1->vertices) {
    -    if (vertex->visitid != qh vertex_visit)
    -      qh_setreplace(vertex->neighbors, facet1, facet2);
    -    else {
    -      qh_setdel(vertex->neighbors, facet1);
    -      if (!SETsecond_(vertex->neighbors))
    -        qh_mergevertex_del(vertex, facet1, facet2);
    -    }
    -  }
    -  if (qh tracevertex)
    -    qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
    -} /* mergevertex_neighbors */
    -
    -
    -/*---------------------------------
    -
    -  qh_mergevertices( vertices1, vertices2 )
    -    merges the vertex set of facet1 into facet2
    -
    -  returns:
    -    replaces vertices2 with merged set
    -    preserves vertex_visit for qh_mergevertex_neighbors
    -    updates qh.newvertex_list
    -
    -  design:
    -    create a merged set of both vertices (in inverse id order)
    -*/
    -void qh_mergevertices(setT *vertices1, setT **vertices2) {
    -  int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
    -  setT *mergedvertices;
    -  vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);
    -
    -  mergedvertices= qh_settemp(newsize);
    -  FOREACHvertex_(vertices1) {
    -    if (!*vertex2 || vertex->id > (*vertex2)->id)
    -      qh_setappend(&mergedvertices, vertex);
    -    else {
    -      while (*vertex2 && (*vertex2)->id > vertex->id)
    -        qh_setappend(&mergedvertices, *vertex2++);
    -      if (!*vertex2 || (*vertex2)->id < vertex->id)
    -        qh_setappend(&mergedvertices, vertex);
    -      else
    -        qh_setappend(&mergedvertices, *vertex2++);
    -    }
    -  }
    -  while (*vertex2)
    -    qh_setappend(&mergedvertices, *vertex2++);
    -  if (newsize < qh_setsize(mergedvertices)) {
    -    qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  qh_setfree(vertices2);
    -  *vertices2= mergedvertices;
    -  qh_settemppop();
    -} /* mergevertices */
    -
    -
    -/*---------------------------------
    -
    -  qh_neighbor_intersections( vertex )
    -    return intersection of all vertices in vertex->neighbors except for vertex
    -
    -  returns:
    -    returns temporary set of vertices
    -    does not include vertex
    -    NULL if a neighbor is simplicial
    -    NULL if empty set
    -
    -  notes:
    -    used for renaming vertices
    -
    -  design:
    -    initialize the intersection set with vertices of the first two neighbors
    -    delete vertex from the intersection
    -    for each remaining neighbor
    -      intersect its vertex set with the intersection set
    -      return NULL if empty
    -    return the intersection set
    -*/
    -setT *qh_neighbor_intersections(vertexT *vertex) {
    -  facetT *neighbor, **neighborp, *neighborA, *neighborB;
    -  setT *intersect;
    -  int neighbor_i, neighbor_n;
    -
    -  FOREACHneighbor_(vertex) {
    -    if (neighbor->simplicial)
    -      return NULL;
    -  }
    -  neighborA= SETfirstt_(vertex->neighbors, facetT);
    -  neighborB= SETsecondt_(vertex->neighbors, facetT);
    -  zinc_(Zintersectnum);
    -  if (!neighborA)
    -    return NULL;
    -  if (!neighborB)
    -    intersect= qh_setcopy(neighborA->vertices, 0);
    -  else
    -    intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices);
    -  qh_settemppush(intersect);
    -  qh_setdelsorted(intersect, vertex);
    -  FOREACHneighbor_i_(vertex) {
    -    if (neighbor_i >= 2) {
    -      zinc_(Zintersectnum);
    -      qh_vertexintersect(&intersect, neighbor->vertices);
    -      if (!SETfirst_(intersect)) {
    -        zinc_(Zintersectfail);
    -        qh_settempfree(&intersect);
    -        return NULL;
    -      }
    -    }
    -  }
    -  trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
    -          qh_setsize(intersect), vertex->id));
    -  return intersect;
    -} /* neighbor_intersections */
    -
    -/*---------------------------------
    -
    -  qh_newvertices( vertices )
    -    add vertices to end of qh.vertex_list (marks as new vertices)
    -
    -  returns:
    -    vertices on qh.newvertex_list
    -    vertex->newlist set
    -*/
    -void qh_newvertices(setT *vertices) {
    -  vertexT *vertex, **vertexp;
    -
    -  FOREACHvertex_(vertices) {
    -    if (!vertex->newlist) {
    -      qh_removevertex(vertex);
    -      qh_appendvertex(vertex);
    -    }
    -  }
    -} /* newvertices */
    -
    -/*---------------------------------
    -
    -  qh_reducevertices()
    -    reduce extra vertices, shared vertices, and redundant vertices
    -    facet->newmerge is set if merged since last call
    -    if !qh.MERGEvertices, only removes extra vertices
    -
    -  returns:
    -    True if also merged degen_redundant facets
    -    vertices are renamed if possible
    -    clears facet->newmerge and vertex->delridge
    -
    -  notes:
    -    ignored if 2-d
    -
    -  design:
    -    merge any degenerate or redundant facets
    -    for each newly merged facet
    -      remove extra vertices
    -    if qh.MERGEvertices
    -      for each newly merged facet
    -        for each vertex
    -          if vertex was on a deleted ridge
    -            rename vertex if it is shared
    -      remove delridge flag from new vertices
    -*/
    -boolT qh_reducevertices(void) {
    -  int numshare=0, numrename= 0;
    -  boolT degenredun= False;
    -  facetT *newfacet;
    -  vertexT *vertex, **vertexp;
    -
    -  if (qh hull_dim == 2)
    -    return False;
    -  if (qh_merge_degenredundant())
    -    degenredun= True;
    - LABELrestart:
    -  FORALLnew_facets {
    -    if (newfacet->newmerge) {
    -      if (!qh MERGEvertices)
    -        newfacet->newmerge= False;
    -      qh_remove_extravertices(newfacet);
    -    }
    -  }
    -  if (!qh MERGEvertices)
    -    return False;
    -  FORALLnew_facets {
    -    if (newfacet->newmerge) {
    -      newfacet->newmerge= False;
    -      FOREACHvertex_(newfacet->vertices) {
    -        if (vertex->delridge) {
    -          if (qh_rename_sharedvertex(vertex, newfacet)) {
    -            numshare++;
    -            vertexp--; /* repeat since deleted vertex */
    -          }
    -        }
    -      }
    -    }
    -  }
    -  FORALLvertex_(qh newvertex_list) {
    -    if (vertex->delridge && !vertex->deleted) {
    -      vertex->delridge= False;
    -      if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) {
    -        numrename++;
    -        if (qh_merge_degenredundant()) {
    -          degenredun= True;
    -          goto LABELrestart;
    -        }
    -      }
    -    }
    -  }
    -  trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
    -          numshare, numrename, degenredun));
    -  return degenredun;
    -} /* reducevertices */
    -
    -/*---------------------------------
    -
    -  qh_redundant_vertex( vertex )
    -    detect and rename a redundant vertex
    -    vertices have full vertex->neighbors
    -
    -  returns:
    -    returns true if find a redundant vertex
    -      deletes vertex(vertex->deleted)
    -
    -  notes:
    -    only needed if vertex->delridge and hull_dim >= 4
    -    may add degenerate facets to qh.facet_mergeset
    -    doesn't change vertex->neighbors or create redundant facets
    -
    -  design:
    -    intersect vertices of all facet neighbors of vertex
    -    determine ridges for these vertices
    -    if find a new vertex for vertex amoung these ridges and vertices
    -      rename vertex to the new vertex
    -*/
    -vertexT *qh_redundant_vertex(vertexT *vertex) {
    -  vertexT *newvertex= NULL;
    -  setT *vertices, *ridges;
    -
    -  trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d can be renamed\n", vertex->id));
    -  if ((vertices= qh_neighbor_intersections(vertex))) {
    -    ridges= qh_vertexridges(vertex);
    -    if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
    -      qh_renamevertex(vertex, newvertex, ridges, NULL, NULL);
    -    qh_settempfree(&ridges);
    -    qh_settempfree(&vertices);
    -  }
    -  return newvertex;
    -} /* redundant_vertex */
    -
    -/*---------------------------------
    -
    -  qh_remove_extravertices( facet )
    -    remove extra vertices from non-simplicial facets
    -
    -  returns:
    -    returns True if it finds them
    -
    -  design:
    -    for each vertex in facet
    -      if vertex not in a ridge (i.e., no longer used)
    -        delete vertex from facet
    -        delete facet from vertice's neighbors
    -        unless vertex in another facet
    -          add vertex to qh.del_vertices for later deletion
    -*/
    -boolT qh_remove_extravertices(facetT *facet) {
    -  ridgeT *ridge, **ridgep;
    -  vertexT *vertex, **vertexp;
    -  boolT foundrem= False;
    -
    -  trace4((qh ferr, 4043, "qh_remove_extravertices: test f%d for extra vertices\n",
    -          facet->id));
    -  FOREACHvertex_(facet->vertices)
    -    vertex->seen= False;
    -  FOREACHridge_(facet->ridges) {
    -    FOREACHvertex_(ridge->vertices)
    -      vertex->seen= True;
    -  }
    -  FOREACHvertex_(facet->vertices) {
    -    if (!vertex->seen) {
    -      foundrem= True;
    -      zinc_(Zremvertex);
    -      qh_setdelsorted(facet->vertices, vertex);
    -      qh_setdel(vertex->neighbors, facet);
    -      if (!qh_setsize(vertex->neighbors)) {
    -        vertex->deleted= True;
    -        qh_setappend(&qh del_vertices, vertex);
    -        zinc_(Zremvertexdel);
    -        trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
    -      }else
    -        trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
    -      vertexp--; /*repeat*/
    -    }
    -  }
    -  return foundrem;
    -} /* remove_extravertices */
    -
    -/*---------------------------------
    -
    -  qh_rename_sharedvertex( vertex, facet )
    -    detect and rename if shared vertex in facet
    -    vertices have full ->neighbors
    -
    -  returns:
    -    newvertex or NULL
    -    the vertex may still exist in other facets (i.e., a neighbor was pinched)
    -    does not change facet->neighbors
    -    updates vertex->neighbors
    -
    -  notes:
    -    a shared vertex for a facet is only in ridges to one neighbor
    -    this may undo a pinched facet
    -
    -    it does not catch pinches involving multiple facets.  These appear
    -      to be difficult to detect, since an exhaustive search is too expensive.
    -
    -  design:
    -    if vertex only has two neighbors
    -      determine the ridges that contain the vertex
    -      determine the vertices shared by both neighbors
    -      if can find a new vertex in this set
    -        rename the vertex to the new vertex
    -*/
    -vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) {
    -  facetT *neighbor, **neighborp, *neighborA= NULL;
    -  setT *vertices, *ridges;
    -  vertexT *newvertex;
    -
    -  if (qh_setsize(vertex->neighbors) == 2) {
    -    neighborA= SETfirstt_(vertex->neighbors, facetT);
    -    if (neighborA == facet)
    -      neighborA= SETsecondt_(vertex->neighbors, facetT);
    -  }else if (qh hull_dim == 3)
    -    return NULL;
    -  else {
    -    qh visit_id++;
    -    FOREACHneighbor_(facet)
    -      neighbor->visitid= qh visit_id;
    -    FOREACHneighbor_(vertex) {
    -      if (neighbor->visitid == qh visit_id) {
    -        if (neighborA)
    -          return NULL;
    -        neighborA= neighbor;
    -      }
    -    }
    -    if (!neighborA) {
    -      qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
    -        vertex->id, facet->id);
    -      qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex);
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -    }
    -  }
    -  /* the vertex is shared by facet and neighborA */
    -  ridges= qh_settemp(qh TEMPsize);
    -  neighborA->visitid= ++qh visit_id;
    -  qh_vertexridges_facet(vertex, facet, &ridges);
    -  trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
    -    qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id));
    -  zinc_(Zintersectnum);
    -  vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices);
    -  qh_setdel(vertices, vertex);
    -  qh_settemppush(vertices);
    -  if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
    -    qh_renamevertex(vertex, newvertex, ridges, facet, neighborA);
    -  qh_settempfree(&vertices);
    -  qh_settempfree(&ridges);
    -  return newvertex;
    -} /* rename_sharedvertex */
    -
    -/*---------------------------------
    -
    -  qh_renameridgevertex( ridge, oldvertex, newvertex )
    -    renames oldvertex as newvertex in ridge
    -
    -  returns:
    -
    -  design:
    -    delete oldvertex from ridge
    -    if newvertex already in ridge
    -      copy ridge->noconvex to another ridge if possible
    -      delete the ridge
    -    else
    -      insert newvertex into the ridge
    -      adjust the ridge's orientation
    -*/
    -void qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
    -  int nth= 0, oldnth;
    -  facetT *temp;
    -  vertexT *vertex, **vertexp;
    -
    -  oldnth= qh_setindex(ridge->vertices, oldvertex);
    -  qh_setdelnthsorted(ridge->vertices, oldnth);
    -  FOREACHvertex_(ridge->vertices) {
    -    if (vertex == newvertex) {
    -      zinc_(Zdelridge);
    -      if (ridge->nonconvex) /* only one ridge has nonconvex set */
    -        qh_copynonconvex(ridge);
    -      qh_delridge(ridge);
    -      trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted.  It contained both v%d and v%d\n",
    -        ridge->id, oldvertex->id, newvertex->id));
    -      return;
    -    }
    -    if (vertex->id < newvertex->id)
    -      break;
    -    nth++;
    -  }
    -  qh_setaddnth(&ridge->vertices, nth, newvertex);
    -  if (abs(oldnth - nth)%2) {
    -    trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
    -            ridge->id));
    -    temp= ridge->top;
    -    ridge->top= ridge->bottom;
    -    ridge->bottom= temp;
    -  }
    -} /* renameridgevertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA )
    -    renames oldvertex as newvertex in ridges
    -    gives oldfacet/neighborA if oldvertex is shared between two facets
    -
    -  returns:
    -    oldvertex may still exist afterwards
    -
    -
    -  notes:
    -    can not change neighbors of newvertex (since it's a subset)
    -
    -  design:
    -    for each ridge in ridges
    -      rename oldvertex to newvertex and delete degenerate ridges
    -    if oldfacet not defined
    -      for each neighbor of oldvertex
    -        delete oldvertex from neighbor's vertices
    -        remove extra vertices from neighbor
    -      add oldvertex to qh.del_vertices
    -    else if oldvertex only between oldfacet and neighborA
    -      delete oldvertex from oldfacet and neighborA
    -      add oldvertex to qh.del_vertices
    -    else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
    -      delete oldvertex from oldfacet
    -      delete oldfacet from oldvertice's neighbors
    -      remove extra vertices (e.g., oldvertex) from neighborA
    -*/
    -void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
    -  facetT *neighbor, **neighborp;
    -  ridgeT *ridge, **ridgep;
    -  boolT istrace= False;
    -
    -  if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id ||
    -        newvertex->id == qh tracevertex_id)
    -    istrace= True;
    -  FOREACHridge_(ridges)
    -    qh_renameridgevertex(ridge, oldvertex, newvertex);
    -  if (!oldfacet) {
    -    zinc_(Zrenameall);
    -    if (istrace)
    -      qh_fprintf(qh ferr, 8082, "qh_renamevertex: renamed v%d to v%d in several facets\n",
    -               oldvertex->id, newvertex->id);
    -    FOREACHneighbor_(oldvertex) {
    -      qh_maydropneighbor(neighbor);
    -      qh_setdelsorted(neighbor->vertices, oldvertex);
    -      if (qh_remove_extravertices(neighbor))
    -        neighborp--; /* neighbor may be deleted */
    -    }
    -    if (!oldvertex->deleted) {
    -      oldvertex->deleted= True;
    -      qh_setappend(&qh del_vertices, oldvertex);
    -    }
    -  }else if (qh_setsize(oldvertex->neighbors) == 2) {
    -    zinc_(Zrenameshare);
    -    if (istrace)
    -      qh_fprintf(qh ferr, 8083, "qh_renamevertex: renamed v%d to v%d in oldfacet f%d\n",
    -               oldvertex->id, newvertex->id, oldfacet->id);
    -    FOREACHneighbor_(oldvertex)
    -      qh_setdelsorted(neighbor->vertices, oldvertex);
    -    oldvertex->deleted= True;
    -    qh_setappend(&qh del_vertices, oldvertex);
    -  }else {
    -    zinc_(Zrenamepinch);
    -    if (istrace || qh IStracing)
    -      qh_fprintf(qh ferr, 8084, "qh_renamevertex: renamed pinched v%d to v%d between f%d and f%d\n",
    -               oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
    -    qh_setdelsorted(oldfacet->vertices, oldvertex);
    -    qh_setdel(oldvertex->neighbors, oldfacet);
    -    qh_remove_extravertices(neighborA);
    -  }
    -} /* renamevertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_test_appendmerge( facet, neighbor )
    -    tests facet/neighbor for convexity
    -    appends to mergeset if non-convex
    -    if pre-merging,
    -      nop if qh.SKIPconvex, or qh.MERGEexact and coplanar
    -
    -  returns:
    -    true if appends facet/neighbor to mergeset
    -    sets facet->center as needed
    -    does not change facet->seen
    -
    -  design:
    -    if qh.cos_max is defined
    -      if the angle between facet normals is too shallow
    -        append an angle-coplanar merge to qh.mergeset
    -        return True
    -    make facet's centrum if needed
    -    if facet's centrum is above the neighbor
    -      set isconcave
    -    else
    -      if facet's centrum is not below the neighbor
    -        set iscoplanar
    -      make neighbor's centrum if needed
    -      if neighbor's centrum is above the facet
    -        set isconcave
    -      else if neighbor's centrum is not below the facet
    -        set iscoplanar
    -   if isconcave or iscoplanar
    -     get angle if needed
    -     append concave or coplanar merge to qh.mergeset
    -*/
    -boolT qh_test_appendmerge(facetT *facet, facetT *neighbor) {
    -  realT dist, dist2= -REALmax, angle= -REALmax;
    -  boolT isconcave= False, iscoplanar= False, okangle= False;
    -
    -  if (qh SKIPconvex && !qh POSTmerging)
    -    return False;
    -  if ((!qh MERGEexact || qh POSTmerging) && qh cos_max < REALmax/2) {
    -    angle= qh_getangle(facet->normal, neighbor->normal);
    -    zinc_(Zangletests);
    -    if (angle > qh cos_max) {
    -      zinc_(Zcoplanarangle);
    -      qh_appendmergeset(facet, neighbor, MRGanglecoplanar, &angle);
    -      trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
    -         angle, facet->id, neighbor->id));
    -      return True;
    -    }else
    -      okangle= True;
    -  }
    -  if (!facet->center)
    -    facet->center= qh_getcentrum(facet);
    -  zzinc_(Zcentrumtests);
    -  qh_distplane(facet->center, neighbor, &dist);
    -  if (dist > qh centrum_radius)
    -    isconcave= True;
    -  else {
    -    if (dist > -qh centrum_radius)
    -      iscoplanar= True;
    -    if (!neighbor->center)
    -      neighbor->center= qh_getcentrum(neighbor);
    -    zzinc_(Zcentrumtests);
    -    qh_distplane(neighbor->center, facet, &dist2);
    -    if (dist2 > qh centrum_radius)
    -      isconcave= True;
    -    else if (!iscoplanar && dist2 > -qh centrum_radius)
    -      iscoplanar= True;
    -  }
    -  if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging)))
    -    return False;
    -  if (!okangle && qh ANGLEmerge) {
    -    angle= qh_getangle(facet->normal, neighbor->normal);
    -    zinc_(Zangletests);
    -  }
    -  if (isconcave) {
    -    zinc_(Zconcaveridge);
    -    if (qh ANGLEmerge)
    -      angle += qh_ANGLEconcave + 0.5;
    -    qh_appendmergeset(facet, neighbor, MRGconcave, &angle);
    -    trace0((qh ferr, 18, "qh_test_appendmerge: concave f%d to f%d dist %4.4g and reverse dist %4.4g angle %4.4g during p%d\n",
    -           facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
    -  }else /* iscoplanar */ {
    -    zinc_(Zcoplanarcentrum);
    -    qh_appendmergeset(facet, neighbor, MRGcoplanar, &angle);
    -    trace2((qh ferr, 2040, "qh_test_appendmerge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
    -              facet->id, neighbor->id, dist, dist2, angle));
    -  }
    -  return True;
    -} /* test_appendmerge */
    -
    -/*---------------------------------
    -
    -  qh_test_vneighbors()
    -    test vertex neighbors for convexity
    -    tests all facets on qh.newfacet_list
    -
    -  returns:
    -    true if non-convex vneighbors appended to qh.facet_mergeset
    -    initializes vertex neighbors if needed
    -
    -  notes:
    -    assumes all facet neighbors have been tested
    -    this can be expensive
    -    this does not guarantee that a centrum is below all facets
    -      but it is unlikely
    -    uses qh.visit_id
    -
    -  design:
    -    build vertex neighbors if necessary
    -    for all new facets
    -      for all vertices
    -        for each unvisited facet neighbor of the vertex
    -          test new facet and neighbor for convexity
    -*/
    -boolT qh_test_vneighbors(void /* qh newfacet_list */) {
    -  facetT *newfacet, *neighbor, **neighborp;
    -  vertexT *vertex, **vertexp;
    -  int nummerges= 0;
    -
    -  trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
    -  if (!qh VERTEXneighbors)
    -    qh_vertexneighbors();
    -  FORALLnew_facets
    -    newfacet->seen= False;
    -  FORALLnew_facets {
    -    newfacet->seen= True;
    -    newfacet->visitid= qh visit_id++;
    -    FOREACHneighbor_(newfacet)
    -      newfacet->visitid= qh visit_id;
    -    FOREACHvertex_(newfacet->vertices) {
    -      FOREACHneighbor_(vertex) {
    -        if (neighbor->seen || neighbor->visitid == qh visit_id)
    -          continue;
    -        if (qh_test_appendmerge(newfacet, neighbor))
    -          nummerges++;
    -      }
    -    }
    -  }
    -  zadd_(Ztestvneighbor, nummerges);
    -  trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
    -           nummerges));
    -  return (nummerges > 0);
    -} /* test_vneighbors */
    -
    -/*---------------------------------
    -
    -  qh_tracemerge( facet1, facet2 )
    -    print trace message after merge
    -*/
    -void qh_tracemerge(facetT *facet1, facetT *facet2) {
    -  boolT waserror= False;
    -
    -#ifndef qh_NOtrace
    -  if (qh IStracing >= 4)
    -    qh_errprint("MERGED", facet2, NULL, NULL, NULL);
    -  if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newlist)) {
    -    qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d and f%d, furthest p%d\n", facet1->id, facet2->id, qh furthest_id);
    -    if (facet2 != qh tracefacet)
    -      qh_errprint("TRACE", qh tracefacet,
    -        (qh tracevertex && qh tracevertex->neighbors) ?
    -           SETfirstt_(qh tracevertex->neighbors, facetT) : NULL,
    -        NULL, qh tracevertex);
    -  }
    -  if (qh tracevertex) {
    -    if (qh tracevertex->deleted)
    -      qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
    -            qh furthest_id);
    -    else
    -      qh_checkvertex(qh tracevertex);
    -  }
    -  if (qh tracefacet) {
    -    qh_checkfacet(qh tracefacet, True, &waserror);
    -    if (waserror)
    -      qh_errexit(qh_ERRqhull, qh tracefacet, NULL);
    -  }
    -#endif /* !qh_NOtrace */
    -  if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
    -    qh_checkfacet(facet2, True, &waserror);
    -    if (waserror)
    -      qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -} /* tracemerge */
    -
    -/*---------------------------------
    -
    -  qh_tracemerging()
    -    print trace message during POSTmerging
    -
    -  returns:
    -    updates qh.mergereport
    -
    -  notes:
    -    called from qh_mergecycle() and qh_mergefacet()
    -
    -  see:
    -    qh_buildtracing()
    -*/
    -void qh_tracemerging(void) {
    -  realT cpu;
    -  int total;
    -  time_t timedata;
    -  struct tm *tp;
    -
    -  qh mergereport= zzval_(Ztotmerge);
    -  time(&timedata);
    -  tp= localtime(&timedata);
    -  cpu= qh_CPUclock;
    -  cpu /= qh_SECticks;
    -  total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
    -  qh_fprintf(qh ferr, 8087, "\n\
    -At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets.  The hull\n\
    -  contains %d facets and %d vertices.\n",
    -      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu,
    -      total, qh num_facets - qh num_visible,
    -      qh num_vertices-qh_setsize(qh del_vertices));
    -} /* tracemerging */
    -
    -/*---------------------------------
    -
    -  qh_updatetested( facet1, facet2 )
    -    clear facet2->tested and facet1->ridge->tested for merge
    -
    -  returns:
    -    deletes facet2->center unless it's already large
    -      if so, clears facet2->ridge->tested
    -
    -  design:
    -    clear facet2->tested
    -    clear ridge->tested for facet1's ridges
    -    if facet2 has a centrum
    -      if facet2 is large
    -        set facet2->keepcentrum
    -      else if facet2 has 3 vertices due to many merges, or not large and post merging
    -        clear facet2->keepcentrum
    -      unless facet2->keepcentrum
    -        clear facet2->center to recompute centrum later
    -        clear ridge->tested for facet2's ridges
    -*/
    -void qh_updatetested(facetT *facet1, facetT *facet2) {
    -  ridgeT *ridge, **ridgep;
    -  int size;
    -
    -  facet2->tested= False;
    -  FOREACHridge_(facet1->ridges)
    -    ridge->tested= False;
    -  if (!facet2->center)
    -    return;
    -  size= qh_setsize(facet2->vertices);
    -  if (!facet2->keepcentrum) {
    -    if (size > qh hull_dim + qh_MAXnewcentrum) {
    -      facet2->keepcentrum= True;
    -      zinc_(Zwidevertices);
    -    }
    -  }else if (size <= qh hull_dim + qh_MAXnewcentrum) {
    -    /* center and keepcentrum was set */
    -    if (size == qh hull_dim || qh POSTmerging)
    -      facet2->keepcentrum= False; /* if many merges need to recompute centrum */
    -  }
    -  if (!facet2->keepcentrum) {
    -    qh_memfree(facet2->center, qh normal_size);
    -    facet2->center= NULL;
    -    FOREACHridge_(facet2->ridges)
    -      ridge->tested= False;
    -  }
    -} /* updatetested */
    -
    -/*---------------------------------
    -
    -  qh_vertexridges( vertex )
    -    return temporary set of ridges adjacent to a vertex
    -    vertex->neighbors defined
    -
    -  ntoes:
    -    uses qh.visit_id
    -    does not include implicit ridges for simplicial facets
    -
    -  design:
    -    for each neighbor of vertex
    -      add ridges that include the vertex to ridges
    -*/
    -setT *qh_vertexridges(vertexT *vertex) {
    -  facetT *neighbor, **neighborp;
    -  setT *ridges= qh_settemp(qh TEMPsize);
    -  int size;
    -
    -  qh visit_id++;
    -  FOREACHneighbor_(vertex)
    -    neighbor->visitid= qh visit_id;
    -  FOREACHneighbor_(vertex) {
    -    if (*neighborp)   /* no new ridges in last neighbor */
    -      qh_vertexridges_facet(vertex, neighbor, &ridges);
    -  }
    -  if (qh PRINTstatistics || qh IStracing) {
    -    size= qh_setsize(ridges);
    -    zinc_(Zvertexridge);
    -    zadd_(Zvertexridgetot, size);
    -    zmax_(Zvertexridgemax, size);
    -    trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
    -             size, vertex->id));
    -  }
    -  return ridges;
    -} /* vertexridges */
    -
    -/*---------------------------------
    -
    -  qh_vertexridges_facet( vertex, facet, ridges )
    -    add adjacent ridges for vertex in facet
    -    neighbor->visitid==qh.visit_id if it hasn't been visited
    -
    -  returns:
    -    ridges updated
    -    sets facet->visitid to qh.visit_id-1
    -
    -  design:
    -    for each ridge of facet
    -      if ridge of visited neighbor (i.e., unprocessed)
    -        if vertex in ridge
    -          append ridge to vertex
    -    mark facet processed
    -*/
    -void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) {
    -  ridgeT *ridge, **ridgep;
    -  facetT *neighbor;
    -
    -  FOREACHridge_(facet->ridges) {
    -    neighbor= otherfacet_(ridge, facet);
    -    if (neighbor->visitid == qh visit_id
    -    && qh_setin(ridge->vertices, vertex))
    -      qh_setappend(ridges, ridge);
    -  }
    -  facet->visitid= qh visit_id-1;
    -} /* vertexridges_facet */
    -
    -/*---------------------------------
    -
    -  qh_willdelete( facet, replace )
    -    moves facet to visible list
    -    sets facet->f.replace to replace (may be NULL)
    -
    -  returns:
    -    bumps qh.num_visible
    -*/
    -void qh_willdelete(facetT *facet, facetT *replace) {
    -
    -  qh_removefacet(facet);
    -  qh_prependfacet(facet, &qh visible_list);
    -  qh num_visible++;
    -  facet->visible= True;
    -  facet->f.replace= replace;
    -} /* willdelete */
    -
    -#else /* qh_NOmerge */
    -void qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle) {
    -}
    -void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
    -                      boolT vneighbors) {
    -}
    -boolT qh_checkzero(boolT testall) {
    -   }
    -#endif /* qh_NOmerge */
    diff --git a/extern/qhull/merge.h b/extern/qhull/merge.h
    deleted file mode 100644
    index da0fb53e7815..000000000000
    --- a/extern/qhull/merge.h
    +++ /dev/null
    @@ -1,178 +0,0 @@
    -/*
      ---------------------------------
    -
    -   merge.h
    -   header file for merge.c
    -
    -   see qh-merge.htm and merge.c
    -
    -   Copyright (c) 1993-2012 C.B. Barber.
    -   $Id: //main/2011/qhull/src/libqhull/merge.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFmerge
    -#define qhDEFmerge 1
    -
    -#include "libqhull.h"
    -
    -
    -/*============ -constants- ==============*/
    -
    -/*----------------------------------
    -
    -  qh_ANGLEredundant
    -    indicates redundant merge in mergeT->angle
    -*/
    -#define qh_ANGLEredundant 6.0
    -
    -/*----------------------------------
    -
    -  qh_ANGLEdegen
    -    indicates degenerate facet in mergeT->angle
    -*/
    -#define qh_ANGLEdegen     5.0
    -
    -/*----------------------------------
    -
    -  qh_ANGLEconcave
    -    offset to indicate concave facets in mergeT->angle
    -
    -  notes:
    -    concave facets are assigned the range of [2,4] in mergeT->angle
    -    roundoff error may make the angle less than 2
    -*/
    -#define qh_ANGLEconcave  1.5
    -
    -/*----------------------------------
    -
    -  MRG... (mergeType)
    -    indicates the type of a merge (mergeT->type)
    -*/
    -typedef enum {  /* in sort order for facet_mergeset */
    -  MRGnone= 0,
    -  MRGcoplanar,          /* centrum coplanar */
    -  MRGanglecoplanar,     /* angle coplanar */
    -                        /* could detect half concave ridges */
    -  MRGconcave,           /* concave ridge */
    -  MRGflip,              /* flipped facet. facet1 == facet2 */
    -  MRGridge,             /* duplicate ridge (qh_MERGEridge) */
    -                        /* degen and redundant go onto degen_mergeset */
    -  MRGdegen,             /* degenerate facet (!enough neighbors) facet1 == facet2 */
    -  MRGredundant,         /* redundant facet (vertex subset) */
    -                        /* merge_degenredundant assumes degen < redundant */
    -  MRGmirror,            /* mirror facet from qh_triangulate */
    -  ENDmrg
    -} mergeType;
    -
    -/*----------------------------------
    -
    -  qh_MERGEapex
    -    flag for qh_mergefacet() to indicate an apex merge
    -*/
    -#define qh_MERGEapex     True
    -
    -/*============ -structures- ====================*/
    -
    -/*----------------------------------
    -
    -  mergeT
    -    structure used to merge facets
    -*/
    -
    -typedef struct mergeT mergeT;
    -struct mergeT {         /* initialize in qh_appendmergeset */
    -  realT   angle;        /* angle between normals of facet1 and facet2 */
    -  facetT *facet1;       /* will merge facet1 into facet2 */
    -  facetT *facet2;
    -  mergeType type;
    -};
    -
    -
    -/*=========== -macros- =========================*/
    -
    -/*----------------------------------
    -
    -  FOREACHmerge_( merges ) {...}
    -    assign 'merge' to each merge in merges
    -
    -  notes:
    -    uses 'mergeT *merge, **mergep;'
    -    if qh_mergefacet(),
    -      restart since qh.facet_mergeset may change
    -    see FOREACHsetelement_
    -*/
    -#define FOREACHmerge_( merges ) FOREACHsetelement_(mergeT, merges, merge)
    -
    -/*============ prototypes in alphabetical order after pre/postmerge =======*/
    -
    -void    qh_premerge(vertexT *apex, realT maxcentrum, realT maxangle);
    -void    qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
    -             boolT vneighbors);
    -void    qh_all_merges(boolT othermerge, boolT vneighbors);
    -void    qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, realT *angle);
    -setT   *qh_basevertices( facetT *samecycle);
    -void    qh_checkconnect(void /* qh new_facets */);
    -boolT   qh_checkzero(boolT testall);
    -int     qh_compareangle(const void *p1, const void *p2);
    -int     qh_comparemerge(const void *p1, const void *p2);
    -int     qh_comparevisit(const void *p1, const void *p2);
    -void    qh_copynonconvex(ridgeT *atridge);
    -void    qh_degen_redundant_facet(facetT *facet);
    -void    qh_degen_redundant_neighbors(facetT *facet, facetT *delfacet);
    -vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges);
    -void    qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
    -           facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp);
    -facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp);
    -void    qh_flippedmerges(facetT *facetlist, boolT *wasmerge);
    -void    qh_forcedmerges( boolT *wasmerge);
    -void    qh_getmergeset(facetT *facetlist);
    -void    qh_getmergeset_initial(facetT *facetlist);
    -void    qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex);
    -ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
    -              vertexT *vertex, vertexT *oldvertex, int *hashslot);
    -void    qh_makeridges(facetT *facet);
    -void    qh_mark_dupridges(facetT *facetlist);
    -void    qh_maydropneighbor(facetT *facet);
    -int     qh_merge_degenredundant(void);
    -void    qh_merge_nonconvex( facetT *facet1, facetT *facet2, mergeType mergetype);
    -void    qh_mergecycle(facetT *samecycle, facetT *newfacet);
    -void    qh_mergecycle_all(facetT *facetlist, boolT *wasmerge);
    -void    qh_mergecycle_facets( facetT *samecycle, facetT *newfacet);
    -void    qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet);
    -void    qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet);
    -void    qh_mergecycle_vneighbors( facetT *samecycle, facetT *newfacet);
    -void    qh_mergefacet(facetT *facet1, facetT *facet2, realT *mindist, realT *maxdist, boolT mergeapex);
    -void    qh_mergefacet2d(facetT *facet1, facetT *facet2);
    -void    qh_mergeneighbors(facetT *facet1, facetT *facet2);
    -void    qh_mergeridges(facetT *facet1, facetT *facet2);
    -void    qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex);
    -void    qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2);
    -void    qh_mergevertex_neighbors(facetT *facet1, facetT *facet2);
    -void    qh_mergevertices(setT *vertices1, setT **vertices);
    -setT   *qh_neighbor_intersections(vertexT *vertex);
    -void    qh_newvertices(setT *vertices);
    -boolT   qh_reducevertices(void);
    -vertexT *qh_redundant_vertex(vertexT *vertex);
    -boolT   qh_remove_extravertices(facetT *facet);
    -vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet);
    -void    qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex);
    -void    qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges,
    -                        facetT *oldfacet, facetT *neighborA);
    -boolT   qh_test_appendmerge(facetT *facet, facetT *neighbor);
    -boolT   qh_test_vneighbors(void /* qh newfacet_list */);
    -void    qh_tracemerge(facetT *facet1, facetT *facet2);
    -void    qh_tracemerging(void);
    -void    qh_updatetested( facetT *facet1, facetT *facet2);
    -setT   *qh_vertexridges(vertexT *vertex);
    -void    qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges);
    -void    qh_willdelete(facetT *facet, facetT *replace);
    -
    -#endif /* qhDEFmerge */
    diff --git a/extern/qhull/poly.c b/extern/qhull/poly.c
    deleted file mode 100644
    index 436cbad20547..000000000000
    --- a/extern/qhull/poly.c
    +++ /dev/null
    @@ -1,1196 +0,0 @@
    -/*
      ---------------------------------
    -
    -   poly.c
    -   implements polygons and simplices
    -
    -   see qh-poly.htm, poly.h and libqhull.h
    -
    -   infrequent code is in poly2.c
    -   (all but top 50 and their callers 12/3/95)
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/poly.c#5 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*======== functions in alphabetical order ==========*/
    -
    -/*---------------------------------
    -
    -  qh_appendfacet( facet )
    -    appends facet to end of qh.facet_list,
    -
    -  returns:
    -    updates qh.newfacet_list, facet_next, facet_list
    -    increments qh.numfacets
    -
    -  notes:
    -    assumes qh.facet_list/facet_tail is defined (createsimplex)
    -
    -  see:
    -    qh_removefacet()
    -
    -*/
    -void qh_appendfacet(facetT *facet) {
    -  facetT *tail= qh facet_tail;
    -
    -  if (tail == qh newfacet_list)
    -    qh newfacet_list= facet;
    -  if (tail == qh facet_next)
    -    qh facet_next= facet;
    -  facet->previous= tail->previous;
    -  facet->next= tail;
    -  if (tail->previous)
    -    tail->previous->next= facet;
    -  else
    -    qh facet_list= facet;
    -  tail->previous= facet;
    -  qh num_facets++;
    -  trace4((qh ferr, 4044, "qh_appendfacet: append f%d to facet_list\n", facet->id));
    -} /* appendfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_appendvertex( vertex )
    -    appends vertex to end of qh.vertex_list,
    -
    -  returns:
    -    sets vertex->newlist
    -    updates qh.vertex_list, newvertex_list
    -    increments qh.num_vertices
    -
    -  notes:
    -    assumes qh.vertex_list/vertex_tail is defined (createsimplex)
    -
    -*/
    -void qh_appendvertex(vertexT *vertex) {
    -  vertexT *tail= qh vertex_tail;
    -
    -  if (tail == qh newvertex_list)
    -    qh newvertex_list= vertex;
    -  vertex->newlist= True;
    -  vertex->previous= tail->previous;
    -  vertex->next= tail;
    -  if (tail->previous)
    -    tail->previous->next= vertex;
    -  else
    -    qh vertex_list= vertex;
    -  tail->previous= vertex;
    -  qh num_vertices++;
    -  trace4((qh ferr, 4045, "qh_appendvertex: append v%d to vertex_list\n", vertex->id));
    -} /* appendvertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_attachnewfacets( )
    -    attach horizon facets to new facets in qh.newfacet_list
    -    newfacets have neighbor and ridge links to horizon but not vice versa
    -    only needed for qh.ONLYgood
    -
    -  returns:
    -    set qh.NEWfacets
    -    horizon facets linked to new facets
    -      ridges changed from visible facets to new facets
    -      simplicial ridges deleted
    -    qh.visible_list, no ridges valid
    -    facet->f.replace is a newfacet (if any)
    -
    -  design:
    -    delete interior ridges and neighbor sets by
    -      for each visible, non-simplicial facet
    -        for each ridge
    -          if last visit or if neighbor is simplicial
    -            if horizon neighbor
    -              delete ridge for horizon's ridge set
    -            delete ridge
    -        erase neighbor set
    -    attach horizon facets and new facets by
    -      for all new facets
    -        if corresponding horizon facet is simplicial
    -          locate corresponding visible facet {may be more than one}
    -          link visible facet to new facet
    -          replace visible facet with new facet in horizon
    -        else it's non-simplicial
    -          for all visible neighbors of the horizon facet
    -            link visible neighbor to new facet
    -            delete visible neighbor from horizon facet
    -          append new facet to horizon's neighbors
    -          the first ridge of the new facet is the horizon ridge
    -          link the new facet into the horizon ridge
    -*/
    -void qh_attachnewfacets(void ) {
    -  facetT *newfacet= NULL, *neighbor, **neighborp, *horizon, *visible;
    -  ridgeT *ridge, **ridgep;
    -
    -  qh NEWfacets= True;
    -  trace3((qh ferr, 3012, "qh_attachnewfacets: delete interior ridges\n"));
    -  qh visit_id++;
    -  FORALLvisible_facets {
    -    visible->visitid= qh visit_id;
    -    if (visible->ridges) {
    -      FOREACHridge_(visible->ridges) {
    -        neighbor= otherfacet_(ridge, visible);
    -        if (neighbor->visitid == qh visit_id
    -            || (!neighbor->visible && neighbor->simplicial)) {
    -          if (!neighbor->visible)  /* delete ridge for simplicial horizon */
    -            qh_setdel(neighbor->ridges, ridge);
    -          qh_setfree(&(ridge->vertices)); /* delete on 2nd visit */
    -          qh_memfree(ridge, (int)sizeof(ridgeT));
    -        }
    -      }
    -      SETfirst_(visible->ridges)= NULL;
    -    }
    -    SETfirst_(visible->neighbors)= NULL;
    -  }
    -  trace1((qh ferr, 1017, "qh_attachnewfacets: attach horizon facets to new facets\n"));
    -  FORALLnew_facets {
    -    horizon= SETfirstt_(newfacet->neighbors, facetT);
    -    if (horizon->simplicial) {
    -      visible= NULL;
    -      FOREACHneighbor_(horizon) {   /* may have more than one horizon ridge */
    -        if (neighbor->visible) {
    -          if (visible) {
    -            if (qh_setequal_skip(newfacet->vertices, 0, horizon->vertices,
    -                                  SETindex_(horizon->neighbors, neighbor))) {
    -              visible= neighbor;
    -              break;
    -            }
    -          }else
    -            visible= neighbor;
    -        }
    -      }
    -      if (visible) {
    -        visible->f.replace= newfacet;
    -        qh_setreplace(horizon->neighbors, visible, newfacet);
    -      }else {
    -        qh_fprintf(qh ferr, 6102, "qhull internal error (qh_attachnewfacets): couldn't find visible facet for horizon f%d of newfacet f%d\n",
    -                 horizon->id, newfacet->id);
    -        qh_errexit2 (qh_ERRqhull, horizon, newfacet);
    -      }
    -    }else { /* non-simplicial, with a ridge for newfacet */
    -      FOREACHneighbor_(horizon) {    /* may hold for many new facets */
    -        if (neighbor->visible) {
    -          neighbor->f.replace= newfacet;
    -          qh_setdelnth(horizon->neighbors,
    -                        SETindex_(horizon->neighbors, neighbor));
    -          neighborp--; /* repeat */
    -        }
    -      }
    -      qh_setappend(&horizon->neighbors, newfacet);
    -      ridge= SETfirstt_(newfacet->ridges, ridgeT);
    -      if (ridge->top == horizon)
    -        ridge->bottom= newfacet;
    -      else
    -        ridge->top= newfacet;
    -      }
    -  } /* newfacets */
    -  if (qh PRINTstatistics) {
    -    FORALLvisible_facets {
    -      if (!visible->f.replace)
    -        zinc_(Zinsidevisible);
    -    }
    -  }
    -} /* attachnewfacets */
    -
    -/*---------------------------------
    -
    -  qh_checkflipped( facet, dist, allerror )
    -    checks facet orientation to interior point
    -
    -    if allerror set,
    -      tests against qh.DISTround
    -    else
    -      tests against 0 since tested against DISTround before
    -
    -  returns:
    -    False if it flipped orientation (sets facet->flipped)
    -    distance if non-NULL
    -*/
    -boolT qh_checkflipped(facetT *facet, realT *distp, boolT allerror) {
    -  realT dist;
    -
    -  if (facet->flipped && !distp)
    -    return False;
    -  zzinc_(Zdistcheck);
    -  qh_distplane(qh interior_point, facet, &dist);
    -  if (distp)
    -    *distp= dist;
    -  if ((allerror && dist > -qh DISTround)|| (!allerror && dist >= 0.0)) {
    -    facet->flipped= True;
    -    zzinc_(Zflippedfacets);
    -    trace0((qh ferr, 19, "qh_checkflipped: facet f%d is flipped, distance= %6.12g during p%d\n",
    -              facet->id, dist, qh furthest_id));
    -    qh_precision("flipped facet");
    -    return False;
    -  }
    -  return True;
    -} /* checkflipped */
    -
    -/*---------------------------------
    -
    -  qh_delfacet( facet )
    -    removes facet from facet_list and frees up its memory
    -
    -  notes:
    -    assumes vertices and ridges already freed
    -*/
    -void qh_delfacet(facetT *facet) {
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  trace4((qh ferr, 4046, "qh_delfacet: delete f%d\n", facet->id));
    -  if (facet == qh tracefacet)
    -    qh tracefacet= NULL;
    -  if (facet == qh GOODclosest)
    -    qh GOODclosest= NULL;
    -  qh_removefacet(facet);
    -  if (!facet->tricoplanar || facet->keepcentrum) {
    -    qh_memfree_(facet->normal, qh normal_size, freelistp);
    -    if (qh CENTERtype == qh_ASvoronoi) {   /* uses macro calls */
    -      qh_memfree_(facet->center, qh center_size, freelistp);
    -    }else /* AScentrum */ {
    -      qh_memfree_(facet->center, qh normal_size, freelistp);
    -    }
    -  }
    -  qh_setfree(&(facet->neighbors));
    -  if (facet->ridges)
    -    qh_setfree(&(facet->ridges));
    -  qh_setfree(&(facet->vertices));
    -  if (facet->outsideset)
    -    qh_setfree(&(facet->outsideset));
    -  if (facet->coplanarset)
    -    qh_setfree(&(facet->coplanarset));
    -  qh_memfree_(facet, (int)sizeof(facetT), freelistp);
    -} /* delfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_deletevisible()
    -    delete visible facets and vertices
    -
    -  returns:
    -    deletes each facet and removes from facetlist
    -    at exit, qh.visible_list empty (== qh.newfacet_list)
    -
    -  notes:
    -    ridges already deleted
    -    horizon facets do not reference facets on qh.visible_list
    -    new facets in qh.newfacet_list
    -    uses   qh.visit_id;
    -*/
    -void qh_deletevisible(void /*qh visible_list*/) {
    -  facetT *visible, *nextfacet;
    -  vertexT *vertex, **vertexp;
    -  int numvisible= 0, numdel= qh_setsize(qh del_vertices);
    -
    -  trace1((qh ferr, 1018, "qh_deletevisible: delete %d visible facets and %d vertices\n",
    -         qh num_visible, numdel));
    -  for (visible= qh visible_list; visible && visible->visible;
    -                visible= nextfacet) { /* deleting current */
    -    nextfacet= visible->next;
    -    numvisible++;
    -    qh_delfacet(visible);
    -  }
    -  if (numvisible != qh num_visible) {
    -    qh_fprintf(qh ferr, 6103, "qhull internal error (qh_deletevisible): qh num_visible %d is not number of visible facets %d\n",
    -             qh num_visible, numvisible);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  qh num_visible= 0;
    -  zadd_(Zvisfacettot, numvisible);
    -  zmax_(Zvisfacetmax, numvisible);
    -  zzadd_(Zdelvertextot, numdel);
    -  zmax_(Zdelvertexmax, numdel);
    -  FOREACHvertex_(qh del_vertices)
    -    qh_delvertex(vertex);
    -  qh_settruncate(qh del_vertices, 0);
    -} /* deletevisible */
    -
    -/*---------------------------------
    -
    -  qh_facetintersect( facetA, facetB, skipa, skipB, prepend )
    -    return vertices for intersection of two simplicial facets
    -    may include 1 prepended entry (if more, need to settemppush)
    -
    -  returns:
    -    returns set of qh.hull_dim-1 + prepend vertices
    -    returns skipped index for each test and checks for exactly one
    -
    -  notes:
    -    does not need settemp since set in quick memory
    -
    -  see also:
    -    qh_vertexintersect and qh_vertexintersect_new
    -    use qh_setnew_delnthsorted to get nth ridge (no skip information)
    -
    -  design:
    -    locate skipped vertex by scanning facet A's neighbors
    -    locate skipped vertex by scanning facet B's neighbors
    -    intersect the vertex sets
    -*/
    -setT *qh_facetintersect(facetT *facetA, facetT *facetB,
    -                         int *skipA,int *skipB, int prepend) {
    -  setT *intersect;
    -  int dim= qh hull_dim, i, j;
    -  facetT **neighborsA, **neighborsB;
    -
    -  neighborsA= SETaddr_(facetA->neighbors, facetT);
    -  neighborsB= SETaddr_(facetB->neighbors, facetT);
    -  i= j= 0;
    -  if (facetB == *neighborsA++)
    -    *skipA= 0;
    -  else if (facetB == *neighborsA++)
    -    *skipA= 1;
    -  else if (facetB == *neighborsA++)
    -    *skipA= 2;
    -  else {
    -    for (i=3; i < dim; i++) {
    -      if (facetB == *neighborsA++) {
    -        *skipA= i;
    -        break;
    -      }
    -    }
    -  }
    -  if (facetA == *neighborsB++)
    -    *skipB= 0;
    -  else if (facetA == *neighborsB++)
    -    *skipB= 1;
    -  else if (facetA == *neighborsB++)
    -    *skipB= 2;
    -  else {
    -    for (j=3; j < dim; j++) {
    -      if (facetA == *neighborsB++) {
    -        *skipB= j;
    -        break;
    -      }
    -    }
    -  }
    -  if (i >= dim || j >= dim) {
    -    qh_fprintf(qh ferr, 6104, "qhull internal error (qh_facetintersect): f%d or f%d not in others neighbors\n",
    -            facetA->id, facetB->id);
    -    qh_errexit2 (qh_ERRqhull, facetA, facetB);
    -  }
    -  intersect= qh_setnew_delnthsorted(facetA->vertices, qh hull_dim, *skipA, prepend);
    -  trace4((qh ferr, 4047, "qh_facetintersect: f%d skip %d matches f%d skip %d\n",
    -          facetA->id, *skipA, facetB->id, *skipB));
    -  return(intersect);
    -} /* facetintersect */
    -
    -/*---------------------------------
    -
    -  qh_gethash( hashsize, set, size, firstindex, skipelem )
    -    return hashvalue for a set with firstindex and skipelem
    -
    -  notes:
    -    returned hash is in [0,hashsize)
    -    assumes at least firstindex+1 elements
    -    assumes skipelem is NULL, in set, or part of hash
    -
    -    hashes memory addresses which may change over different runs of the same data
    -    using sum for hash does badly in high d
    -*/
    -int qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem) {
    -  void **elemp= SETelemaddr_(set, firstindex, void);
    -  ptr_intT hash = 0, elem;
    -  unsigned result;
    -  int i;
    -#ifdef _MSC_VER                   /* Microsoft Visual C++ -- warn about 64-bit issues */
    -#pragma warning( push)            /* WARN64 -- ptr_intT holds a 64-bit pointer */
    -#pragma warning( disable : 4311)  /* 'type cast': pointer truncation from 'void*' to 'ptr_intT' */
    -#endif
    -
    -  switch (size-firstindex) {
    -  case 1:
    -    hash= (ptr_intT)(*elemp) - (ptr_intT) skipelem;
    -    break;
    -  case 2:
    -    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] - (ptr_intT) skipelem;
    -    break;
    -  case 3:
    -    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
    -      - (ptr_intT) skipelem;
    -    break;
    -  case 4:
    -    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
    -      + (ptr_intT)elemp[3] - (ptr_intT) skipelem;
    -    break;
    -  case 5:
    -    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
    -      + (ptr_intT)elemp[3] + (ptr_intT)elemp[4] - (ptr_intT) skipelem;
    -    break;
    -  case 6:
    -    hash= (ptr_intT)(*elemp) + (ptr_intT)elemp[1] + (ptr_intT)elemp[2]
    -      + (ptr_intT)elemp[3] + (ptr_intT)elemp[4]+ (ptr_intT)elemp[5]
    -      - (ptr_intT) skipelem;
    -    break;
    -  default:
    -    hash= 0;
    -    i= 3;
    -    do {     /* this is about 10% in 10-d */
    -      if ((elem= (ptr_intT)*elemp++) != (ptr_intT)skipelem) {
    -        hash ^= (elem << i) + (elem >> (32-i));
    -        i += 3;
    -        if (i >= 32)
    -          i -= 32;
    -      }
    -    }while (*elemp);
    -    break;
    -  }
    -  if (hashsize<0) {
    -    qh_fprintf(qh ferr, 6202, "qhull internal error: negative hashsize %d passed to qh_gethash [poly.c]\n", hashsize);
    -    qh_errexit2 (qh_ERRqhull, NULL, NULL);
    -  }
    -  result= (unsigned)hash;
    -  result %= (unsigned)hashsize;
    -  /* result= 0; for debugging */
    -  return result;
    -#ifdef _MSC_VER
    -#pragma warning( pop)
    -#endif
    -} /* gethash */
    -
    -/*---------------------------------
    -
    -  qh_makenewfacet( vertices, toporient, horizon )
    -    creates a toporient? facet from vertices
    -
    -  returns:
    -    returns newfacet
    -      adds newfacet to qh.facet_list
    -      newfacet->vertices= vertices
    -      if horizon
    -        newfacet->neighbor= horizon, but not vice versa
    -    newvertex_list updated with vertices
    -*/
    -facetT *qh_makenewfacet(setT *vertices, boolT toporient,facetT *horizon) {
    -  facetT *newfacet;
    -  vertexT *vertex, **vertexp;
    -
    -  FOREACHvertex_(vertices) {
    -    if (!vertex->newlist) {
    -      qh_removevertex(vertex);
    -      qh_appendvertex(vertex);
    -    }
    -  }
    -  newfacet= qh_newfacet();
    -  newfacet->vertices= vertices;
    -  newfacet->toporient= (unsigned char)toporient;
    -  if (horizon)
    -    qh_setappend(&(newfacet->neighbors), horizon);
    -  qh_appendfacet(newfacet);
    -  return(newfacet);
    -} /* makenewfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_makenewplanes()
    -    make new hyperplanes for facets on qh.newfacet_list
    -
    -  returns:
    -    all facets have hyperplanes or are marked for   merging
    -    doesn't create hyperplane if horizon is coplanar (will merge)
    -    updates qh.min_vertex if qh.JOGGLEmax
    -
    -  notes:
    -    facet->f.samecycle is defined for facet->mergehorizon facets
    -*/
    -void qh_makenewplanes(void /* newfacet_list */) {
    -  facetT *newfacet;
    -
    -  FORALLnew_facets {
    -    if (!newfacet->mergehorizon)
    -      qh_setfacetplane(newfacet);
    -  }
    -  if (qh JOGGLEmax < REALmax/2)
    -    minimize_(qh min_vertex, -wwval_(Wnewvertexmax));
    -} /* makenewplanes */
    -
    -/*---------------------------------
    -
    -  qh_makenew_nonsimplicial( visible, apex, numnew )
    -    make new facets for ridges of a visible facet
    -
    -  returns:
    -    first newfacet, bumps numnew as needed
    -    attaches new facets if !qh.ONLYgood
    -    marks ridge neighbors for simplicial visible
    -    if (qh.ONLYgood)
    -      ridges on newfacet, horizon, and visible
    -    else
    -      ridge and neighbors between newfacet and   horizon
    -      visible facet's ridges are deleted
    -
    -  notes:
    -    qh.visit_id if visible has already been processed
    -    sets neighbor->seen for building f.samecycle
    -      assumes all 'seen' flags initially false
    -
    -  design:
    -    for each ridge of visible facet
    -      get neighbor of visible facet
    -      if neighbor was already processed
    -        delete the ridge (will delete all visible facets later)
    -      if neighbor is a horizon facet
    -        create a new facet
    -        if neighbor coplanar
    -          adds newfacet to f.samecycle for later merging
    -        else
    -          updates neighbor's neighbor set
    -          (checks for non-simplicial facet with multiple ridges to visible facet)
    -        updates neighbor's ridge set
    -        (checks for simplicial neighbor to non-simplicial visible facet)
    -        (deletes ridge if neighbor is simplicial)
    -
    -*/
    -#ifndef qh_NOmerge
    -facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
    -  void **freelistp; /* used !qh_NOmem */
    -  ridgeT *ridge, **ridgep;
    -  facetT *neighbor, *newfacet= NULL, *samecycle;
    -  setT *vertices;
    -  boolT toporient;
    -  int ridgeid;
    -
    -  FOREACHridge_(visible->ridges) {
    -    ridgeid= ridge->id;
    -    neighbor= otherfacet_(ridge, visible);
    -    if (neighbor->visible) {
    -      if (!qh ONLYgood) {
    -        if (neighbor->visitid == qh visit_id) {
    -          qh_setfree(&(ridge->vertices));  /* delete on 2nd visit */
    -          qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    -        }
    -      }
    -    }else {  /* neighbor is an horizon facet */
    -      toporient= (ridge->top == visible);
    -      vertices= qh_setnew(qh hull_dim); /* makes sure this is quick */
    -      qh_setappend(&vertices, apex);
    -      qh_setappend_set(&vertices, ridge->vertices);
    -      newfacet= qh_makenewfacet(vertices, toporient, neighbor);
    -      (*numnew)++;
    -      if (neighbor->coplanar) {
    -        newfacet->mergehorizon= True;
    -        if (!neighbor->seen) {
    -          newfacet->f.samecycle= newfacet;
    -          neighbor->f.newcycle= newfacet;
    -        }else {
    -          samecycle= neighbor->f.newcycle;
    -          newfacet->f.samecycle= samecycle->f.samecycle;
    -          samecycle->f.samecycle= newfacet;
    -        }
    -      }
    -      if (qh ONLYgood) {
    -        if (!neighbor->simplicial)
    -          qh_setappend(&(newfacet->ridges), ridge);
    -      }else {  /* qh_attachnewfacets */
    -        if (neighbor->seen) {
    -          if (neighbor->simplicial) {
    -            qh_fprintf(qh ferr, 6105, "qhull internal error (qh_makenew_nonsimplicial): simplicial f%d sharing two ridges with f%d\n",
    -                   neighbor->id, visible->id);
    -            qh_errexit2 (qh_ERRqhull, neighbor, visible);
    -          }
    -          qh_setappend(&(neighbor->neighbors), newfacet);
    -        }else
    -          qh_setreplace(neighbor->neighbors, visible, newfacet);
    -        if (neighbor->simplicial) {
    -          qh_setdel(neighbor->ridges, ridge);
    -          qh_setfree(&(ridge->vertices));
    -          qh_memfree(ridge, (int)sizeof(ridgeT));
    -        }else {
    -          qh_setappend(&(newfacet->ridges), ridge);
    -          if (toporient)
    -            ridge->top= newfacet;
    -          else
    -            ridge->bottom= newfacet;
    -        }
    -      trace4((qh ferr, 4048, "qh_makenew_nonsimplicial: created facet f%d from v%d and r%d of horizon f%d\n",
    -            newfacet->id, apex->id, ridgeid, neighbor->id));
    -      }
    -    }
    -    neighbor->seen= True;
    -  } /* for each ridge */
    -  if (!qh ONLYgood)
    -    SETfirst_(visible->ridges)= NULL;
    -  return newfacet;
    -} /* makenew_nonsimplicial */
    -#else /* qh_NOmerge */
    -facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew) {
    -  return NULL;
    -}
    -#endif /* qh_NOmerge */
    -
    -/*---------------------------------
    -
    -  qh_makenew_simplicial( visible, apex, numnew )
    -    make new facets for simplicial visible facet and apex
    -
    -  returns:
    -    attaches new facets if (!qh.ONLYgood)
    -      neighbors between newfacet and horizon
    -
    -  notes:
    -    nop if neighbor->seen or neighbor->visible(see qh_makenew_nonsimplicial)
    -
    -  design:
    -    locate neighboring horizon facet for visible facet
    -    determine vertices and orientation
    -    create new facet
    -    if coplanar,
    -      add new facet to f.samecycle
    -    update horizon facet's neighbor list
    -*/
    -facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew) {
    -  facetT *neighbor, **neighborp, *newfacet= NULL;
    -  setT *vertices;
    -  boolT flip, toporient;
    -  int horizonskip, visibleskip;
    -
    -  FOREACHneighbor_(visible) {
    -    if (!neighbor->seen && !neighbor->visible) {
    -      vertices= qh_facetintersect(neighbor,visible, &horizonskip, &visibleskip, 1);
    -      SETfirst_(vertices)= apex;
    -      flip= ((horizonskip & 0x1) ^ (visibleskip & 0x1));
    -      if (neighbor->toporient)
    -        toporient= horizonskip & 0x1;
    -      else
    -        toporient= (horizonskip & 0x1) ^ 0x1;
    -      newfacet= qh_makenewfacet(vertices, toporient, neighbor);
    -      (*numnew)++;
    -      if (neighbor->coplanar && (qh PREmerge || qh MERGEexact)) {
    -#ifndef qh_NOmerge
    -        newfacet->f.samecycle= newfacet;
    -        newfacet->mergehorizon= True;
    -#endif
    -      }
    -      if (!qh ONLYgood)
    -        SETelem_(neighbor->neighbors, horizonskip)= newfacet;
    -      trace4((qh ferr, 4049, "qh_makenew_simplicial: create facet f%d top %d from v%d and horizon f%d skip %d top %d and visible f%d skip %d, flip? %d\n",
    -            newfacet->id, toporient, apex->id, neighbor->id, horizonskip,
    -              neighbor->toporient, visible->id, visibleskip, flip));
    -    }
    -  }
    -  return newfacet;
    -} /* makenew_simplicial */
    -
    -/*---------------------------------
    -
    -  qh_matchneighbor( newfacet, newskip, hashsize, hashcount )
    -    either match subridge of newfacet with neighbor or add to hash_table
    -
    -  returns:
    -    duplicate ridges are unmatched and marked by qh_DUPLICATEridge
    -
    -  notes:
    -    ridge is newfacet->vertices w/o newskip vertex
    -    do not allocate memory (need to free hash_table cleanly)
    -    uses linear hash chains
    -
    -  see also:
    -    qh_matchduplicates
    -
    -  design:
    -    for each possible matching facet in qh.hash_table
    -      if vertices match
    -        set ismatch, if facets have opposite orientation
    -        if ismatch and matching facet doesn't have a match
    -          match the facets by updating their neighbor sets
    -        else
    -          indicate a duplicate ridge
    -          set facet hyperplane for later testing
    -          add facet to hashtable
    -          unless the other facet was already a duplicate ridge
    -            mark both facets with a duplicate ridge
    -            add other facet (if defined) to hash table
    -*/
    -void qh_matchneighbor(facetT *newfacet, int newskip, int hashsize, int *hashcount) {
    -  boolT newfound= False;   /* True, if new facet is already in hash chain */
    -  boolT same, ismatch;
    -  int hash, scan;
    -  facetT *facet, *matchfacet;
    -  int skip, matchskip;
    -
    -  hash= qh_gethash(hashsize, newfacet->vertices, qh hull_dim, 1,
    -                     SETelem_(newfacet->vertices, newskip));
    -  trace4((qh ferr, 4050, "qh_matchneighbor: newfacet f%d skip %d hash %d hashcount %d\n",
    -          newfacet->id, newskip, hash, *hashcount));
    -  zinc_(Zhashlookup);
    -  for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
    -       scan= (++scan >= hashsize ? 0 : scan)) {
    -    if (facet == newfacet) {
    -      newfound= True;
    -      continue;
    -    }
    -    zinc_(Zhashtests);
    -    if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
    -      if (SETelem_(newfacet->vertices, newskip) ==
    -          SETelem_(facet->vertices, skip)) {
    -        qh_precision("two facets with the same vertices");
    -        qh_fprintf(qh ferr, 6106, "qhull precision error: Vertex sets are the same for f%d and f%d.  Can not force output.\n",
    -          facet->id, newfacet->id);
    -        qh_errexit2 (qh_ERRprec, facet, newfacet);
    -      }
    -      ismatch= (same == (boolT)((newfacet->toporient ^ facet->toporient)));
    -      matchfacet= SETelemt_(facet->neighbors, skip, facetT);
    -      if (ismatch && !matchfacet) {
    -        SETelem_(facet->neighbors, skip)= newfacet;
    -        SETelem_(newfacet->neighbors, newskip)= facet;
    -        (*hashcount)--;
    -        trace4((qh ferr, 4051, "qh_matchneighbor: f%d skip %d matched with new f%d skip %d\n",
    -           facet->id, skip, newfacet->id, newskip));
    -        return;
    -      }
    -      if (!qh PREmerge && !qh MERGEexact) {
    -        qh_precision("a ridge with more than two neighbors");
    -        qh_fprintf(qh ferr, 6107, "qhull precision error: facets f%d, f%d and f%d meet at a ridge with more than 2 neighbors.  Can not continue.\n",
    -                 facet->id, newfacet->id, getid_(matchfacet));
    -        qh_errexit2 (qh_ERRprec, facet, newfacet);
    -      }
    -      SETelem_(newfacet->neighbors, newskip)= qh_DUPLICATEridge;
    -      newfacet->dupridge= True;
    -      if (!newfacet->normal)
    -        qh_setfacetplane(newfacet);
    -      qh_addhash(newfacet, qh hash_table, hashsize, hash);
    -      (*hashcount)++;
    -      if (!facet->normal)
    -        qh_setfacetplane(facet);
    -      if (matchfacet != qh_DUPLICATEridge) {
    -        SETelem_(facet->neighbors, skip)= qh_DUPLICATEridge;
    -        facet->dupridge= True;
    -        if (!facet->normal)
    -          qh_setfacetplane(facet);
    -        if (matchfacet) {
    -          matchskip= qh_setindex(matchfacet->neighbors, facet);
    -          SETelem_(matchfacet->neighbors, matchskip)= qh_DUPLICATEridge;
    -          matchfacet->dupridge= True;
    -          if (!matchfacet->normal)
    -            qh_setfacetplane(matchfacet);
    -          qh_addhash(matchfacet, qh hash_table, hashsize, hash);
    -          *hashcount += 2;
    -        }
    -      }
    -      trace4((qh ferr, 4052, "qh_matchneighbor: new f%d skip %d duplicates ridge for f%d skip %d matching f%d ismatch %d at hash %d\n",
    -           newfacet->id, newskip, facet->id, skip,
    -           (matchfacet == qh_DUPLICATEridge ? -2 : getid_(matchfacet)),
    -           ismatch, hash));
    -      return; /* end of duplicate ridge */
    -    }
    -  }
    -  if (!newfound)
    -    SETelem_(qh hash_table, scan)= newfacet;  /* same as qh_addhash */
    -  (*hashcount)++;
    -  trace4((qh ferr, 4053, "qh_matchneighbor: no match for f%d skip %d at hash %d\n",
    -           newfacet->id, newskip, hash));
    -} /* matchneighbor */
    -
    -
    -/*---------------------------------
    -
    -  qh_matchnewfacets()
    -    match newfacets in qh.newfacet_list to their newfacet neighbors
    -
    -  returns:
    -    qh.newfacet_list with full neighbor sets
    -      get vertices with nth neighbor by deleting nth vertex
    -    if qh.PREmerge/MERGEexact or qh.FORCEoutput
    -      sets facet->flippped if flipped normal (also prevents point partitioning)
    -    if duplicate ridges and qh.PREmerge/MERGEexact
    -      sets facet->dupridge
    -      missing neighbor links identifies extra ridges to be merging (qh_MERGEridge)
    -
    -  notes:
    -    newfacets already have neighbor[0] (horizon facet)
    -    assumes qh.hash_table is NULL
    -    vertex->neighbors has not been updated yet
    -    do not allocate memory after qh.hash_table (need to free it cleanly)
    -
    -  design:
    -    delete neighbor sets for all new facets
    -    initialize a hash table
    -    for all new facets
    -      match facet with neighbors
    -    if unmatched facets (due to duplicate ridges)
    -      for each new facet with a duplicate ridge
    -        match it with a facet
    -    check for flipped facets
    -*/
    -void qh_matchnewfacets(void /* qh newfacet_list */) {
    -  int numnew=0, hashcount=0, newskip;
    -  facetT *newfacet, *neighbor;
    -  int dim= qh hull_dim, hashsize, neighbor_i, neighbor_n;
    -  setT *neighbors;
    -#ifndef qh_NOtrace
    -  int facet_i, facet_n, numfree= 0;
    -  facetT *facet;
    -#endif
    -
    -  trace1((qh ferr, 1019, "qh_matchnewfacets: match neighbors for new facets.\n"));
    -  FORALLnew_facets {
    -    numnew++;
    -    {  /* inline qh_setzero(newfacet->neighbors, 1, qh hull_dim); */
    -      neighbors= newfacet->neighbors;
    -      neighbors->e[neighbors->maxsize].i= dim+1; /*may be overwritten*/
    -      memset((char *)SETelemaddr_(neighbors, 1, void), 0, dim * SETelemsize);
    -    }
    -  }
    -
    -  qh_newhashtable(numnew*(qh hull_dim-1)); /* twice what is normally needed,
    -                                     but every ridge could be DUPLICATEridge */
    -  hashsize= qh_setsize(qh hash_table);
    -  FORALLnew_facets {
    -    for (newskip=1; newskipneighbors, k, facetT);
    -          if (!neighbor || neighbor == qh_DUPLICATEridge)
    -            count++;
    -        }
    -        if (facet == newfacet)
    -          break;
    -      }
    -      if (count != hashcount) {
    -        qh_fprintf(qh ferr, 8088, "qh_matchnewfacets: after adding facet %d, hashcount %d != count %d\n",
    -                 newfacet->id, hashcount, count);
    -        qh_errexit(qh_ERRqhull, newfacet, NULL);
    -      }
    -    }
    -#endif  /* end of trap code */
    -  }
    -  if (hashcount) {
    -    FORALLnew_facets {
    -      if (newfacet->dupridge) {
    -        FOREACHneighbor_i_(newfacet) {
    -          if (neighbor == qh_DUPLICATEridge) {
    -            qh_matchduplicates(newfacet, neighbor_i, hashsize, &hashcount);
    -                    /* this may report MERGEfacet */
    -          }
    -        }
    -      }
    -    }
    -  }
    -  if (hashcount) {
    -    qh_fprintf(qh ferr, 6108, "qhull internal error (qh_matchnewfacets): %d neighbors did not match up\n",
    -        hashcount);
    -    qh_printhashtable(qh ferr);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -#ifndef qh_NOtrace
    -  if (qh IStracing >= 2) {
    -    FOREACHfacet_i_(qh hash_table) {
    -      if (!facet)
    -        numfree++;
    -    }
    -    qh_fprintf(qh ferr, 8089, "qh_matchnewfacets: %d new facets, %d unused hash entries .  hashsize %d\n",
    -             numnew, numfree, qh_setsize(qh hash_table));
    -  }
    -#endif /* !qh_NOtrace */
    -  qh_setfree(&qh hash_table);
    -  if (qh PREmerge || qh MERGEexact) {
    -    if (qh IStracing >= 4)
    -      qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
    -    FORALLnew_facets {
    -      if (newfacet->normal)
    -        qh_checkflipped(newfacet, NULL, qh_ALL);
    -    }
    -  }else if (qh FORCEoutput)
    -    qh_checkflipped_all(qh newfacet_list);  /* prints warnings for flipped */
    -} /* matchnewfacets */
    -
    -
    -/*---------------------------------
    -
    -  qh_matchvertices( firstindex, verticesA, skipA, verticesB, skipB, same )
    -    tests whether vertices match with a single skip
    -    starts match at firstindex since all new facets have a common vertex
    -
    -  returns:
    -    true if matched vertices
    -    skip index for each set
    -    sets same iff vertices have the same orientation
    -
    -  notes:
    -    assumes skipA is in A and both sets are the same size
    -
    -  design:
    -    set up pointers
    -    scan both sets checking for a match
    -    test orientation
    -*/
    -boolT qh_matchvertices(int firstindex, setT *verticesA, int skipA,
    -       setT *verticesB, int *skipB, boolT *same) {
    -  vertexT **elemAp, **elemBp, **skipBp=NULL, **skipAp;
    -
    -  elemAp= SETelemaddr_(verticesA, firstindex, vertexT);
    -  elemBp= SETelemaddr_(verticesB, firstindex, vertexT);
    -  skipAp= SETelemaddr_(verticesA, skipA, vertexT);
    -  do if (elemAp != skipAp) {
    -    while (*elemAp != *elemBp++) {
    -      if (skipBp)
    -        return False;
    -      skipBp= elemBp;  /* one extra like FOREACH */
    -    }
    -  }while (*(++elemAp));
    -  if (!skipBp)
    -    skipBp= ++elemBp;
    -  *skipB= SETindex_(verticesB, skipB); /* i.e., skipBp - verticesB */
    -  *same= !((skipA & 0x1) ^ (*skipB & 0x1)); /* result is 0 or 1 */
    -  trace4((qh ferr, 4054, "qh_matchvertices: matched by skip %d(v%d) and skip %d(v%d) same? %d\n",
    -          skipA, (*skipAp)->id, *skipB, (*(skipBp-1))->id, *same));
    -  return(True);
    -} /* matchvertices */
    -
    -/*---------------------------------
    -
    -  qh_newfacet()
    -    return a new facet
    -
    -  returns:
    -    all fields initialized or cleared   (NULL)
    -    preallocates neighbors set
    -*/
    -facetT *qh_newfacet(void) {
    -  facetT *facet;
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  qh_memalloc_((int)sizeof(facetT), freelistp, facet, facetT);
    -  memset((char *)facet, (size_t)0, sizeof(facetT));
    -  if (qh facet_id == qh tracefacet_id)
    -    qh tracefacet= facet;
    -  facet->id= qh facet_id++;
    -  facet->neighbors= qh_setnew(qh hull_dim);
    -#if !qh_COMPUTEfurthest
    -  facet->furthestdist= 0.0;
    -#endif
    -#if qh_MAXoutside
    -  if (qh FORCEoutput && qh APPROXhull)
    -    facet->maxoutside= qh MINoutside;
    -  else
    -    facet->maxoutside= qh DISTround;
    -#endif
    -  facet->simplicial= True;
    -  facet->good= True;
    -  facet->newfacet= True;
    -  trace4((qh ferr, 4055, "qh_newfacet: created facet f%d\n", facet->id));
    -  return(facet);
    -} /* newfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_newridge()
    -    return a new ridge
    -*/
    -ridgeT *qh_newridge(void) {
    -  ridgeT *ridge;
    -  void **freelistp;   /* used !qh_NOmem */
    -
    -  qh_memalloc_((int)sizeof(ridgeT), freelistp, ridge, ridgeT);
    -  memset((char *)ridge, (size_t)0, sizeof(ridgeT));
    -  zinc_(Ztotridges);
    -  if (qh ridge_id == 0xFFFFFF) {
    -    qh_fprintf(qh ferr, 7074, "\
    -qhull warning: more than %d ridges.  ID field overflows and two ridges\n\
    -may have the same identifier.  Otherwise output ok.\n", 0xFFFFFF);
    -  }
    -  ridge->id= qh ridge_id++;
    -  trace4((qh ferr, 4056, "qh_newridge: created ridge r%d\n", ridge->id));
    -  return(ridge);
    -} /* newridge */
    -
    -
    -/*---------------------------------
    -
    -  qh_pointid(  )
    -    return id for a point,
    -    returns -3 if null, -2 if interior, or -1 if not known
    -
    -  alternative code:
    -    unsigned long id;
    -    id= ((unsigned long)point - (unsigned long)qh.first_point)/qh.normal_size;
    -
    -  notes:
    -    WARN64 -- id truncated to 32-bits, at most 2G points
    -    NOerrors returned (QhullPoint::id)
    -    if point not in point array
    -      the code does a comparison of unrelated pointers.
    -*/
    -int qh_pointid(pointT *point) {
    -  ptr_intT offset, id;
    -
    -  if (!point)
    -    return -3;
    -  else if (point == qh interior_point)
    -    return -2;
    -  else if (point >= qh first_point
    -  && point < qh first_point + qh num_points * qh hull_dim) {
    -    offset= (ptr_intT)(point - qh first_point);
    -    id= offset / qh hull_dim;
    -  }else if ((id= qh_setindex(qh other_points, point)) != -1)
    -    id += qh num_points;
    -  else
    -    return -1;
    -  return (int)id;
    -} /* pointid */
    -
    -/*---------------------------------
    -
    -  qh_removefacet( facet )
    -    unlinks facet from qh.facet_list,
    -
    -  returns:
    -    updates qh.facet_list .newfacet_list .facet_next visible_list
    -    decrements qh.num_facets
    -
    -  see:
    -    qh_appendfacet
    -*/
    -void qh_removefacet(facetT *facet) {
    -  facetT *next= facet->next, *previous= facet->previous;
    -
    -  if (facet == qh newfacet_list)
    -    qh newfacet_list= next;
    -  if (facet == qh facet_next)
    -    qh facet_next= next;
    -  if (facet == qh visible_list)
    -    qh visible_list= next;
    -  if (previous) {
    -    previous->next= next;
    -    next->previous= previous;
    -  }else {  /* 1st facet in qh facet_list */
    -    qh facet_list= next;
    -    qh facet_list->previous= NULL;
    -  }
    -  qh num_facets--;
    -  trace4((qh ferr, 4057, "qh_removefacet: remove f%d from facet_list\n", facet->id));
    -} /* removefacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_removevertex( vertex )
    -    unlinks vertex from qh.vertex_list,
    -
    -  returns:
    -    updates qh.vertex_list .newvertex_list
    -    decrements qh.num_vertices
    -*/
    -void qh_removevertex(vertexT *vertex) {
    -  vertexT *next= vertex->next, *previous= vertex->previous;
    -
    -  if (vertex == qh newvertex_list)
    -    qh newvertex_list= next;
    -  if (previous) {
    -    previous->next= next;
    -    next->previous= previous;
    -  }else {  /* 1st vertex in qh vertex_list */
    -    qh vertex_list= vertex->next;
    -    qh vertex_list->previous= NULL;
    -  }
    -  qh num_vertices--;
    -  trace4((qh ferr, 4058, "qh_removevertex: remove v%d from vertex_list\n", vertex->id));
    -} /* removevertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_updatevertices()
    -    update vertex neighbors and delete interior vertices
    -
    -  returns:
    -    if qh.VERTEXneighbors, updates neighbors for each vertex
    -      if qh.newvertex_list,
    -         removes visible neighbors  from vertex neighbors
    -      if qh.newfacet_list
    -         adds new facets to vertex neighbors
    -    if qh.visible_list
    -       interior vertices added to qh.del_vertices for later partitioning
    -
    -  design:
    -    if qh.VERTEXneighbors
    -      deletes references to visible facets from vertex neighbors
    -      appends new facets to the neighbor list for each vertex
    -      checks all vertices of visible facets
    -        removes visible facets from neighbor lists
    -        marks unused vertices for deletion
    -*/
    -void qh_updatevertices(void /*qh newvertex_list, newfacet_list, visible_list*/) {
    -  facetT *newfacet= NULL, *neighbor, **neighborp, *visible;
    -  vertexT *vertex, **vertexp;
    -
    -  trace3((qh ferr, 3013, "qh_updatevertices: delete interior vertices and update vertex->neighbors\n"));
    -  if (qh VERTEXneighbors) {
    -    FORALLvertex_(qh newvertex_list) {
    -      FOREACHneighbor_(vertex) {
    -        if (neighbor->visible)
    -          SETref_(neighbor)= NULL;
    -      }
    -      qh_setcompact(vertex->neighbors);
    -    }
    -    FORALLnew_facets {
    -      FOREACHvertex_(newfacet->vertices)
    -        qh_setappend(&vertex->neighbors, newfacet);
    -    }
    -    FORALLvisible_facets {
    -      FOREACHvertex_(visible->vertices) {
    -        if (!vertex->newlist && !vertex->deleted) {
    -          FOREACHneighbor_(vertex) { /* this can happen under merging */
    -            if (!neighbor->visible)
    -              break;
    -          }
    -          if (neighbor)
    -            qh_setdel(vertex->neighbors, visible);
    -          else {
    -            vertex->deleted= True;
    -            qh_setappend(&qh del_vertices, vertex);
    -            trace2((qh ferr, 2041, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    -                  qh_pointid(vertex->point), vertex->id, visible->id));
    -          }
    -        }
    -      }
    -    }
    -  }else {  /* !VERTEXneighbors */
    -    FORALLvisible_facets {
    -      FOREACHvertex_(visible->vertices) {
    -        if (!vertex->newlist && !vertex->deleted) {
    -          vertex->deleted= True;
    -          qh_setappend(&qh del_vertices, vertex);
    -          trace2((qh ferr, 2042, "qh_updatevertices: delete vertex p%d(v%d) in f%d\n",
    -                  qh_pointid(vertex->point), vertex->id, visible->id));
    -        }
    -      }
    -    }
    -  }
    -} /* updatevertices */
    diff --git a/extern/qhull/poly.h b/extern/qhull/poly.h
    deleted file mode 100644
    index 9cf04cfbacf7..000000000000
    --- a/extern/qhull/poly.h
    +++ /dev/null
    @@ -1,295 +0,0 @@
    -/*
      ---------------------------------
    -
    -   poly.h
    -   header file for poly.c and poly2.c
    -
    -   see qh-poly.htm, libqhull.h and poly.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/poly.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFpoly
    -#define qhDEFpoly 1
    -
    -#include "libqhull.h"
    -
    -/*===============   constants ========================== */
    -
    -/*----------------------------------
    -
    -  ALGORITHMfault
    -    use as argument to checkconvex() to report errors during buildhull
    -*/
    -#define qh_ALGORITHMfault 0
    -
    -/*----------------------------------
    -
    -  DATAfault
    -    use as argument to checkconvex() to report errors during initialhull
    -*/
    -#define qh_DATAfault 1
    -
    -/*----------------------------------
    -
    -  DUPLICATEridge
    -    special value for facet->neighbor to indicate a duplicate ridge
    -
    -  notes:
    -    set by matchneighbor, used by matchmatch and mark_dupridge
    -*/
    -#define qh_DUPLICATEridge (facetT *)1L
    -
    -/*----------------------------------
    -
    -  MERGEridge       flag in facet
    -    special value for facet->neighbor to indicate a merged ridge
    -
    -  notes:
    -    set by matchneighbor, used by matchmatch and mark_dupridge
    -*/
    -#define qh_MERGEridge (facetT *)2L
    -
    -
    -/*============ -structures- ====================*/
    -
    -/*=========== -macros- =========================*/
    -
    -/*----------------------------------
    -
    -  FORALLfacet_( facetlist ) { ... }
    -    assign 'facet' to each facet in facetlist
    -
    -  notes:
    -    uses 'facetT *facet;'
    -    assumes last facet is a sentinel
    -
    -  see:
    -    FORALLfacets
    -*/
    -#define FORALLfacet_( facetlist ) if (facetlist ) for ( facet=( facetlist ); facet && facet->next; facet= facet->next )
    -
    -/*----------------------------------
    -
    -  FORALLnew_facets { ... }
    -    assign 'newfacet' to each facet in qh.newfacet_list
    -
    -  notes:
    -    uses 'facetT *newfacet;'
    -    at exit, newfacet==NULL
    -*/
    -#define FORALLnew_facets for ( newfacet=qh newfacet_list;newfacet && newfacet->next;newfacet=newfacet->next )
    -
    -/*----------------------------------
    -
    -  FORALLvertex_( vertexlist ) { ... }
    -    assign 'vertex' to each vertex in vertexlist
    -
    -  notes:
    -    uses 'vertexT *vertex;'
    -    at exit, vertex==NULL
    -*/
    -#define FORALLvertex_( vertexlist ) for (vertex=( vertexlist );vertex && vertex->next;vertex= vertex->next )
    -
    -/*----------------------------------
    -
    -  FORALLvisible_facets { ... }
    -    assign 'visible' to each visible facet in qh.visible_list
    -
    -  notes:
    -    uses 'vacetT *visible;'
    -    at exit, visible==NULL
    -*/
    -#define FORALLvisible_facets for (visible=qh visible_list; visible && visible->visible; visible= visible->next)
    -
    -/*----------------------------------
    -
    -  FORALLsame_( newfacet ) { ... }
    -    assign 'same' to each facet in newfacet->f.samecycle
    -
    -  notes:
    -    uses 'facetT *same;'
    -    stops when it returns to newfacet
    -*/
    -#define FORALLsame_(newfacet) for (same= newfacet->f.samecycle; same != newfacet; same= same->f.samecycle)
    -
    -/*----------------------------------
    -
    -  FORALLsame_cycle_( newfacet ) { ... }
    -    assign 'same' to each facet in newfacet->f.samecycle
    -
    -  notes:
    -    uses 'facetT *same;'
    -    at exit, same == NULL
    -*/
    -#define FORALLsame_cycle_(newfacet) \
    -     for (same= newfacet->f.samecycle; \
    -         same; same= (same == newfacet ?  NULL : same->f.samecycle))
    -
    -/*----------------------------------
    -
    -  FOREACHneighborA_( facet ) { ... }
    -    assign 'neighborA' to each neighbor in facet->neighbors
    -
    -  FOREACHneighborA_( vertex ) { ... }
    -    assign 'neighborA' to each neighbor in vertex->neighbors
    -
    -  declare:
    -    facetT *neighborA, **neighborAp;
    -
    -  see:
    -    FOREACHsetelement_
    -*/
    -#define FOREACHneighborA_(facet)  FOREACHsetelement_(facetT, facet->neighbors, neighborA)
    -
    -/*----------------------------------
    -
    -  FOREACHvisible_( facets ) { ... }
    -    assign 'visible' to each facet in facets
    -
    -  notes:
    -    uses 'facetT *facet, *facetp;'
    -    see FOREACHsetelement_
    -*/
    -#define FOREACHvisible_(facets) FOREACHsetelement_(facetT, facets, visible)
    -
    -/*----------------------------------
    -
    -  FOREACHnewfacet_( facets ) { ... }
    -    assign 'newfacet' to each facet in facets
    -
    -  notes:
    -    uses 'facetT *newfacet, *newfacetp;'
    -    see FOREACHsetelement_
    -*/
    -#define FOREACHnewfacet_(facets) FOREACHsetelement_(facetT, facets, newfacet)
    -
    -/*----------------------------------
    -
    -  FOREACHvertexA_( vertices ) { ... }
    -    assign 'vertexA' to each vertex in vertices
    -
    -  notes:
    -    uses 'vertexT *vertexA, *vertexAp;'
    -    see FOREACHsetelement_
    -*/
    -#define FOREACHvertexA_(vertices) FOREACHsetelement_(vertexT, vertices, vertexA)
    -
    -/*----------------------------------
    -
    -  FOREACHvertexreverse12_( vertices ) { ... }
    -    assign 'vertex' to each vertex in vertices
    -    reverse order of first two vertices
    -
    -  notes:
    -    uses 'vertexT *vertex, *vertexp;'
    -    see FOREACHsetelement_
    -*/
    -#define FOREACHvertexreverse12_(vertices) FOREACHsetelementreverse12_(vertexT, vertices, vertex)
    -
    -
    -/*=============== prototypes poly.c in alphabetical order ================*/
    -
    -void    qh_appendfacet(facetT *facet);
    -void    qh_appendvertex(vertexT *vertex);
    -void    qh_attachnewfacets(void);
    -boolT   qh_checkflipped(facetT *facet, realT *dist, boolT allerror);
    -void    qh_delfacet(facetT *facet);
    -void    qh_deletevisible(void /*qh visible_list, qh horizon_list*/);
    -setT   *qh_facetintersect(facetT *facetA, facetT *facetB, int *skipAp,int *skipBp, int extra);
    -int     qh_gethash(int hashsize, setT *set, int size, int firstindex, void *skipelem);
    -facetT *qh_makenewfacet(setT *vertices, boolT toporient, facetT *facet);
    -void    qh_makenewplanes(void /* newfacet_list */);
    -facetT *qh_makenew_nonsimplicial(facetT *visible, vertexT *apex, int *numnew);
    -facetT *qh_makenew_simplicial(facetT *visible, vertexT *apex, int *numnew);
    -void    qh_matchneighbor(facetT *newfacet, int newskip, int hashsize,
    -                          int *hashcount);
    -void    qh_matchnewfacets(void);
    -boolT   qh_matchvertices(int firstindex, setT *verticesA, int skipA,
    -                          setT *verticesB, int *skipB, boolT *same);
    -facetT *qh_newfacet(void);
    -ridgeT *qh_newridge(void);
    -int     qh_pointid(pointT *point);
    -void    qh_removefacet(facetT *facet);
    -void    qh_removevertex(vertexT *vertex);
    -void    qh_updatevertices(void);
    -
    -
    -/*========== -prototypes poly2.c in alphabetical order ===========*/
    -
    -void    qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash);
    -void    qh_check_bestdist(void);
    -void    qh_check_maxout(void);
    -void    qh_check_output(void);
    -void    qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2);
    -void    qh_check_points(void);
    -void    qh_checkconvex(facetT *facetlist, int fault);
    -void    qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp);
    -void    qh_checkflipped_all(facetT *facetlist);
    -void    qh_checkpolygon(facetT *facetlist);
    -void    qh_checkvertex(vertexT *vertex);
    -void    qh_clearcenters(qh_CENTER type);
    -void    qh_createsimplex(setT *vertices);
    -void    qh_delridge(ridgeT *ridge);
    -void    qh_delvertex(vertexT *vertex);
    -setT   *qh_facet3vertex(facetT *facet);
    -facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    -           realT *bestdist, boolT *isoutside);
    -facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart);
    -facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
    -                          int *numpart);
    -int     qh_findgood(facetT *facetlist, int goodhorizon);
    -void    qh_findgood_all(facetT *facetlist);
    -void    qh_furthestnext(void /* qh facet_list */);
    -void    qh_furthestout(facetT *facet);
    -void    qh_infiniteloop(facetT *facet);
    -void    qh_initbuild(void);
    -void    qh_initialhull(setT *vertices);
    -setT   *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints);
    -vertexT *qh_isvertex(pointT *point, setT *vertices);
    -vertexT *qh_makenewfacets(pointT *point /*horizon_list, visible_list*/);
    -void    qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount);
    -void    qh_nearcoplanar(void /* qh.facet_list */);
    -vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp);
    -int     qh_newhashtable(int newsize);
    -vertexT *qh_newvertex(pointT *point);
    -ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp);
    -void    qh_outcoplanar(void /* facet_list */);
    -pointT *qh_point(int id);
    -void    qh_point_add(setT *set, pointT *point, void *elem);
    -setT   *qh_pointfacet(void /*qh facet_list*/);
    -setT   *qh_pointvertex(void /*qh facet_list*/);
    -void    qh_prependfacet(facetT *facet, facetT **facetlist);
    -void    qh_printhashtable(FILE *fp);
    -void    qh_printlists(void);
    -void    qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/);
    -void    qh_setvoronoi_all(void);
    -void    qh_triangulate(void /*qh facet_list*/);
    -void    qh_triangulate_facet(facetT *facetA, vertexT **first_vertex);
    -void    qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB);
    -void    qh_triangulate_mirror(facetT *facetA, facetT *facetB);
    -void    qh_triangulate_null(facetT *facetA);
    -void    qh_vertexintersect(setT **vertexsetA,setT *vertexsetB);
    -setT   *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB);
    -void    qh_vertexneighbors(void /*qh facet_list*/);
    -boolT   qh_vertexsubset(setT *vertexsetA, setT *vertexsetB);
    -
    -
    -#endif /* qhDEFpoly */
    diff --git a/extern/qhull/poly2.c b/extern/qhull/poly2.c
    deleted file mode 100644
    index 317461fc54ef..000000000000
    --- a/extern/qhull/poly2.c
    +++ /dev/null
    @@ -1,3154 +0,0 @@
    -/*
      ---------------------------------
    -
    -   poly2.c
    -   implements polygons and simplices
    -
    -   see qh-poly.htm, poly.h and libqhull.h
    -
    -   frequently used code is in poly.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/poly2.c#5 $$Change: 1490 $
    -   $DateTime: 2012/02/19 20:27:01 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*======== functions in alphabetical order ==========*/
    -
    -/*---------------------------------
    -
    -  qh_addhash( newelem, hashtable, hashsize, hash )
    -    add newelem to linear hash table at hash if not already there
    -*/
    -void qh_addhash(void* newelem, setT *hashtable, int hashsize, int hash) {
    -  int scan;
    -  void *elem;
    -
    -  for (scan= (int)hash; (elem= SETelem_(hashtable, scan));
    -       scan= (++scan >= hashsize ? 0 : scan)) {
    -    if (elem == newelem)
    -      break;
    -  }
    -  /* loop terminates because qh_HASHfactor >= 1.1 by qh_initbuffers */
    -  if (!elem)
    -    SETelem_(hashtable, scan)= newelem;
    -} /* addhash */
    -
    -/*---------------------------------
    -
    -  qh_check_bestdist()
    -    check that all points are within max_outside of the nearest facet
    -    if qh.ONLYgood,
    -      ignores !good facets
    -
    -  see:
    -    qh_check_maxout(), qh_outerinner()
    -
    -  notes:
    -    only called from qh_check_points()
    -      seldom used since qh.MERGING is almost always set
    -    if notverified>0 at end of routine
    -      some points were well inside the hull.  If the hull contains
    -      a lens-shaped component, these points were not verified.  Use
    -      options 'Qi Tv' to verify all points.  (Exhaustive check also verifies)
    -
    -  design:
    -    determine facet for each point (if any)
    -    for each point
    -      start with the assigned facet or with the first facet
    -      find the best facet for the point and check all coplanar facets
    -      error if point is outside of facet
    -*/
    -void qh_check_bestdist(void) {
    -  boolT waserror= False, unassigned;
    -  facetT *facet, *bestfacet, *errfacet1= NULL, *errfacet2= NULL;
    -  facetT *facetlist;
    -  realT dist, maxoutside, maxdist= -REALmax;
    -  pointT *point;
    -  int numpart= 0, facet_i, facet_n, notgood= 0, notverified= 0;
    -  setT *facets;
    -
    -  trace1((qh ferr, 1020, "qh_check_bestdist: check points below nearest facet.  Facet_list f%d\n",
    -      qh facet_list->id));
    -  maxoutside= qh_maxouter();
    -  maxoutside += qh DISTround;
    -  /* one more qh.DISTround for check computation */
    -  trace1((qh ferr, 1021, "qh_check_bestdist: check that all points are within %2.2g of best facet\n", maxoutside));
    -  facets= qh_pointfacet(/*qh facet_list*/);
    -  if (!qh_QUICKhelp && qh PRINTprecision)
    -    qh_fprintf(qh ferr, 8091, "\n\
    -qhull output completed.  Verifying that %d points are\n\
    -below %2.2g of the nearest %sfacet.\n",
    -             qh_setsize(facets), maxoutside, (qh ONLYgood ?  "good " : ""));
    -  FOREACHfacet_i_(facets) {  /* for each point with facet assignment */
    -    if (facet)
    -      unassigned= False;
    -    else {
    -      unassigned= True;
    -      facet= qh facet_list;
    -    }
    -    point= qh_point(facet_i);
    -    if (point == qh GOODpointp)
    -      continue;
    -    qh_distplane(point, facet, &dist);
    -    numpart++;
    -    bestfacet= qh_findbesthorizon(!qh_IScheckmax, point, facet, qh_NOupper, &dist, &numpart);
    -    /* occurs after statistics reported */
    -    maximize_(maxdist, dist);
    -    if (dist > maxoutside) {
    -      if (qh ONLYgood && !bestfacet->good
    -          && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
    -               && dist > maxoutside))
    -        notgood++;
    -      else {
    -        waserror= True;
    -        qh_fprintf(qh ferr, 6109, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
    -                facet_i, bestfacet->id, dist, maxoutside);
    -        if (errfacet1 != bestfacet) {
    -          errfacet2= errfacet1;
    -          errfacet1= bestfacet;
    -        }
    -      }
    -    }else if (unassigned && dist < -qh MAXcoplanar)
    -      notverified++;
    -  }
    -  qh_settempfree(&facets);
    -  if (notverified && !qh DELAUNAY && !qh_QUICKhelp && qh PRINTprecision)
    -    qh_fprintf(qh ferr, 8092, "\n%d points were well inside the hull.  If the hull contains\n\
    -a lens-shaped component, these points were not verified.  Use\n\
    -options 'Qci Tv' to verify all points.\n", notverified);
    -  if (maxdist > qh outside_err) {
    -    qh_fprintf(qh ferr, 6110, "qhull precision error (qh_check_bestdist): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
    -              maxdist, qh outside_err);
    -    qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
    -  }else if (waserror && qh outside_err > REALmax/2)
    -    qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
    -  /* else if waserror, the error was logged to qh.ferr but does not effect the output */
    -  trace0((qh ferr, 20, "qh_check_bestdist: max distance outside %2.2g\n", maxdist));
    -} /* check_bestdist */
    -
    -/*---------------------------------
    -
    -  qh_check_maxout()
    -    updates qh.max_outside by checking all points against bestfacet
    -    if qh.ONLYgood, ignores !good facets
    -
    -  returns:
    -    updates facet->maxoutside via qh_findbesthorizon()
    -    sets qh.maxoutdone
    -    if printing qh.min_vertex (qh_outerinner),
    -      it is updated to the current vertices
    -    removes inside/coplanar points from coplanarset as needed
    -
    -  notes:
    -    defines coplanar as min_vertex instead of MAXcoplanar
    -    may not need to check near-inside points because of qh.MAXcoplanar
    -      and qh.KEEPnearinside (before it was -DISTround)
    -
    -  see also:
    -    qh_check_bestdist()
    -
    -  design:
    -    if qh.min_vertex is needed
    -      for all neighbors of all vertices
    -        test distance from vertex to neighbor
    -    determine facet for each point (if any)
    -    for each point with an assigned facet
    -      find the best facet for the point and check all coplanar facets
    -        (updates outer planes)
    -    remove near-inside points from coplanar sets
    -*/
    -#ifndef qh_NOmerge
    -void qh_check_maxout(void) {
    -  facetT *facet, *bestfacet, *neighbor, **neighborp, *facetlist;
    -  realT dist, maxoutside, minvertex, old_maxoutside;
    -  pointT *point;
    -  int numpart= 0, facet_i, facet_n, notgood= 0;
    -  setT *facets, *vertices;
    -  vertexT *vertex;
    -
    -  trace1((qh ferr, 1022, "qh_check_maxout: check and update maxoutside for each facet.\n"));
    -  maxoutside= minvertex= 0;
    -  if (qh VERTEXneighbors
    -  && (qh PRINTsummary || qh KEEPinside || qh KEEPcoplanar
    -        || qh TRACElevel || qh PRINTstatistics
    -        || qh PRINTout[0] == qh_PRINTsummary || qh PRINTout[0] == qh_PRINTnone)) {
    -    trace1((qh ferr, 1023, "qh_check_maxout: determine actual maxoutside and minvertex\n"));
    -    vertices= qh_pointvertex(/*qh facet_list*/);
    -    FORALLvertices {
    -      FOREACHneighbor_(vertex) {
    -        zinc_(Zdistvertex);  /* distance also computed by main loop below */
    -        qh_distplane(vertex->point, neighbor, &dist);
    -        minimize_(minvertex, dist);
    -        if (-dist > qh TRACEdist || dist > qh TRACEdist
    -        || neighbor == qh tracefacet || vertex == qh tracevertex)
    -          qh_fprintf(qh ferr, 8093, "qh_check_maxout: p%d(v%d) is %.2g from f%d\n",
    -                    qh_pointid(vertex->point), vertex->id, dist, neighbor->id);
    -      }
    -    }
    -    if (qh MERGING) {
    -      wmin_(Wminvertex, qh min_vertex);
    -    }
    -    qh min_vertex= minvertex;
    -    qh_settempfree(&vertices);
    -  }
    -  facets= qh_pointfacet(/*qh facet_list*/);
    -  do {
    -    old_maxoutside= fmax_(qh max_outside, maxoutside);
    -    FOREACHfacet_i_(facets) {     /* for each point with facet assignment */
    -      if (facet) {
    -        point= qh_point(facet_i);
    -        if (point == qh GOODpointp)
    -          continue;
    -        zzinc_(Ztotcheck);
    -        qh_distplane(point, facet, &dist);
    -        numpart++;
    -        bestfacet= qh_findbesthorizon(qh_IScheckmax, point, facet, !qh_NOupper, &dist, &numpart);
    -        if (bestfacet && dist > maxoutside) {
    -          if (qh ONLYgood && !bestfacet->good
    -          && !((bestfacet= qh_findgooddist(point, bestfacet, &dist, &facetlist))
    -               && dist > maxoutside))
    -            notgood++;
    -          else
    -            maxoutside= dist;
    -        }
    -        if (dist > qh TRACEdist || (bestfacet && bestfacet == qh tracefacet))
    -          qh_fprintf(qh ferr, 8094, "qh_check_maxout: p%d is %.2g above f%d\n",
    -                     qh_pointid(point), dist, bestfacet->id);
    -      }
    -    }
    -  }while
    -    (maxoutside > 2*old_maxoutside);
    -    /* if qh.maxoutside increases substantially, qh_SEARCHdist is not valid
    -          e.g., RBOX 5000 s Z1 G1e-13 t1001200614 | qhull */
    -  zzadd_(Zcheckpart, numpart);
    -  qh_settempfree(&facets);
    -  wval_(Wmaxout)= maxoutside - qh max_outside;
    -  wmax_(Wmaxoutside, qh max_outside);
    -  qh max_outside= maxoutside;
    -  qh_nearcoplanar(/*qh.facet_list*/);
    -  qh maxoutdone= True;
    -  trace1((qh ferr, 1024, "qh_check_maxout: maxoutside %2.2g, min_vertex %2.2g, outside of not good %d\n",
    -       maxoutside, qh min_vertex, notgood));
    -} /* check_maxout */
    -#else /* qh_NOmerge */
    -void qh_check_maxout(void) {
    -}
    -#endif
    -
    -/*---------------------------------
    -
    -  qh_check_output()
    -    performs the checks at the end of qhull algorithm
    -    Maybe called after voronoi output.  Will recompute otherwise centrums are Voronoi centers instead
    -*/
    -void qh_check_output(void) {
    -  int i;
    -
    -  if (qh STOPcone)
    -    return;
    -  if (qh VERIFYoutput | qh IStracing | qh CHECKfrequently) {
    -    qh_checkpolygon(qh facet_list);
    -    qh_checkflipped_all(qh facet_list);
    -    qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    -  }else if (!qh MERGING && qh_newstats(qhstat precision, &i)) {
    -    qh_checkflipped_all(qh facet_list);
    -    qh_checkconvex(qh facet_list, qh_ALGORITHMfault);
    -  }
    -} /* check_output */
    -
    -
    -
    -/*---------------------------------
    -
    -  qh_check_point( point, facet, maxoutside, maxdist, errfacet1, errfacet2 )
    -    check that point is less than maxoutside from facet
    -*/
    -void qh_check_point(pointT *point, facetT *facet, realT *maxoutside, realT *maxdist, facetT **errfacet1, facetT **errfacet2) {
    -  realT dist;
    -
    -  /* occurs after statistics reported */
    -  qh_distplane(point, facet, &dist);
    -  if (dist > *maxoutside) {
    -    if (*errfacet1 != facet) {
    -      *errfacet2= *errfacet1;
    -      *errfacet1= facet;
    -    }
    -    qh_fprintf(qh ferr, 6111, "qhull precision error: point p%d is outside facet f%d, distance= %6.8g maxoutside= %6.8g\n",
    -              qh_pointid(point), facet->id, dist, *maxoutside);
    -  }
    -  maximize_(*maxdist, dist);
    -} /* qh_check_point */
    -
    -
    -/*---------------------------------
    -
    -  qh_check_points()
    -    checks that all points are inside all facets
    -
    -  notes:
    -    if many points and qh_check_maxout not called (i.e., !qh.MERGING),
    -       calls qh_findbesthorizon (seldom done).
    -    ignores flipped facets
    -    maxoutside includes 2 qh.DISTrounds
    -      one qh.DISTround for the computed distances in qh_check_points
    -    qh_printafacet and qh_printsummary needs only one qh.DISTround
    -    the computation for qh.VERIFYdirect does not account for qh.other_points
    -
    -  design:
    -    if many points
    -      use qh_check_bestdist()
    -    else
    -      for all facets
    -        for all points
    -          check that point is inside facet
    -*/
    -void qh_check_points(void) {
    -  facetT *facet, *errfacet1= NULL, *errfacet2= NULL;
    -  realT total, maxoutside, maxdist= -REALmax;
    -  pointT *point, **pointp, *pointtemp;
    -  boolT testouter;
    -
    -  maxoutside= qh_maxouter();
    -  maxoutside += qh DISTround;
    -  /* one more qh.DISTround for check computation */
    -  trace1((qh ferr, 1025, "qh_check_points: check all points below %2.2g of all facet planes\n",
    -          maxoutside));
    -  if (qh num_good)   /* miss counts other_points and !good facets */
    -     total= (float)qh num_good * (float)qh num_points;
    -  else
    -     total= (float)qh num_facets * (float)qh num_points;
    -  if (total >= qh_VERIFYdirect && !qh maxoutdone) {
    -    if (!qh_QUICKhelp && qh SKIPcheckmax && qh MERGING)
    -      qh_fprintf(qh ferr, 7075, "qhull input warning: merging without checking outer planes('Q5' or 'Po').\n\
    -Verify may report that a point is outside of a facet.\n");
    -    qh_check_bestdist();
    -  }else {
    -    if (qh_MAXoutside && qh maxoutdone)
    -      testouter= True;
    -    else
    -      testouter= False;
    -    if (!qh_QUICKhelp) {
    -      if (qh MERGEexact)
    -        qh_fprintf(qh ferr, 7076, "qhull input warning: exact merge ('Qx').  Verify may report that a point\n\
    -is outside of a facet.  See qh-optq.htm#Qx\n");
    -      else if (qh SKIPcheckmax || qh NOnearinside)
    -        qh_fprintf(qh ferr, 7077, "qhull input warning: no outer plane check ('Q5') or no processing of\n\
    -near-inside points ('Q8').  Verify may report that a point is outside\n\
    -of a facet.\n");
    -    }
    -    if (qh PRINTprecision) {
    -      if (testouter)
    -        qh_fprintf(qh ferr, 8098, "\n\
    -Output completed.  Verifying that all points are below outer planes of\n\
    -all %sfacets.  Will make %2.0f distance computations.\n",
    -              (qh ONLYgood ?  "good " : ""), total);
    -      else
    -        qh_fprintf(qh ferr, 8099, "\n\
    -Output completed.  Verifying that all points are below %2.2g of\n\
    -all %sfacets.  Will make %2.0f distance computations.\n",
    -              maxoutside, (qh ONLYgood ?  "good " : ""), total);
    -    }
    -    FORALLfacets {
    -      if (!facet->good && qh ONLYgood)
    -        continue;
    -      if (facet->flipped)
    -        continue;
    -      if (!facet->normal) {
    -        qh_fprintf(qh ferr, 7061, "qhull warning (qh_check_points): missing normal for facet f%d\n", facet->id);
    -        continue;
    -      }
    -      if (testouter) {
    -#if qh_MAXoutside
    -        maxoutside= facet->maxoutside + 2* qh DISTround;
    -        /* one DISTround to actual point and another to computed point */
    -#endif
    -      }
    -      FORALLpoints {
    -        if (point != qh GOODpointp)
    -          qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
    -      }
    -      FOREACHpoint_(qh other_points) {
    -        if (point != qh GOODpointp)
    -          qh_check_point(point, facet, &maxoutside, &maxdist, &errfacet1, &errfacet2);
    -      }
    -    }
    -    if (maxdist > qh outside_err) {
    -      qh_fprintf(qh ferr, 6112, "qhull precision error (qh_check_points): a coplanar point is %6.2g from convex hull.  The maximum value(qh.outside_err) is %6.2g\n",
    -                maxdist, qh outside_err );
    -      qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
    -    }else if (errfacet1 && qh outside_err > REALmax/2)
    -        qh_errexit2( qh_ERRprec, errfacet1, errfacet2 );
    -    /* else if errfacet1, the error was logged to qh.ferr but does not effect the output */
    -    trace0((qh ferr, 21, "qh_check_points: max distance outside %2.2g\n", maxdist));
    -  }
    -} /* check_points */
    -
    -
    -/*---------------------------------
    -
    -  qh_checkconvex( facetlist, fault )
    -    check that each ridge in facetlist is convex
    -    fault = qh_DATAfault if reporting errors
    -          = qh_ALGORITHMfault otherwise
    -
    -  returns:
    -    counts Zconcaveridges and Zcoplanarridges
    -    errors if concaveridge or if merging an coplanar ridge
    -
    -  note:
    -    if not merging,
    -      tests vertices for neighboring simplicial facets
    -    else if ZEROcentrum,
    -      tests vertices for neighboring simplicial   facets
    -    else
    -      tests centrums of neighboring facets
    -
    -  design:
    -    for all facets
    -      report flipped facets
    -      if ZEROcentrum and simplicial neighbors
    -        test vertices for neighboring simplicial facets
    -      else
    -        test centrum against all neighbors
    -*/
    -void qh_checkconvex(facetT *facetlist, int fault) {
    -  facetT *facet, *neighbor, **neighborp, *errfacet1=NULL, *errfacet2=NULL;
    -  vertexT *vertex;
    -  realT dist;
    -  pointT *centrum;
    -  boolT waserror= False, centrum_warning= False, tempcentrum= False, allsimplicial;
    -  int neighbor_i;
    -
    -  trace1((qh ferr, 1026, "qh_checkconvex: check all ridges are convex\n"));
    -  if (!qh RERUN) {
    -    zzval_(Zconcaveridges)= 0;
    -    zzval_(Zcoplanarridges)= 0;
    -  }
    -  FORALLfacet_(facetlist) {
    -    if (facet->flipped) {
    -      qh_precision("flipped facet");
    -      qh_fprintf(qh ferr, 6113, "qhull precision error: f%d is flipped(interior point is outside)\n",
    -               facet->id);
    -      errfacet1= facet;
    -      waserror= True;
    -      continue;
    -    }
    -    if (qh MERGING && (!qh ZEROcentrum || !facet->simplicial || facet->tricoplanar))
    -      allsimplicial= False;
    -    else {
    -      allsimplicial= True;
    -      neighbor_i= 0;
    -      FOREACHneighbor_(facet) {
    -        vertex= SETelemt_(facet->vertices, neighbor_i++, vertexT);
    -        if (!neighbor->simplicial || neighbor->tricoplanar) {
    -          allsimplicial= False;
    -          continue;
    -        }
    -        qh_distplane(vertex->point, neighbor, &dist);
    -        if (dist > -qh DISTround) {
    -          if (fault == qh_DATAfault) {
    -            qh_precision("coplanar or concave ridge");
    -            qh_fprintf(qh ferr, 6114, "qhull precision error: initial simplex is not convex. Distance=%.2g\n", dist);
    -            qh_errexit(qh_ERRsingular, NULL, NULL);
    -          }
    -          if (dist > qh DISTround) {
    -            zzinc_(Zconcaveridges);
    -            qh_precision("concave ridge");
    -            qh_fprintf(qh ferr, 6115, "qhull precision error: f%d is concave to f%d, since p%d(v%d) is %6.4g above\n",
    -              facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
    -            errfacet1= facet;
    -            errfacet2= neighbor;
    -            waserror= True;
    -          }else if (qh ZEROcentrum) {
    -            if (dist > 0) {     /* qh_checkzero checks that dist < - qh DISTround */
    -              zzinc_(Zcoplanarridges);
    -              qh_precision("coplanar ridge");
    -              qh_fprintf(qh ferr, 6116, "qhull precision error: f%d is clearly not convex to f%d, since p%d(v%d) is %6.4g above\n",
    -                facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist);
    -              errfacet1= facet;
    -              errfacet2= neighbor;
    -              waserror= True;
    -            }
    -          }else {
    -            zzinc_(Zcoplanarridges);
    -            qh_precision("coplanar ridge");
    -            trace0((qh ferr, 22, "qhull precision error: f%d may be coplanar to f%d, since p%d(v%d) is within %6.4g during p%d\n",
    -              facet->id, neighbor->id, qh_pointid(vertex->point), vertex->id, dist, qh furthest_id));
    -          }
    -        }
    -      }
    -    }
    -    if (!allsimplicial) {
    -      if (qh CENTERtype == qh_AScentrum) {
    -        if (!facet->center)
    -          facet->center= qh_getcentrum(facet);
    -        centrum= facet->center;
    -      }else {
    -        if (!centrum_warning && (!facet->simplicial || facet->tricoplanar)) {
    -           centrum_warning= True;
    -           qh_fprintf(qh ferr, 7062, "qhull warning: recomputing centrums for convexity test.  This may lead to false, precision errors.\n");
    -        }
    -        centrum= qh_getcentrum(facet);
    -        tempcentrum= True;
    -      }
    -      FOREACHneighbor_(facet) {
    -        if (qh ZEROcentrum && facet->simplicial && neighbor->simplicial)
    -          continue;
    -        if (facet->tricoplanar || neighbor->tricoplanar)
    -          continue;
    -        zzinc_(Zdistconvex);
    -        qh_distplane(centrum, neighbor, &dist);
    -        if (dist > qh DISTround) {
    -          zzinc_(Zconcaveridges);
    -          qh_precision("concave ridge");
    -          qh_fprintf(qh ferr, 6117, "qhull precision error: f%d is concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
    -            facet->id, neighbor->id, facet->id, dist, neighbor->id);
    -          errfacet1= facet;
    -          errfacet2= neighbor;
    -          waserror= True;
    -        }else if (dist >= 0.0) {   /* if arithmetic always rounds the same,
    -                                     can test against centrum radius instead */
    -          zzinc_(Zcoplanarridges);
    -          qh_precision("coplanar ridge");
    -          qh_fprintf(qh ferr, 6118, "qhull precision error: f%d is coplanar or concave to f%d.  Centrum of f%d is %6.4g above f%d\n",
    -            facet->id, neighbor->id, facet->id, dist, neighbor->id);
    -          errfacet1= facet;
    -          errfacet2= neighbor;
    -          waserror= True;
    -        }
    -      }
    -      if (tempcentrum)
    -        qh_memfree(centrum, qh normal_size);
    -    }
    -  }
    -  if (waserror && !qh FORCEoutput)
    -    qh_errexit2 (qh_ERRprec, errfacet1, errfacet2);
    -} /* checkconvex */
    -
    -
    -/*---------------------------------
    -
    -  qh_checkfacet( facet, newmerge, waserror )
    -    checks for consistency errors in facet
    -    newmerge set if from merge.c
    -
    -  returns:
    -    sets waserror if any error occurs
    -
    -  checks:
    -    vertex ids are inverse sorted
    -    unless newmerge, at least hull_dim neighbors and vertices (exactly if simplicial)
    -    if non-simplicial, at least as many ridges as neighbors
    -    neighbors are not duplicated
    -    ridges are not duplicated
    -    in 3-d, ridges=verticies
    -    (qh.hull_dim-1) ridge vertices
    -    neighbors are reciprocated
    -    ridge neighbors are facet neighbors and a ridge for every neighbor
    -    simplicial neighbors match facetintersect
    -    vertex intersection matches vertices of common ridges
    -    vertex neighbors and facet vertices agree
    -    all ridges have distinct vertex sets
    -
    -  notes:
    -    uses neighbor->seen
    -
    -  design:
    -    check sets
    -    check vertices
    -    check sizes of neighbors and vertices
    -    check for qh_MERGEridge and qh_DUPLICATEridge flags
    -    check neighbor set
    -    check ridge set
    -    check ridges, neighbors, and vertices
    -*/
    -void qh_checkfacet(facetT *facet, boolT newmerge, boolT *waserrorp) {
    -  facetT *neighbor, **neighborp, *errother=NULL;
    -  ridgeT *ridge, **ridgep, *errridge= NULL, *ridge2;
    -  vertexT *vertex, **vertexp;
    -  unsigned previousid= INT_MAX;
    -  int numneighbors, numvertices, numridges=0, numRvertices=0;
    -  boolT waserror= False;
    -  int skipA, skipB, ridge_i, ridge_n, i;
    -  setT *intersection;
    -
    -  if (facet->visible) {
    -    qh_fprintf(qh ferr, 6119, "qhull internal error (qh_checkfacet): facet f%d is on the visible_list\n",
    -      facet->id);
    -    qh_errexit(qh_ERRqhull, facet, NULL);
    -  }
    -  if (!facet->normal) {
    -    qh_fprintf(qh ferr, 6120, "qhull internal error (qh_checkfacet): facet f%d does not have  a normal\n",
    -      facet->id);
    -    waserror= True;
    -  }
    -  qh_setcheck(facet->vertices, "vertices for f", facet->id);
    -  qh_setcheck(facet->ridges, "ridges for f", facet->id);
    -  qh_setcheck(facet->outsideset, "outsideset for f", facet->id);
    -  qh_setcheck(facet->coplanarset, "coplanarset for f", facet->id);
    -  qh_setcheck(facet->neighbors, "neighbors for f", facet->id);
    -  FOREACHvertex_(facet->vertices) {
    -    if (vertex->deleted) {
    -      qh_fprintf(qh ferr, 6121, "qhull internal error (qh_checkfacet): deleted vertex v%d in f%d\n", vertex->id, facet->id);
    -      qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
    -      waserror= True;
    -    }
    -    if (vertex->id >= previousid) {
    -      qh_fprintf(qh ferr, 6122, "qhull internal error (qh_checkfacet): vertices of f%d are not in descending id order at v%d\n", facet->id, vertex->id);
    -      waserror= True;
    -      break;
    -    }
    -    previousid= vertex->id;
    -  }
    -  numneighbors= qh_setsize(facet->neighbors);
    -  numvertices= qh_setsize(facet->vertices);
    -  numridges= qh_setsize(facet->ridges);
    -  if (facet->simplicial) {
    -    if (numvertices+numneighbors != 2*qh hull_dim
    -    && !facet->degenerate && !facet->redundant) {
    -      qh_fprintf(qh ferr, 6123, "qhull internal error (qh_checkfacet): for simplicial facet f%d, #vertices %d + #neighbors %d != 2*qh hull_dim\n",
    -                facet->id, numvertices, numneighbors);
    -      qh_setprint(qh ferr, "", facet->neighbors);
    -      waserror= True;
    -    }
    -  }else { /* non-simplicial */
    -    if (!newmerge
    -    &&(numvertices < qh hull_dim || numneighbors < qh hull_dim)
    -    && !facet->degenerate && !facet->redundant) {
    -      qh_fprintf(qh ferr, 6124, "qhull internal error (qh_checkfacet): for facet f%d, #vertices %d or #neighbors %d < qh hull_dim\n",
    -         facet->id, numvertices, numneighbors);
    -       waserror= True;
    -    }
    -    /* in 3-d, can get a vertex twice in an edge list, e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv TP624 TW1e-13 T4 */
    -    if (numridges < numneighbors
    -    ||(qh hull_dim == 3 && numvertices > numridges && !qh NEWfacets)
    -    ||(qh hull_dim == 2 && numridges + numvertices + numneighbors != 6)) {
    -      if (!facet->degenerate && !facet->redundant) {
    -        qh_fprintf(qh ferr, 6125, "qhull internal error (qh_checkfacet): for facet f%d, #ridges %d < #neighbors %d or(3-d) > #vertices %d or(2-d) not all 2\n",
    -            facet->id, numridges, numneighbors, numvertices);
    -        waserror= True;
    -      }
    -    }
    -  }
    -  FOREACHneighbor_(facet) {
    -    if (neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge) {
    -      qh_fprintf(qh ferr, 6126, "qhull internal error (qh_checkfacet): facet f%d still has a MERGE or DUP neighbor\n", facet->id);
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -    neighbor->seen= True;
    -  }
    -  FOREACHneighbor_(facet) {
    -    if (!qh_setin(neighbor->neighbors, facet)) {
    -      qh_fprintf(qh ferr, 6127, "qhull internal error (qh_checkfacet): facet f%d has neighbor f%d, but f%d does not have neighbor f%d\n",
    -              facet->id, neighbor->id, neighbor->id, facet->id);
    -      errother= neighbor;
    -      waserror= True;
    -    }
    -    if (!neighbor->seen) {
    -      qh_fprintf(qh ferr, 6128, "qhull internal error (qh_checkfacet): facet f%d has a duplicate neighbor f%d\n",
    -              facet->id, neighbor->id);
    -      errother= neighbor;
    -      waserror= True;
    -    }
    -    neighbor->seen= False;
    -  }
    -  FOREACHridge_(facet->ridges) {
    -    qh_setcheck(ridge->vertices, "vertices for r", ridge->id);
    -    ridge->seen= False;
    -  }
    -  FOREACHridge_(facet->ridges) {
    -    if (ridge->seen) {
    -      qh_fprintf(qh ferr, 6129, "qhull internal error (qh_checkfacet): facet f%d has a duplicate ridge r%d\n",
    -              facet->id, ridge->id);
    -      errridge= ridge;
    -      waserror= True;
    -    }
    -    ridge->seen= True;
    -    numRvertices= qh_setsize(ridge->vertices);
    -    if (numRvertices != qh hull_dim - 1) {
    -      qh_fprintf(qh ferr, 6130, "qhull internal error (qh_checkfacet): ridge between f%d and f%d has %d vertices\n",
    -                ridge->top->id, ridge->bottom->id, numRvertices);
    -      errridge= ridge;
    -      waserror= True;
    -    }
    -    neighbor= otherfacet_(ridge, facet);
    -    neighbor->seen= True;
    -    if (!qh_setin(facet->neighbors, neighbor)) {
    -      qh_fprintf(qh ferr, 6131, "qhull internal error (qh_checkfacet): for facet f%d, neighbor f%d of ridge r%d not in facet\n",
    -           facet->id, neighbor->id, ridge->id);
    -      errridge= ridge;
    -      waserror= True;
    -    }
    -  }
    -  if (!facet->simplicial) {
    -    FOREACHneighbor_(facet) {
    -      if (!neighbor->seen) {
    -        qh_fprintf(qh ferr, 6132, "qhull internal error (qh_checkfacet): facet f%d does not have a ridge for neighbor f%d\n",
    -              facet->id, neighbor->id);
    -        errother= neighbor;
    -        waserror= True;
    -      }
    -      intersection= qh_vertexintersect_new(facet->vertices, neighbor->vertices);
    -      qh_settemppush(intersection);
    -      FOREACHvertex_(facet->vertices) {
    -        vertex->seen= False;
    -        vertex->seen2= False;
    -      }
    -      FOREACHvertex_(intersection)
    -        vertex->seen= True;
    -      FOREACHridge_(facet->ridges) {
    -        if (neighbor != otherfacet_(ridge, facet))
    -            continue;
    -        FOREACHvertex_(ridge->vertices) {
    -          if (!vertex->seen) {
    -            qh_fprintf(qh ferr, 6133, "qhull internal error (qh_checkfacet): vertex v%d in r%d not in f%d intersect f%d\n",
    -                  vertex->id, ridge->id, facet->id, neighbor->id);
    -            qh_errexit(qh_ERRqhull, facet, ridge);
    -          }
    -          vertex->seen2= True;
    -        }
    -      }
    -      if (!newmerge) {
    -        FOREACHvertex_(intersection) {
    -          if (!vertex->seen2) {
    -            if (qh IStracing >=3 || !qh MERGING) {
    -              qh_fprintf(qh ferr, 6134, "qhull precision error (qh_checkfacet): vertex v%d in f%d intersect f%d but\n\
    - not in a ridge.  This is ok under merging.  Last point was p%d\n",
    -                     vertex->id, facet->id, neighbor->id, qh furthest_id);
    -              if (!qh FORCEoutput && !qh MERGING) {
    -                qh_errprint("ERRONEOUS", facet, neighbor, NULL, vertex);
    -                if (!qh MERGING)
    -                  qh_errexit(qh_ERRqhull, NULL, NULL);
    -              }
    -            }
    -          }
    -        }
    -      }
    -      qh_settempfree(&intersection);
    -    }
    -  }else { /* simplicial */
    -    FOREACHneighbor_(facet) {
    -      if (neighbor->simplicial) {
    -        skipA= SETindex_(facet->neighbors, neighbor);
    -        skipB= qh_setindex(neighbor->neighbors, facet);
    -        if (!qh_setequal_skip(facet->vertices, skipA, neighbor->vertices, skipB)) {
    -          qh_fprintf(qh ferr, 6135, "qhull internal error (qh_checkfacet): facet f%d skip %d and neighbor f%d skip %d do not match \n",
    -                   facet->id, skipA, neighbor->id, skipB);
    -          errother= neighbor;
    -          waserror= True;
    -        }
    -      }
    -    }
    -  }
    -  if (qh hull_dim < 5 && (qh IStracing > 2 || qh CHECKfrequently)) {
    -    FOREACHridge_i_(facet->ridges) {           /* expensive */
    -      for (i=ridge_i+1; i < ridge_n; i++) {
    -        ridge2= SETelemt_(facet->ridges, i, ridgeT);
    -        if (qh_setequal(ridge->vertices, ridge2->vertices)) {
    -          qh_fprintf(qh ferr, 6227, "Qhull internal error (qh_checkfacet): ridges r%d and r%d have the same vertices\n",
    -                  ridge->id, ridge2->id);
    -          errridge= ridge;
    -          waserror= True;
    -        }
    -      }
    -    }
    -  }
    -  if (waserror) {
    -    qh_errprint("ERRONEOUS", facet, errother, errridge, NULL);
    -    *waserrorp= True;
    -  }
    -} /* checkfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_checkflipped_all( facetlist )
    -    checks orientation of facets in list against interior point
    -*/
    -void qh_checkflipped_all(facetT *facetlist) {
    -  facetT *facet;
    -  boolT waserror= False;
    -  realT dist;
    -
    -  if (facetlist == qh facet_list)
    -    zzval_(Zflippedfacets)= 0;
    -  FORALLfacet_(facetlist) {
    -    if (facet->normal && !qh_checkflipped(facet, &dist, !qh_ALL)) {
    -      qh_fprintf(qh ferr, 6136, "qhull precision error: facet f%d is flipped, distance= %6.12g\n",
    -              facet->id, dist);
    -      if (!qh FORCEoutput) {
    -        qh_errprint("ERRONEOUS", facet, NULL, NULL, NULL);
    -        waserror= True;
    -      }
    -    }
    -  }
    -  if (waserror) {
    -    qh_fprintf(qh ferr, 8101, "\n\
    -A flipped facet occurs when its distance to the interior point is\n\
    -greater than %2.2g, the maximum roundoff error.\n", -qh DISTround);
    -    qh_errexit(qh_ERRprec, NULL, NULL);
    -  }
    -} /* checkflipped_all */
    -
    -/*---------------------------------
    -
    -  qh_checkpolygon( facetlist )
    -    checks the correctness of the structure
    -
    -  notes:
    -    call with either qh.facet_list or qh.newfacet_list
    -    checks num_facets and num_vertices if qh.facet_list
    -
    -  design:
    -    for each facet
    -      checks facet and outside set
    -    initializes vertexlist
    -    for each facet
    -      checks vertex set
    -    if checking all facets(qh.facetlist)
    -      check facet count
    -      if qh.VERTEXneighbors
    -        check vertex neighbors and count
    -      check vertex count
    -*/
    -void qh_checkpolygon(facetT *facetlist) {
    -  facetT *facet;
    -  vertexT *vertex, **vertexp, *vertexlist;
    -  int numfacets= 0, numvertices= 0, numridges= 0;
    -  int totvneighbors= 0, totvertices= 0;
    -  boolT waserror= False, nextseen= False, visibleseen= False;
    -
    -  trace1((qh ferr, 1027, "qh_checkpolygon: check all facets from f%d\n", facetlist->id));
    -  if (facetlist != qh facet_list || qh ONLYgood)
    -    nextseen= True;
    -  FORALLfacet_(facetlist) {
    -    if (facet == qh visible_list)
    -      visibleseen= True;
    -    if (!facet->visible) {
    -      if (!nextseen) {
    -        if (facet == qh facet_next)
    -          nextseen= True;
    -        else if (qh_setsize(facet->outsideset)) {
    -          if (!qh NARROWhull
    -#if !qh_COMPUTEfurthest
    -               || facet->furthestdist >= qh MINoutside
    -#endif
    -                        ) {
    -            qh_fprintf(qh ferr, 6137, "qhull internal error (qh_checkpolygon): f%d has outside points before qh facet_next\n",
    -                     facet->id);
    -            qh_errexit(qh_ERRqhull, facet, NULL);
    -          }
    -        }
    -      }
    -      numfacets++;
    -      qh_checkfacet(facet, False, &waserror);
    -    }
    -  }
    -  if (qh visible_list && !visibleseen && facetlist == qh facet_list) {
    -    qh_fprintf(qh ferr, 6138, "qhull internal error (qh_checkpolygon): visible list f%d no longer on facet list\n", qh visible_list->id);
    -    qh_printlists();
    -    qh_errexit(qh_ERRqhull, qh visible_list, NULL);
    -  }
    -  if (facetlist == qh facet_list)
    -    vertexlist= qh vertex_list;
    -  else if (facetlist == qh newfacet_list)
    -    vertexlist= qh newvertex_list;
    -  else
    -    vertexlist= NULL;
    -  FORALLvertex_(vertexlist) {
    -    vertex->seen= False;
    -    vertex->visitid= 0;
    -  }
    -  FORALLfacet_(facetlist) {
    -    if (facet->visible)
    -      continue;
    -    if (facet->simplicial)
    -      numridges += qh hull_dim;
    -    else
    -      numridges += qh_setsize(facet->ridges);
    -    FOREACHvertex_(facet->vertices) {
    -      vertex->visitid++;
    -      if (!vertex->seen) {
    -        vertex->seen= True;
    -        numvertices++;
    -        if (qh_pointid(vertex->point) == -1) {
    -          qh_fprintf(qh ferr, 6139, "qhull internal error (qh_checkpolygon): unknown point %p for vertex v%d first_point %p\n",
    -                   vertex->point, vertex->id, qh first_point);
    -          waserror= True;
    -        }
    -      }
    -    }
    -  }
    -  qh vertex_visit += (unsigned int)numfacets;
    -  if (facetlist == qh facet_list) {
    -    if (numfacets != qh num_facets - qh num_visible) {
    -      qh_fprintf(qh ferr, 6140, "qhull internal error (qh_checkpolygon): actual number of facets is %d, cumulative facet count is %d - %d visible facets\n",
    -              numfacets, qh num_facets, qh num_visible);
    -      waserror= True;
    -    }
    -    qh vertex_visit++;
    -    if (qh VERTEXneighbors) {
    -      FORALLvertices {
    -        qh_setcheck(vertex->neighbors, "neighbors for v", vertex->id);
    -        if (vertex->deleted)
    -          continue;
    -        totvneighbors += qh_setsize(vertex->neighbors);
    -      }
    -      FORALLfacet_(facetlist)
    -        totvertices += qh_setsize(facet->vertices);
    -      if (totvneighbors != totvertices) {
    -        qh_fprintf(qh ferr, 6141, "qhull internal error (qh_checkpolygon): vertex neighbors inconsistent.  Totvneighbors %d, totvertices %d\n",
    -                totvneighbors, totvertices);
    -        waserror= True;
    -      }
    -    }
    -    if (numvertices != qh num_vertices - qh_setsize(qh del_vertices)) {
    -      qh_fprintf(qh ferr, 6142, "qhull internal error (qh_checkpolygon): actual number of vertices is %d, cumulative vertex count is %d\n",
    -              numvertices, qh num_vertices - qh_setsize(qh del_vertices));
    -      waserror= True;
    -    }
    -    if (qh hull_dim == 2 && numvertices != numfacets) {
    -      qh_fprintf(qh ferr, 6143, "qhull internal error (qh_checkpolygon): #vertices %d != #facets %d\n",
    -        numvertices, numfacets);
    -      waserror= True;
    -    }
    -    if (qh hull_dim == 3 && numvertices + numfacets - numridges/2 != 2) {
    -      qh_fprintf(qh ferr, 7063, "qhull warning: #vertices %d + #facets %d - #edges %d != 2\n\
    -        A vertex appears twice in a edge list.  May occur during merging.",
    -        numvertices, numfacets, numridges/2);
    -      /* occurs if lots of merging and a vertex ends up twice in an edge list.  e.g., RBOX 1000 s W1e-13 t995849315 D2 | QHULL d Tc Tv */
    -    }
    -  }
    -  if (waserror)
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -} /* checkpolygon */
    -
    -
    -/*---------------------------------
    -
    -  qh_checkvertex( vertex )
    -    check vertex for consistency
    -    checks vertex->neighbors
    -
    -  notes:
    -    neighbors checked efficiently in checkpolygon
    -*/
    -void qh_checkvertex(vertexT *vertex) {
    -  boolT waserror= False;
    -  facetT *neighbor, **neighborp, *errfacet=NULL;
    -
    -  if (qh_pointid(vertex->point) == -1) {
    -    qh_fprintf(qh ferr, 6144, "qhull internal error (qh_checkvertex): unknown point id %p\n", vertex->point);
    -    waserror= True;
    -  }
    -  if (vertex->id >= qh vertex_id) {
    -    qh_fprintf(qh ferr, 6145, "qhull internal error (qh_checkvertex): unknown vertex id %d\n", vertex->id);
    -    waserror= True;
    -  }
    -  if (!waserror && !vertex->deleted) {
    -    if (qh_setsize(vertex->neighbors)) {
    -      FOREACHneighbor_(vertex) {
    -        if (!qh_setin(neighbor->vertices, vertex)) {
    -          qh_fprintf(qh ferr, 6146, "qhull internal error (qh_checkvertex): neighbor f%d does not contain v%d\n", neighbor->id, vertex->id);
    -          errfacet= neighbor;
    -          waserror= True;
    -        }
    -      }
    -    }
    -  }
    -  if (waserror) {
    -    qh_errprint("ERRONEOUS", NULL, NULL, NULL, vertex);
    -    qh_errexit(qh_ERRqhull, errfacet, NULL);
    -  }
    -} /* checkvertex */
    -
    -/*---------------------------------
    -
    -  qh_clearcenters( type )
    -    clear old data from facet->center
    -
    -  notes:
    -    sets new centertype
    -    nop if CENTERtype is the same
    -*/
    -void qh_clearcenters(qh_CENTER type) {
    -  facetT *facet;
    -
    -  if (qh CENTERtype != type) {
    -    FORALLfacets {
    -      if (facet->tricoplanar && !facet->keepcentrum)
    -          facet->center= NULL;
    -      else if (qh CENTERtype == qh_ASvoronoi){
    -        if (facet->center) {
    -          qh_memfree(facet->center, qh center_size);
    -          facet->center= NULL;
    -        }
    -      }else /* qh CENTERtype == qh_AScentrum */ {
    -        if (facet->center) {
    -          qh_memfree(facet->center, qh normal_size);
    -          facet->center= NULL;
    -        }
    -      }
    -    }
    -    qh CENTERtype= type;
    -  }
    -  trace2((qh ferr, 2043, "qh_clearcenters: switched to center type %d\n", type));
    -} /* clearcenters */
    -
    -/*---------------------------------
    -
    -  qh_createsimplex( vertices )
    -    creates a simplex from a set of vertices
    -
    -  returns:
    -    initializes qh.facet_list to the simplex
    -    initializes qh.newfacet_list, .facet_tail
    -    initializes qh.vertex_list, .newvertex_list, .vertex_tail
    -
    -  design:
    -    initializes lists
    -    for each vertex
    -      create a new facet
    -    for each new facet
    -      create its neighbor set
    -*/
    -void qh_createsimplex(setT *vertices) {
    -  facetT *facet= NULL, *newfacet;
    -  boolT toporient= True;
    -  int vertex_i, vertex_n, nth;
    -  setT *newfacets= qh_settemp(qh hull_dim+1);
    -  vertexT *vertex;
    -
    -  qh facet_list= qh newfacet_list= qh facet_tail= qh_newfacet();
    -  qh num_facets= qh num_vertices= qh num_visible= 0;
    -  qh vertex_list= qh newvertex_list= qh vertex_tail= qh_newvertex(NULL);
    -  FOREACHvertex_i_(vertices) {
    -    newfacet= qh_newfacet();
    -    newfacet->vertices= qh_setnew_delnthsorted(vertices, vertex_n,
    -                                                vertex_i, 0);
    -    newfacet->toporient= (unsigned char)toporient;
    -    qh_appendfacet(newfacet);
    -    newfacet->newfacet= True;
    -    qh_appendvertex(vertex);
    -    qh_setappend(&newfacets, newfacet);
    -    toporient ^= True;
    -  }
    -  FORALLnew_facets {
    -    nth= 0;
    -    FORALLfacet_(qh newfacet_list) {
    -      if (facet != newfacet)
    -        SETelem_(newfacet->neighbors, nth++)= facet;
    -    }
    -    qh_settruncate(newfacet->neighbors, qh hull_dim);
    -  }
    -  qh_settempfree(&newfacets);
    -  trace1((qh ferr, 1028, "qh_createsimplex: created simplex\n"));
    -} /* createsimplex */
    -
    -/*---------------------------------
    -
    -  qh_delridge( ridge )
    -    deletes ridge from data structures it belongs to
    -    frees up its memory
    -
    -  notes:
    -    in merge.c, caller sets vertex->delridge for each vertex
    -    ridges also freed in qh_freeqhull
    -*/
    -void qh_delridge(ridgeT *ridge) {
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  qh_setdel(ridge->top->ridges, ridge);
    -  qh_setdel(ridge->bottom->ridges, ridge);
    -  qh_setfree(&(ridge->vertices));
    -  qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
    -} /* delridge */
    -
    -
    -/*---------------------------------
    -
    -  qh_delvertex( vertex )
    -    deletes a vertex and frees its memory
    -
    -  notes:
    -    assumes vertex->adjacencies have been updated if needed
    -    unlinks from vertex_list
    -*/
    -void qh_delvertex(vertexT *vertex) {
    -
    -  if (vertex == qh tracevertex)
    -    qh tracevertex= NULL;
    -  qh_removevertex(vertex);
    -  qh_setfree(&vertex->neighbors);
    -  qh_memfree(vertex, (int)sizeof(vertexT));
    -} /* delvertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_facet3vertex(  )
    -    return temporary set of 3-d vertices in qh_ORIENTclock order
    -
    -  design:
    -    if simplicial facet
    -      build set from facet->vertices with facet->toporient
    -    else
    -      for each ridge in order
    -        build set from ridge's vertices
    -*/
    -setT *qh_facet3vertex(facetT *facet) {
    -  ridgeT *ridge, *firstridge;
    -  vertexT *vertex;
    -  int cntvertices, cntprojected=0;
    -  setT *vertices;
    -
    -  cntvertices= qh_setsize(facet->vertices);
    -  vertices= qh_settemp(cntvertices);
    -  if (facet->simplicial) {
    -    if (cntvertices != 3) {
    -      qh_fprintf(qh ferr, 6147, "qhull internal error (qh_facet3vertex): only %d vertices for simplicial facet f%d\n",
    -                  cntvertices, facet->id);
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -    qh_setappend(&vertices, SETfirst_(facet->vertices));
    -    if (facet->toporient ^ qh_ORIENTclock)
    -      qh_setappend(&vertices, SETsecond_(facet->vertices));
    -    else
    -      qh_setaddnth(&vertices, 0, SETsecond_(facet->vertices));
    -    qh_setappend(&vertices, SETelem_(facet->vertices, 2));
    -  }else {
    -    ridge= firstridge= SETfirstt_(facet->ridges, ridgeT);   /* no infinite */
    -    while ((ridge= qh_nextridge3d(ridge, facet, &vertex))) {
    -      qh_setappend(&vertices, vertex);
    -      if (++cntprojected > cntvertices || ridge == firstridge)
    -        break;
    -    }
    -    if (!ridge || cntprojected != cntvertices) {
    -      qh_fprintf(qh ferr, 6148, "qhull internal error (qh_facet3vertex): ridges for facet %d don't match up.  got at least %d\n",
    -                  facet->id, cntprojected);
    -      qh_errexit(qh_ERRqhull, facet, ridge);
    -    }
    -  }
    -  return vertices;
    -} /* facet3vertex */
    -
    -/*---------------------------------
    -
    -  qh_findbestfacet( point, bestoutside, bestdist, isoutside )
    -    find facet that is furthest below a point
    -
    -    for Delaunay triangulations,
    -      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
    -      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
    -
    -  returns:
    -    if bestoutside is set (e.g., qh_ALL)
    -      returns best facet that is not upperdelaunay
    -      if Delaunay and inside, point is outside circumsphere of bestfacet
    -    else
    -      returns first facet below point
    -      if point is inside, returns nearest, !upperdelaunay facet
    -    distance to facet
    -    isoutside set if outside of facet
    -
    -  notes:
    -    For tricoplanar facets, this finds one of the tricoplanar facets closest
    -    to the point.  For Delaunay triangulations, the point may be inside a
    -    different tricoplanar facet. See locate a facet with qh_findbestfacet()
    -
    -    If inside, qh_findbestfacet performs an exhaustive search
    -       this may be too conservative.  Sometimes it is clearly required.
    -
    -    qh_findbestfacet is not used by qhull.
    -    uses qh.visit_id and qh.coplanarset
    -
    -  see:
    -    qh_findbest
    -*/
    -facetT *qh_findbestfacet(pointT *point, boolT bestoutside,
    -           realT *bestdist, boolT *isoutside) {
    -  facetT *bestfacet= NULL;
    -  int numpart, totpart= 0;
    -
    -  bestfacet= qh_findbest(point, qh facet_list,
    -                            bestoutside, !qh_ISnewfacets, bestoutside /* qh_NOupper */,
    -                            bestdist, isoutside, &totpart);
    -  if (*bestdist < -qh DISTround) {
    -    bestfacet= qh_findfacet_all(point, bestdist, isoutside, &numpart);
    -    totpart += numpart;
    -    if ((isoutside && bestoutside)
    -    || (!isoutside && bestfacet->upperdelaunay)) {
    -      bestfacet= qh_findbest(point, bestfacet,
    -                            bestoutside, False, bestoutside,
    -                            bestdist, isoutside, &totpart);
    -      totpart += numpart;
    -    }
    -  }
    -  trace3((qh ferr, 3014, "qh_findbestfacet: f%d dist %2.2g isoutside %d totpart %d\n",
    -          bestfacet->id, *bestdist, *isoutside, totpart));
    -  return bestfacet;
    -} /* findbestfacet */
    -
    -/*---------------------------------
    -
    -  qh_findbestlower( facet, point, bestdist, numpart )
    -    returns best non-upper, non-flipped neighbor of facet for point
    -    if needed, searches vertex neighbors
    -
    -  returns:
    -    returns bestdist and updates numpart
    -
    -  notes:
    -    if Delaunay and inside, point is outside of circumsphere of bestfacet
    -    called by qh_findbest() for points above an upperdelaunay facet
    -
    -*/
    -facetT *qh_findbestlower(facetT *upperfacet, pointT *point, realT *bestdistp, int *numpart) {
    -  facetT *neighbor, **neighborp, *bestfacet= NULL;
    -  realT bestdist= -REALmax/2 /* avoid underflow */;
    -  realT dist;
    -  vertexT *vertex;
    -
    -  zinc_(Zbestlower);
    -  FOREACHneighbor_(upperfacet) {
    -    if (neighbor->upperdelaunay || neighbor->flipped)
    -      continue;
    -    (*numpart)++;
    -    qh_distplane(point, neighbor, &dist);
    -    if (dist > bestdist) {
    -      bestfacet= neighbor;
    -      bestdist= dist;
    -    }
    -  }
    -  if (!bestfacet) {
    -    zinc_(Zbestlowerv);
    -    /* rarely called, numpart does not count nearvertex computations */
    -    vertex= qh_nearvertex(upperfacet, point, &dist);
    -    qh_vertexneighbors();
    -    FOREACHneighbor_(vertex) {
    -      if (neighbor->upperdelaunay || neighbor->flipped)
    -        continue;
    -      (*numpart)++;
    -      qh_distplane(point, neighbor, &dist);
    -      if (dist > bestdist) {
    -        bestfacet= neighbor;
    -        bestdist= dist;
    -      }
    -    }
    -  }
    -  if (!bestfacet) {
    -    qh_fprintf(qh ferr, 6228, "\n\
    -Qhull internal error (qh_findbestlower): all neighbors of facet %d are flipped or upper Delaunay.\n\
    -Please report this error to qhull_bug@qhull.org with the input and all of the output.\n",
    -       upperfacet->id);
    -    qh_errexit(qh_ERRqhull, upperfacet, NULL);
    -  }
    -  *bestdistp= bestdist;
    -  trace3((qh ferr, 3015, "qh_findbestlower: f%d dist %2.2g for f%d p%d\n",
    -          bestfacet->id, bestdist, upperfacet->id, qh_pointid(point)));
    -  return bestfacet;
    -} /* findbestlower */
    -
    -/*---------------------------------
    -
    -  qh_findfacet_all( point, bestdist, isoutside, numpart )
    -    exhaustive search for facet below a point
    -
    -    for Delaunay triangulations,
    -      Use qh_setdelaunay() to lift point to paraboloid and scale by 'Qbb' if needed
    -      Do not use options 'Qbk', 'QBk', or 'QbB' since they scale the coordinates.
    -
    -  returns:
    -    returns first facet below point
    -    if point is inside,
    -      returns nearest facet
    -    distance to facet
    -    isoutside if point is outside of the hull
    -    number of distance tests
    -
    -  notes:
    -    for library users, not used by Qhull
    -*/
    -facetT *qh_findfacet_all(pointT *point, realT *bestdist, boolT *isoutside,
    -                          int *numpart) {
    -  facetT *bestfacet= NULL, *facet;
    -  realT dist;
    -  int totpart= 0;
    -
    -  *bestdist= -REALmax;
    -  *isoutside= False;
    -  FORALLfacets {
    -    if (facet->flipped || !facet->normal)
    -      continue;
    -    totpart++;
    -    qh_distplane(point, facet, &dist);
    -    if (dist > *bestdist) {
    -      *bestdist= dist;
    -      bestfacet= facet;
    -      if (dist > qh MINoutside) {
    -        *isoutside= True;
    -        break;
    -      }
    -    }
    -  }
    -  *numpart= totpart;
    -  trace3((qh ferr, 3016, "qh_findfacet_all: f%d dist %2.2g isoutside %d totpart %d\n",
    -          getid_(bestfacet), *bestdist, *isoutside, totpart));
    -  return bestfacet;
    -} /* findfacet_all */
    -
    -/*---------------------------------
    -
    -  qh_findgood( facetlist, goodhorizon )
    -    identify good facets for qh.PRINTgood
    -    if qh.GOODvertex>0
    -      facet includes point as vertex
    -      if !match, returns goodhorizon
    -      inactive if qh.MERGING
    -    if qh.GOODpoint
    -      facet is visible or coplanar (>0) or not visible (<0)
    -    if qh.GOODthreshold
    -      facet->normal matches threshold
    -    if !goodhorizon and !match,
    -      selects facet with closest angle
    -      sets GOODclosest
    -
    -  returns:
    -    number of new, good facets found
    -    determines facet->good
    -    may update qh.GOODclosest
    -
    -  notes:
    -    qh_findgood_all further reduces the good region
    -
    -  design:
    -    count good facets
    -    mark good facets for qh.GOODpoint
    -    mark good facets for qh.GOODthreshold
    -    if necessary
    -      update qh.GOODclosest
    -*/
    -int qh_findgood(facetT *facetlist, int goodhorizon) {
    -  facetT *facet, *bestfacet= NULL;
    -  realT angle, bestangle= REALmax, dist;
    -  int  numgood=0;
    -
    -  FORALLfacet_(facetlist) {
    -    if (facet->good)
    -      numgood++;
    -  }
    -  if (qh GOODvertex>0 && !qh MERGING) {
    -    FORALLfacet_(facetlist) {
    -      if (!qh_isvertex(qh GOODvertexp, facet->vertices)) {
    -        facet->good= False;
    -        numgood--;
    -      }
    -    }
    -  }
    -  if (qh GOODpoint && numgood) {
    -    FORALLfacet_(facetlist) {
    -      if (facet->good && facet->normal) {
    -        zinc_(Zdistgood);
    -        qh_distplane(qh GOODpointp, facet, &dist);
    -        if ((qh GOODpoint > 0) ^ (dist > 0.0)) {
    -          facet->good= False;
    -          numgood--;
    -        }
    -      }
    -    }
    -  }
    -  if (qh GOODthreshold && (numgood || goodhorizon || qh GOODclosest)) {
    -    FORALLfacet_(facetlist) {
    -      if (facet->good && facet->normal) {
    -        if (!qh_inthresholds(facet->normal, &angle)) {
    -          facet->good= False;
    -          numgood--;
    -          if (angle < bestangle) {
    -            bestangle= angle;
    -            bestfacet= facet;
    -          }
    -        }
    -      }
    -    }
    -    if (!numgood && (!goodhorizon || qh GOODclosest)) {
    -      if (qh GOODclosest) {
    -        if (qh GOODclosest->visible)
    -          qh GOODclosest= NULL;
    -        else {
    -          qh_inthresholds(qh GOODclosest->normal, &angle);
    -          if (angle < bestangle)
    -            bestfacet= qh GOODclosest;
    -        }
    -      }
    -      if (bestfacet && bestfacet != qh GOODclosest) {
    -        if (qh GOODclosest)
    -          qh GOODclosest->good= False;
    -        qh GOODclosest= bestfacet;
    -        bestfacet->good= True;
    -        numgood++;
    -        trace2((qh ferr, 2044, "qh_findgood: f%d is closest(%2.2g) to thresholds\n",
    -           bestfacet->id, bestangle));
    -        return numgood;
    -      }
    -    }else if (qh GOODclosest) { /* numgood > 0 */
    -      qh GOODclosest->good= False;
    -      qh GOODclosest= NULL;
    -    }
    -  }
    -  zadd_(Zgoodfacet, numgood);
    -  trace2((qh ferr, 2045, "qh_findgood: found %d good facets with %d good horizon\n",
    -               numgood, goodhorizon));
    -  if (!numgood && qh GOODvertex>0 && !qh MERGING)
    -    return goodhorizon;
    -  return numgood;
    -} /* findgood */
    -
    -/*---------------------------------
    -
    -  qh_findgood_all( facetlist )
    -    apply other constraints for good facets (used by qh.PRINTgood)
    -    if qh.GOODvertex
    -      facet includes (>0) or doesn't include (<0) point as vertex
    -      if last good facet and ONLYgood, prints warning and continues
    -    if qh.SPLITthresholds
    -      facet->normal matches threshold, or if none, the closest one
    -    calls qh_findgood
    -    nop if good not used
    -
    -  returns:
    -    clears facet->good if not good
    -    sets qh.num_good
    -
    -  notes:
    -    this is like qh_findgood but more restrictive
    -
    -  design:
    -    uses qh_findgood to mark good facets
    -    marks facets for qh.GOODvertex
    -    marks facets for qh.SPLITthreholds
    -*/
    -void qh_findgood_all(facetT *facetlist) {
    -  facetT *facet, *bestfacet=NULL;
    -  realT angle, bestangle= REALmax;
    -  int  numgood=0, startgood;
    -
    -  if (!qh GOODvertex && !qh GOODthreshold && !qh GOODpoint
    -  && !qh SPLITthresholds)
    -    return;
    -  if (!qh ONLYgood)
    -    qh_findgood(qh facet_list, 0);
    -  FORALLfacet_(facetlist) {
    -    if (facet->good)
    -      numgood++;
    -  }
    -  if (qh GOODvertex <0 || (qh GOODvertex > 0 && qh MERGING)) {
    -    FORALLfacet_(facetlist) {
    -      if (facet->good && ((qh GOODvertex > 0) ^ !!qh_isvertex(qh GOODvertexp, facet->vertices))) {
    -        if (!--numgood) {
    -          if (qh ONLYgood) {
    -            qh_fprintf(qh ferr, 7064, "qhull warning: good vertex p%d does not match last good facet f%d.  Ignored.\n",
    -               qh_pointid(qh GOODvertexp), facet->id);
    -            return;
    -          }else if (qh GOODvertex > 0)
    -            qh_fprintf(qh ferr, 7065, "qhull warning: point p%d is not a vertex('QV%d').\n",
    -                qh GOODvertex-1, qh GOODvertex-1);
    -          else
    -            qh_fprintf(qh ferr, 7066, "qhull warning: point p%d is a vertex for every facet('QV-%d').\n",
    -                -qh GOODvertex - 1, -qh GOODvertex - 1);
    -        }
    -        facet->good= False;
    -      }
    -    }
    -  }
    -  startgood= numgood;
    -  if (qh SPLITthresholds) {
    -    FORALLfacet_(facetlist) {
    -      if (facet->good) {
    -        if (!qh_inthresholds(facet->normal, &angle)) {
    -          facet->good= False;
    -          numgood--;
    -          if (angle < bestangle) {
    -            bestangle= angle;
    -            bestfacet= facet;
    -          }
    -        }
    -      }
    -    }
    -    if (!numgood && bestfacet) {
    -      bestfacet->good= True;
    -      numgood++;
    -      trace0((qh ferr, 23, "qh_findgood_all: f%d is closest(%2.2g) to thresholds\n",
    -           bestfacet->id, bestangle));
    -      return;
    -    }
    -  }
    -  qh num_good= numgood;
    -  trace0((qh ferr, 24, "qh_findgood_all: %d good facets remain out of %d facets\n",
    -        numgood, startgood));
    -} /* findgood_all */
    -
    -/*---------------------------------
    -
    -  qh_furthestnext()
    -    set qh.facet_next to facet with furthest of all furthest points
    -    searches all facets on qh.facet_list
    -
    -  notes:
    -    this may help avoid precision problems
    -*/
    -void qh_furthestnext(void /* qh facet_list */) {
    -  facetT *facet, *bestfacet= NULL;
    -  realT dist, bestdist= -REALmax;
    -
    -  FORALLfacets {
    -    if (facet->outsideset) {
    -#if qh_COMPUTEfurthest
    -      pointT *furthest;
    -      furthest= (pointT*)qh_setlast(facet->outsideset);
    -      zinc_(Zcomputefurthest);
    -      qh_distplane(furthest, facet, &dist);
    -#else
    -      dist= facet->furthestdist;
    -#endif
    -      if (dist > bestdist) {
    -        bestfacet= facet;
    -        bestdist= dist;
    -      }
    -    }
    -  }
    -  if (bestfacet) {
    -    qh_removefacet(bestfacet);
    -    qh_prependfacet(bestfacet, &qh facet_next);
    -    trace1((qh ferr, 1029, "qh_furthestnext: made f%d next facet(dist %.2g)\n",
    -            bestfacet->id, bestdist));
    -  }
    -} /* furthestnext */
    -
    -/*---------------------------------
    -
    -  qh_furthestout( facet )
    -    make furthest outside point the last point of outsideset
    -
    -  returns:
    -    updates facet->outsideset
    -    clears facet->notfurthest
    -    sets facet->furthestdist
    -
    -  design:
    -    determine best point of outsideset
    -    make it the last point of outsideset
    -*/
    -void qh_furthestout(facetT *facet) {
    -  pointT *point, **pointp, *bestpoint= NULL;
    -  realT dist, bestdist= -REALmax;
    -
    -  FOREACHpoint_(facet->outsideset) {
    -    qh_distplane(point, facet, &dist);
    -    zinc_(Zcomputefurthest);
    -    if (dist > bestdist) {
    -      bestpoint= point;
    -      bestdist= dist;
    -    }
    -  }
    -  if (bestpoint) {
    -    qh_setdel(facet->outsideset, point);
    -    qh_setappend(&facet->outsideset, point);
    -#if !qh_COMPUTEfurthest
    -    facet->furthestdist= bestdist;
    -#endif
    -  }
    -  facet->notfurthest= False;
    -  trace3((qh ferr, 3017, "qh_furthestout: p%d is furthest outside point of f%d\n",
    -          qh_pointid(point), facet->id));
    -} /* furthestout */
    -
    -
    -/*---------------------------------
    -
    -  qh_infiniteloop( facet )
    -    report infinite loop error due to facet
    -*/
    -void qh_infiniteloop(facetT *facet) {
    -
    -  qh_fprintf(qh ferr, 6149, "qhull internal error (qh_infiniteloop): potential infinite loop detected\n");
    -  qh_errexit(qh_ERRqhull, facet, NULL);
    -} /* qh_infiniteloop */
    -
    -/*---------------------------------
    -
    -  qh_initbuild()
    -    initialize hull and outside sets with point array
    -    qh.FIRSTpoint/qh.NUMpoints is point array
    -    if qh.GOODpoint
    -      adds qh.GOODpoint to initial hull
    -
    -  returns:
    -    qh_facetlist with initial hull
    -    points partioned into outside sets, coplanar sets, or inside
    -    initializes qh.GOODpointp, qh.GOODvertexp,
    -
    -  design:
    -    initialize global variables used during qh_buildhull
    -    determine precision constants and points with max/min coordinate values
    -      if qh.SCALElast, scale last coordinate(for 'd')
    -    build initial simplex
    -    partition input points into facets of initial simplex
    -    set up lists
    -    if qh.ONLYgood
    -      check consistency
    -      add qh.GOODvertex if defined
    -*/
    -void qh_initbuild( void) {
    -  setT *maxpoints, *vertices;
    -  facetT *facet;
    -  int i, numpart;
    -  realT dist;
    -  boolT isoutside;
    -
    -  qh furthest_id= -1;
    -  qh lastreport= 0;
    -  qh facet_id= qh vertex_id= qh ridge_id= 0;
    -  qh visit_id= qh vertex_visit= 0;
    -  qh maxoutdone= False;
    -
    -  if (qh GOODpoint > 0)
    -    qh GOODpointp= qh_point(qh GOODpoint-1);
    -  else if (qh GOODpoint < 0)
    -    qh GOODpointp= qh_point(-qh GOODpoint-1);
    -  if (qh GOODvertex > 0)
    -    qh GOODvertexp= qh_point(qh GOODvertex-1);
    -  else if (qh GOODvertex < 0)
    -    qh GOODvertexp= qh_point(-qh GOODvertex-1);
    -  if ((qh GOODpoint
    -       && (qh GOODpointp < qh first_point  /* also catches !GOODpointp */
    -           || qh GOODpointp > qh_point(qh num_points-1)))
    -    || (qh GOODvertex
    -        && (qh GOODvertexp < qh first_point  /* also catches !GOODvertexp */
    -            || qh GOODvertexp > qh_point(qh num_points-1)))) {
    -    qh_fprintf(qh ferr, 6150, "qhull input error: either QGn or QVn point is > p%d\n",
    -             qh num_points-1);
    -    qh_errexit(qh_ERRinput, NULL, NULL);
    -  }
    -  maxpoints= qh_maxmin(qh first_point, qh num_points, qh hull_dim);
    -  if (qh SCALElast)
    -    qh_scalelast(qh first_point, qh num_points, qh hull_dim,
    -               qh MINlastcoord, qh MAXlastcoord, qh MAXwidth);
    -  qh_detroundoff();
    -  if (qh DELAUNAY && qh upper_threshold[qh hull_dim-1] > REALmax/2
    -                  && qh lower_threshold[qh hull_dim-1] < -REALmax/2) {
    -    for (i=qh_PRINTEND; i--; ) {
    -      if (qh PRINTout[i] == qh_PRINTgeom && qh DROPdim < 0
    -          && !qh GOODthreshold && !qh SPLITthresholds)
    -        break;  /* in this case, don't set upper_threshold */
    -    }
    -    if (i < 0) {
    -      if (qh UPPERdelaunay) { /* matches qh.upperdelaunay in qh_setfacetplane */
    -        qh lower_threshold[qh hull_dim-1]= qh ANGLEround * qh_ZEROdelaunay;
    -        qh GOODthreshold= True;
    -      }else {
    -        qh upper_threshold[qh hull_dim-1]= -qh ANGLEround * qh_ZEROdelaunay;
    -        if (!qh GOODthreshold)
    -          qh SPLITthresholds= True; /* build upper-convex hull even if Qg */
    -          /* qh_initqhull_globals errors if Qg without Pdk/etc. */
    -      }
    -    }
    -  }
    -  vertices= qh_initialvertices(qh hull_dim, maxpoints, qh first_point, qh num_points);
    -  qh_initialhull(vertices);  /* initial qh facet_list */
    -  qh_partitionall(vertices, qh first_point, qh num_points);
    -  if (qh PRINToptions1st || qh TRACElevel || qh IStracing) {
    -    if (qh TRACElevel || qh IStracing)
    -      qh_fprintf(qh ferr, 8103, "\nTrace level %d for %s | %s\n",
    -         qh IStracing ? qh IStracing : qh TRACElevel, qh rbox_command, qh qhull_command);
    -    qh_fprintf(qh ferr, 8104, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
    -  }
    -  qh_resetlists(False, qh_RESETvisible /*qh visible_list newvertex_list newfacet_list */);
    -  qh facet_next= qh facet_list;
    -  qh_furthestnext(/* qh facet_list */);
    -  if (qh PREmerge) {
    -    qh cos_max= qh premerge_cos;
    -    qh centrum_radius= qh premerge_centrum;
    -  }
    -  if (qh ONLYgood) {
    -    if (qh GOODvertex > 0 && qh MERGING) {
    -      qh_fprintf(qh ferr, 6151, "qhull input error: 'Qg QVn' (only good vertex) does not work with merging.\nUse 'QJ' to joggle the input or 'Q0' to turn off merging.\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    if (!(qh GOODthreshold || qh GOODpoint
    -         || (!qh MERGEexact && !qh PREmerge && qh GOODvertexp))) {
    -      qh_fprintf(qh ferr, 6152, "qhull input error: 'Qg' (ONLYgood) needs a good threshold('Pd0D0'), a\n\
    -good point(QGn or QG-n), or a good vertex with 'QJ' or 'Q0' (QVn).\n");
    -      qh_errexit(qh_ERRinput, NULL, NULL);
    -    }
    -    if (qh GOODvertex > 0  && !qh MERGING  /* matches qh_partitionall */
    -        && !qh_isvertex(qh GOODvertexp, vertices)) {
    -      facet= qh_findbestnew(qh GOODvertexp, qh facet_list,
    -                          &dist, !qh_ALL, &isoutside, &numpart);
    -      zadd_(Zdistgood, numpart);
    -      if (!isoutside) {
    -        qh_fprintf(qh ferr, 6153, "qhull input error: point for QV%d is inside initial simplex.  It can not be made a vertex.\n",
    -               qh_pointid(qh GOODvertexp));
    -        qh_errexit(qh_ERRinput, NULL, NULL);
    -      }
    -      if (!qh_addpoint(qh GOODvertexp, facet, False)) {
    -        qh_settempfree(&vertices);
    -        qh_settempfree(&maxpoints);
    -        return;
    -      }
    -    }
    -    qh_findgood(qh facet_list, 0);
    -  }
    -  qh_settempfree(&vertices);
    -  qh_settempfree(&maxpoints);
    -  trace1((qh ferr, 1030, "qh_initbuild: initial hull created and points partitioned\n"));
    -} /* initbuild */
    -
    -/*---------------------------------
    -
    -  qh_initialhull( vertices )
    -    constructs the initial hull as a DIM3 simplex of vertices
    -
    -  design:
    -    creates a simplex (initializes lists)
    -    determines orientation of simplex
    -    sets hyperplanes for facets
    -    doubles checks orientation (in case of axis-parallel facets with Gaussian elimination)
    -    checks for flipped facets and qh.NARROWhull
    -    checks the result
    -*/
    -void qh_initialhull(setT *vertices) {
    -  facetT *facet, *firstfacet, *neighbor, **neighborp;
    -  realT dist, angle, minangle= REALmax;
    -#ifndef qh_NOtrace
    -  int k;
    -#endif
    -
    -  qh_createsimplex(vertices);  /* qh facet_list */
    -  qh_resetlists(False, qh_RESETvisible);
    -  qh facet_next= qh facet_list;      /* advance facet when processed */
    -  qh interior_point= qh_getcenter(vertices);
    -  firstfacet= qh facet_list;
    -  qh_setfacetplane(firstfacet);
    -  zinc_(Znumvisibility); /* needs to be in printsummary */
    -  qh_distplane(qh interior_point, firstfacet, &dist);
    -  if (dist > 0) {
    -    FORALLfacets
    -      facet->toporient ^= (unsigned char)True;
    -  }
    -  FORALLfacets
    -    qh_setfacetplane(facet);
    -  FORALLfacets {
    -    if (!qh_checkflipped(facet, NULL, qh_ALL)) {/* due to axis-parallel facet */
    -      trace1((qh ferr, 1031, "qh_initialhull: initial orientation incorrect.  Correct all facets\n"));
    -      facet->flipped= False;
    -      FORALLfacets {
    -        facet->toporient ^= (unsigned char)True;
    -        qh_orientoutside(facet);
    -      }
    -      break;
    -    }
    -  }
    -  FORALLfacets {
    -    if (!qh_checkflipped(facet, NULL, !qh_ALL)) {  /* can happen with 'R0.1' */
    -      if (qh DELAUNAY && ! qh ATinfinity) {
    -        if (qh UPPERdelaunay)
    -          qh_fprintf(qh ferr, 6240, "Qhull input error: Can not compute the upper Delaunay triangulation or upper Voronoi diagram of cocircular/cospherical points.\n");
    -        else
    -          qh_fprintf(qh ferr, 6239, "Qhull input error: Use option 'Qz' for the Delaunay triangulation or Voronoi diagram of cocircular/cospherical points.  Option 'Qz' adds a point \"at infinity\" (above the corresponding paraboloid).\n");
    -        qh_errexit(qh_ERRinput, NULL, NULL);
    -      }
    -      qh_precision("initial facet is coplanar with interior point");
    -      qh_fprintf(qh ferr, 6154, "qhull precision error: initial facet %d is coplanar with the interior point\n",
    -                   facet->id);
    -      qh_errexit(qh_ERRsingular, facet, NULL);
    -    }
    -    FOREACHneighbor_(facet) {
    -      angle= qh_getangle(facet->normal, neighbor->normal);
    -      minimize_( minangle, angle);
    -    }
    -  }
    -  if (minangle < qh_MAXnarrow && !qh NOnarrow) {
    -    realT diff= 1.0 + minangle;
    -
    -    qh NARROWhull= True;
    -    qh_option("_narrow-hull", NULL, &diff);
    -    if (minangle < qh_WARNnarrow && !qh RERUN && qh PRINTprecision)
    -      qh_printhelp_narrowhull(qh ferr, minangle);
    -  }
    -  zzval_(Zprocessed)= qh hull_dim+1;
    -  qh_checkpolygon(qh facet_list);
    -  qh_checkconvex(qh facet_list,   qh_DATAfault);
    -#ifndef qh_NOtrace
    -  if (qh IStracing >= 1) {
    -    qh_fprintf(qh ferr, 8105, "qh_initialhull: simplex constructed, interior point:");
    -    for (k=0; k < qh hull_dim; k++)
    -      qh_fprintf(qh ferr, 8106, " %6.4g", qh interior_point[k]);
    -    qh_fprintf(qh ferr, 8107, "\n");
    -  }
    -#endif
    -} /* initialhull */
    -
    -/*---------------------------------
    -
    -  qh_initialvertices( dim, maxpoints, points, numpoints )
    -    determines a non-singular set of initial vertices
    -    maxpoints may include duplicate points
    -
    -  returns:
    -    temporary set of dim+1 vertices in descending order by vertex id
    -    if qh.RANDOMoutside && !qh.ALLpoints
    -      picks random points
    -    if dim >= qh_INITIALmax,
    -      uses min/max x and max points with non-zero determinants
    -
    -  notes:
    -    unless qh.ALLpoints,
    -      uses maxpoints as long as determinate is non-zero
    -*/
    -setT *qh_initialvertices(int dim, setT *maxpoints, pointT *points, int numpoints) {
    -  pointT *point, **pointp;
    -  setT *vertices, *simplex, *tested;
    -  realT randr;
    -  int idx, point_i, point_n, k;
    -  boolT nearzero= False;
    -
    -  vertices= qh_settemp(dim + 1);
    -  simplex= qh_settemp(dim+1);
    -  if (qh ALLpoints)
    -    qh_maxsimplex(dim, NULL, points, numpoints, &simplex);
    -  else if (qh RANDOMoutside) {
    -    while (qh_setsize(simplex) != dim+1) {
    -      randr= qh_RANDOMint;
    -      randr= randr/(qh_RANDOMmax+1);
    -      idx= (int)floor(qh num_points * randr);
    -      while (qh_setin(simplex, qh_point(idx))) {
    -            idx++; /* in case qh_RANDOMint always returns the same value */
    -        idx= idx < qh num_points ? idx : 0;
    -      }
    -      qh_setappend(&simplex, qh_point(idx));
    -    }
    -  }else if (qh hull_dim >= qh_INITIALmax) {
    -    tested= qh_settemp(dim+1);
    -    qh_setappend(&simplex, SETfirst_(maxpoints));   /* max and min X coord */
    -    qh_setappend(&simplex, SETsecond_(maxpoints));
    -    qh_maxsimplex(fmin_(qh_INITIALsearch, dim), maxpoints, points, numpoints, &simplex);
    -    k= qh_setsize(simplex);
    -    FOREACHpoint_i_(maxpoints) {
    -      if (point_i & 0x1) {     /* first pick up max. coord. points */
    -        if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
    -          qh_detsimplex(point, simplex, k, &nearzero);
    -          if (nearzero)
    -            qh_setappend(&tested, point);
    -          else {
    -            qh_setappend(&simplex, point);
    -            if (++k == dim)  /* use search for last point */
    -              break;
    -          }
    -        }
    -      }
    -    }
    -    while (k != dim && (point= (pointT*)qh_setdellast(maxpoints))) {
    -      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
    -        qh_detsimplex(point, simplex, k, &nearzero);
    -        if (nearzero)
    -          qh_setappend(&tested, point);
    -        else {
    -          qh_setappend(&simplex, point);
    -          k++;
    -        }
    -      }
    -    }
    -    idx= 0;
    -    while (k != dim && (point= qh_point(idx++))) {
    -      if (!qh_setin(simplex, point) && !qh_setin(tested, point)){
    -        qh_detsimplex(point, simplex, k, &nearzero);
    -        if (!nearzero){
    -          qh_setappend(&simplex, point);
    -          k++;
    -        }
    -      }
    -    }
    -    qh_settempfree(&tested);
    -    qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
    -  }else
    -    qh_maxsimplex(dim, maxpoints, points, numpoints, &simplex);
    -  FOREACHpoint_(simplex)
    -    qh_setaddnth(&vertices, 0, qh_newvertex(point)); /* descending order */
    -  qh_settempfree(&simplex);
    -  return vertices;
    -} /* initialvertices */
    -
    -
    -/*---------------------------------
    -
    -  qh_isvertex(  )
    -    returns vertex if point is in vertex set, else returns NULL
    -
    -  notes:
    -    for qh.GOODvertex
    -*/
    -vertexT *qh_isvertex(pointT *point, setT *vertices) {
    -  vertexT *vertex, **vertexp;
    -
    -  FOREACHvertex_(vertices) {
    -    if (vertex->point == point)
    -      return vertex;
    -  }
    -  return NULL;
    -} /* isvertex */
    -
    -/*---------------------------------
    -
    -  qh_makenewfacets( point )
    -    make new facets from point and qh.visible_list
    -
    -  returns:
    -    qh.newfacet_list= list of new facets with hyperplanes and ->newfacet
    -    qh.newvertex_list= list of vertices in new facets with ->newlist set
    -
    -    if (qh.ONLYgood)
    -      newfacets reference horizon facets, but not vice versa
    -      ridges reference non-simplicial horizon ridges, but not vice versa
    -      does not change existing facets
    -    else
    -      sets qh.NEWfacets
    -      new facets attached to horizon facets and ridges
    -      for visible facets,
    -        visible->r.replace is corresponding new facet
    -
    -  see also:
    -    qh_makenewplanes() -- make hyperplanes for facets
    -    qh_attachnewfacets() -- attachnewfacets if not done here(qh ONLYgood)
    -    qh_matchnewfacets() -- match up neighbors
    -    qh_updatevertices() -- update vertex neighbors and delvertices
    -    qh_deletevisible() -- delete visible facets
    -    qh_checkpolygon() --check the result
    -    qh_triangulate() -- triangulate a non-simplicial facet
    -
    -  design:
    -    for each visible facet
    -      make new facets to its horizon facets
    -      update its f.replace
    -      clear its neighbor set
    -*/
    -vertexT *qh_makenewfacets(pointT *point /*visible_list*/) {
    -  facetT *visible, *newfacet= NULL, *newfacet2= NULL, *neighbor, **neighborp;
    -  vertexT *apex;
    -  int numnew=0;
    -
    -  qh newfacet_list= qh facet_tail;
    -  qh newvertex_list= qh vertex_tail;
    -  apex= qh_newvertex(point);
    -  qh_appendvertex(apex);
    -  qh visit_id++;
    -  if (!qh ONLYgood)
    -    qh NEWfacets= True;
    -  FORALLvisible_facets {
    -    FOREACHneighbor_(visible)
    -      neighbor->seen= False;
    -    if (visible->ridges) {
    -      visible->visitid= qh visit_id;
    -      newfacet2= qh_makenew_nonsimplicial(visible, apex, &numnew);
    -    }
    -    if (visible->simplicial)
    -      newfacet= qh_makenew_simplicial(visible, apex, &numnew);
    -    if (!qh ONLYgood) {
    -      if (newfacet2)  /* newfacet is null if all ridges defined */
    -        newfacet= newfacet2;
    -      if (newfacet)
    -        visible->f.replace= newfacet;
    -      else
    -        zinc_(Zinsidevisible);
    -      SETfirst_(visible->neighbors)= NULL;
    -    }
    -  }
    -  trace1((qh ferr, 1032, "qh_makenewfacets: created %d new facets from point p%d to horizon\n",
    -          numnew, qh_pointid(point)));
    -  if (qh IStracing >= 4)
    -    qh_printfacetlist(qh newfacet_list, NULL, qh_ALL);
    -  return apex;
    -} /* makenewfacets */
    -
    -/*---------------------------------
    -
    -  qh_matchduplicates( atfacet, atskip, hashsize, hashcount )
    -    match duplicate ridges in qh.hash_table for atfacet/atskip
    -    duplicates marked with ->dupridge and qh_DUPLICATEridge
    -
    -  returns:
    -    picks match with worst merge (min distance apart)
    -    updates hashcount
    -
    -  see also:
    -    qh_matchneighbor
    -
    -  notes:
    -
    -  design:
    -    compute hash value for atfacet and atskip
    -    repeat twice -- once to make best matches, once to match the rest
    -      for each possible facet in qh.hash_table
    -        if it is a matching facet and pass 2
    -          make match
    -          unless tricoplanar, mark match for merging (qh_MERGEridge)
    -          [e.g., tricoplanar RBOX s 1000 t993602376 | QHULL C-1e-3 d Qbb FA Qt]
    -        if it is a matching facet and pass 1
    -          test if this is a better match
    -      if pass 1,
    -        make best match (it will not be merged)
    -*/
    -#ifndef qh_NOmerge
    -void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
    -  boolT same, ismatch;
    -  int hash, scan;
    -  facetT *facet, *newfacet, *maxmatch= NULL, *maxmatch2= NULL, *nextfacet;
    -  int skip, newskip, nextskip= 0, maxskip= 0, maxskip2= 0, makematch;
    -  realT maxdist= -REALmax, mindist, dist2, low, high;
    -
    -  hash= qh_gethash(hashsize, atfacet->vertices, qh hull_dim, 1,
    -                     SETelem_(atfacet->vertices, atskip));
    -  trace2((qh ferr, 2046, "qh_matchduplicates: find duplicate matches for f%d skip %d hash %d hashcount %d\n",
    -          atfacet->id, atskip, hash, *hashcount));
    -  for (makematch= 0; makematch < 2; makematch++) {
    -    qh visit_id++;
    -    for (newfacet= atfacet, newskip= atskip; newfacet; newfacet= nextfacet, newskip= nextskip) {
    -      zinc_(Zhashlookup);
    -      nextfacet= NULL;
    -      newfacet->visitid= qh visit_id;
    -      for (scan= hash; (facet= SETelemt_(qh hash_table, scan, facetT));
    -           scan= (++scan >= hashsize ? 0 : scan)) {
    -        if (!facet->dupridge || facet->visitid == qh visit_id)
    -          continue;
    -        zinc_(Zhashtests);
    -        if (qh_matchvertices(1, newfacet->vertices, newskip, facet->vertices, &skip, &same)) {
    -          ismatch= (same == (boolT)(newfacet->toporient ^ facet->toporient));
    -          if (SETelemt_(facet->neighbors, skip, facetT) != qh_DUPLICATEridge) {
    -            if (!makematch) {
    -              qh_fprintf(qh ferr, 6155, "qhull internal error (qh_matchduplicates): missing dupridge at f%d skip %d for new f%d skip %d hash %d\n",
    -                     facet->id, skip, newfacet->id, newskip, hash);
    -              qh_errexit2 (qh_ERRqhull, facet, newfacet);
    -            }
    -          }else if (ismatch && makematch) {
    -            if (SETelemt_(newfacet->neighbors, newskip, facetT) == qh_DUPLICATEridge) {
    -              SETelem_(facet->neighbors, skip)= newfacet;
    -              if (newfacet->tricoplanar)
    -                SETelem_(newfacet->neighbors, newskip)= facet;
    -              else
    -                SETelem_(newfacet->neighbors, newskip)= qh_MERGEridge;
    -              *hashcount -= 2; /* removed two unmatched facets */
    -              trace4((qh ferr, 4059, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d merge\n",
    -                    facet->id, skip, newfacet->id, newskip));
    -            }
    -          }else if (ismatch) {
    -            mindist= qh_getdistance(facet, newfacet, &low, &high);
    -            dist2= qh_getdistance(newfacet, facet, &low, &high);
    -            minimize_(mindist, dist2);
    -            if (mindist > maxdist) {
    -              maxdist= mindist;
    -              maxmatch= facet;
    -              maxskip= skip;
    -              maxmatch2= newfacet;
    -              maxskip2= newskip;
    -            }
    -            trace3((qh ferr, 3018, "qh_matchduplicates: duplicate f%d skip %d new f%d skip %d at dist %2.2g, max is now f%d f%d\n",
    -                    facet->id, skip, newfacet->id, newskip, mindist,
    -                    maxmatch->id, maxmatch2->id));
    -          }else { /* !ismatch */
    -            nextfacet= facet;
    -            nextskip= skip;
    -          }
    -        }
    -        if (makematch && !facet
    -        && SETelemt_(facet->neighbors, skip, facetT) == qh_DUPLICATEridge) {
    -          qh_fprintf(qh ferr, 6156, "qhull internal error (qh_matchduplicates): no MERGEridge match for duplicate f%d skip %d at hash %d\n",
    -                     newfacet->id, newskip, hash);
    -          qh_errexit(qh_ERRqhull, newfacet, NULL);
    -        }
    -      }
    -    } /* end of for each new facet at hash */
    -    if (!makematch) {
    -      if (!maxmatch) {
    -        qh_fprintf(qh ferr, 6157, "qhull internal error (qh_matchduplicates): no maximum match at duplicate f%d skip %d at hash %d\n",
    -                     atfacet->id, atskip, hash);
    -        qh_errexit(qh_ERRqhull, atfacet, NULL);
    -      }
    -      SETelem_(maxmatch->neighbors, maxskip)= maxmatch2;
    -      SETelem_(maxmatch2->neighbors, maxskip2)= maxmatch;
    -      *hashcount -= 2; /* removed two unmatched facets */
    -      zzinc_(Zmultiridge);
    -      trace0((qh ferr, 25, "qh_matchduplicates: duplicate f%d skip %d matched with new f%d skip %d keep\n",
    -              maxmatch->id, maxskip, maxmatch2->id, maxskip2));
    -      qh_precision("ridge with multiple neighbors");
    -      if (qh IStracing >= 4)
    -        qh_errprint("DUPLICATED/MATCH", maxmatch, maxmatch2, NULL, NULL);
    -    }
    -  }
    -} /* matchduplicates */
    -
    -/*---------------------------------
    -
    -  qh_nearcoplanar()
    -    for all facets, remove near-inside points from facet->coplanarset
    -    coplanar points defined by innerplane from qh_outerinner()
    -
    -  returns:
    -    if qh KEEPcoplanar && !qh KEEPinside
    -      facet->coplanarset only contains coplanar points
    -    if qh.JOGGLEmax
    -      drops inner plane by another qh.JOGGLEmax diagonal since a
    -        vertex could shift out while a coplanar point shifts in
    -
    -  notes:
    -    used for qh.PREmerge and qh.JOGGLEmax
    -    must agree with computation of qh.NEARcoplanar in qh_detroundoff()
    -  design:
    -    if not keeping coplanar or inside points
    -      free all coplanar sets
    -    else if not keeping both coplanar and inside points
    -      remove !coplanar or !inside points from coplanar sets
    -*/
    -void qh_nearcoplanar(void /* qh.facet_list */) {
    -  facetT *facet;
    -  pointT *point, **pointp;
    -  int numpart;
    -  realT dist, innerplane;
    -
    -  if (!qh KEEPcoplanar && !qh KEEPinside) {
    -    FORALLfacets {
    -      if (facet->coplanarset)
    -        qh_setfree( &facet->coplanarset);
    -    }
    -  }else if (!qh KEEPcoplanar || !qh KEEPinside) {
    -    qh_outerinner(NULL, NULL, &innerplane);
    -    if (qh JOGGLEmax < REALmax/2)
    -      innerplane -= qh JOGGLEmax * sqrt((realT)qh hull_dim);
    -    numpart= 0;
    -    FORALLfacets {
    -      if (facet->coplanarset) {
    -        FOREACHpoint_(facet->coplanarset) {
    -          numpart++;
    -          qh_distplane(point, facet, &dist);
    -          if (dist < innerplane) {
    -            if (!qh KEEPinside)
    -              SETref_(point)= NULL;
    -          }else if (!qh KEEPcoplanar)
    -            SETref_(point)= NULL;
    -        }
    -        qh_setcompact(facet->coplanarset);
    -      }
    -    }
    -    zzadd_(Zcheckpart, numpart);
    -  }
    -} /* nearcoplanar */
    -
    -/*---------------------------------
    -
    -  qh_nearvertex( facet, point, bestdist )
    -    return nearest vertex in facet to point
    -
    -  returns:
    -    vertex and its distance
    -
    -  notes:
    -    if qh.DELAUNAY
    -      distance is measured in the input set
    -    searches neighboring tricoplanar facets (requires vertexneighbors)
    -      Slow implementation.  Recomputes vertex set for each point.
    -    The vertex set could be stored in the qh.keepcentrum facet.
    -*/
    -vertexT *qh_nearvertex(facetT *facet, pointT *point, realT *bestdistp) {
    -  realT bestdist= REALmax, dist;
    -  vertexT *bestvertex= NULL, *vertex, **vertexp, *apex;
    -  coordT *center;
    -  facetT *neighbor, **neighborp;
    -  setT *vertices;
    -  int dim= qh hull_dim;
    -
    -  if (qh DELAUNAY)
    -    dim--;
    -  if (facet->tricoplanar) {
    -    if (!qh VERTEXneighbors || !facet->center) {
    -      qh_fprintf(qh ferr, 6158, "qhull internal error (qh_nearvertex): qh.VERTEXneighbors and facet->center required for tricoplanar facets\n");
    -      qh_errexit(qh_ERRqhull, facet, NULL);
    -    }
    -    vertices= qh_settemp(qh TEMPsize);
    -    apex= SETfirstt_(facet->vertices, vertexT);
    -    center= facet->center;
    -    FOREACHneighbor_(apex) {
    -      if (neighbor->center == center) {
    -        FOREACHvertex_(neighbor->vertices)
    -          qh_setappend(&vertices, vertex);
    -      }
    -    }
    -  }else
    -    vertices= facet->vertices;
    -  FOREACHvertex_(vertices) {
    -    dist= qh_pointdist(vertex->point, point, -dim);
    -    if (dist < bestdist) {
    -      bestdist= dist;
    -      bestvertex= vertex;
    -    }
    -  }
    -  if (facet->tricoplanar)
    -    qh_settempfree(&vertices);
    -  *bestdistp= sqrt(bestdist);
    -  trace3((qh ferr, 3019, "qh_nearvertex: v%d dist %2.2g for f%d p%d\n",
    -        bestvertex->id, *bestdistp, facet->id, qh_pointid(point)));
    -  return bestvertex;
    -} /* nearvertex */
    -
    -/*---------------------------------
    -
    -  qh_newhashtable( newsize )
    -    returns size of qh.hash_table of at least newsize slots
    -
    -  notes:
    -    assumes qh.hash_table is NULL
    -    qh_HASHfactor determines the number of extra slots
    -    size is not divisible by 2, 3, or 5
    -*/
    -int qh_newhashtable(int newsize) {
    -  int size;
    -
    -  size= ((newsize+1)*qh_HASHfactor) | 0x1;  /* odd number */
    -  while (True) {
    -    if (newsize<0 || size<0) {
    -        qh_fprintf(qhmem.ferr, 6236, "qhull error (qh_newhashtable): negative request (%d) or size (%d).  Did int overflow due to high-D?\n", newsize, size); /* WARN64 */
    -        qh_errexit(qhmem_ERRmem, NULL, NULL);
    -    }
    -    if ((size%3) && (size%5))
    -      break;
    -    size += 2;
    -    /* loop terminates because there is an infinite number of primes */
    -  }
    -  qh hash_table= qh_setnew(size);
    -  qh_setzero(qh hash_table, 0, size);
    -  return size;
    -} /* newhashtable */
    -
    -/*---------------------------------
    -
    -  qh_newvertex( point )
    -    returns a new vertex for point
    -*/
    -vertexT *qh_newvertex(pointT *point) {
    -  vertexT *vertex;
    -
    -  zinc_(Ztotvertices);
    -  vertex= (vertexT *)qh_memalloc((int)sizeof(vertexT));
    -  memset((char *) vertex, (size_t)0, sizeof(vertexT));
    -  if (qh vertex_id == 0xFFFFFF) {
    -    qh_fprintf(qh ferr, 6159, "qhull error: more than %d vertices.  ID field overflows and two vertices\n\
    -may have the same identifier.  Vertices will not be sorted correctly.\n", 0xFFFFFF);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }
    -  if (qh vertex_id == qh tracevertex_id)
    -    qh tracevertex= vertex;
    -  vertex->id= qh vertex_id++;
    -  vertex->point= point;
    -  vertex->dim= (unsigned char)(qh hull_dim <= MAX_vdim ? qh hull_dim : 0);
    -  trace4((qh ferr, 4060, "qh_newvertex: vertex p%d(v%d) created\n", qh_pointid(vertex->point),
    -          vertex->id));
    -  return(vertex);
    -} /* newvertex */
    -
    -/*---------------------------------
    -
    -  qh_nextridge3d( atridge, facet, vertex )
    -    return next ridge and vertex for a 3d facet
    -    returns NULL on error
    -    [for QhullFacet::nextRidge3d] Does not call qh_errexit nor access qh_qh.
    -
    -  notes:
    -    in qh_ORIENTclock order
    -    this is a O(n^2) implementation to trace all ridges
    -    be sure to stop on any 2nd visit
    -    same as QhullRidge::nextRidge3d
    -    does not use qh_qh or qh_errexit [QhullFacet.cpp]
    -
    -  design:
    -    for each ridge
    -      exit if it is the ridge after atridge
    -*/
    -ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
    -  vertexT *atvertex, *vertex, *othervertex;
    -  ridgeT *ridge, **ridgep;
    -
    -  if ((atridge->top == facet) ^ qh_ORIENTclock)
    -    atvertex= SETsecondt_(atridge->vertices, vertexT);
    -  else
    -    atvertex= SETfirstt_(atridge->vertices, vertexT);
    -  FOREACHridge_(facet->ridges) {
    -    if (ridge == atridge)
    -      continue;
    -    if ((ridge->top == facet) ^ qh_ORIENTclock) {
    -      othervertex= SETsecondt_(ridge->vertices, vertexT);
    -      vertex= SETfirstt_(ridge->vertices, vertexT);
    -    }else {
    -      vertex= SETsecondt_(ridge->vertices, vertexT);
    -      othervertex= SETfirstt_(ridge->vertices, vertexT);
    -    }
    -    if (vertex == atvertex) {
    -      if (vertexp)
    -        *vertexp= othervertex;
    -      return ridge;
    -    }
    -  }
    -  return NULL;
    -} /* nextridge3d */
    -#else /* qh_NOmerge */
    -void qh_matchduplicates(facetT *atfacet, int atskip, int hashsize, int *hashcount) {
    -}
    -ridgeT *qh_nextridge3d(ridgeT *atridge, facetT *facet, vertexT **vertexp) {
    -
    -  return NULL;
    -}
    -#endif /* qh_NOmerge */
    -
    -/*---------------------------------
    -
    -  qh_outcoplanar()
    -    move points from all facets' outsidesets to their coplanarsets
    -
    -  notes:
    -    for post-processing under qh.NARROWhull
    -
    -  design:
    -    for each facet
    -      for each outside point for facet
    -        partition point into coplanar set
    -*/
    -void qh_outcoplanar(void /* facet_list */) {
    -  pointT *point, **pointp;
    -  facetT *facet;
    -  realT dist;
    -
    -  trace1((qh ferr, 1033, "qh_outcoplanar: move outsideset to coplanarset for qh NARROWhull\n"));
    -  FORALLfacets {
    -    FOREACHpoint_(facet->outsideset) {
    -      qh num_outside--;
    -      if (qh KEEPcoplanar || qh KEEPnearinside) {
    -        qh_distplane(point, facet, &dist);
    -        zinc_(Zpartition);
    -        qh_partitioncoplanar(point, facet, &dist);
    -      }
    -    }
    -    qh_setfree(&facet->outsideset);
    -  }
    -} /* outcoplanar */
    -
    -/*---------------------------------
    -
    -  qh_point( id )
    -    return point for a point id, or NULL if unknown
    -
    -  alternative code:
    -    return((pointT *)((unsigned   long)qh.first_point
    -           + (unsigned long)((id)*qh.normal_size)));
    -*/
    -pointT *qh_point(int id) {
    -
    -  if (id < 0)
    -    return NULL;
    -  if (id < qh num_points)
    -    return qh first_point + id * qh hull_dim;
    -  id -= qh num_points;
    -  if (id < qh_setsize(qh other_points))
    -    return SETelemt_(qh other_points, id, pointT);
    -  return NULL;
    -} /* point */
    -
    -/*---------------------------------
    -
    -  qh_point_add( set, point, elem )
    -    stores elem at set[point.id]
    -
    -  returns:
    -    access function for qh_pointfacet and qh_pointvertex
    -
    -  notes:
    -    checks point.id
    -*/
    -void qh_point_add(setT *set, pointT *point, void *elem) {
    -  int id, size;
    -
    -  SETreturnsize_(set, size);
    -  if ((id= qh_pointid(point)) < 0)
    -    qh_fprintf(qh ferr, 7067, "qhull internal warning (point_add): unknown point %p id %d\n",
    -      point, id);
    -  else if (id >= size) {
    -    qh_fprintf(qh ferr, 6160, "qhull internal errror(point_add): point p%d is out of bounds(%d)\n",
    -             id, size);
    -    qh_errexit(qh_ERRqhull, NULL, NULL);
    -  }else
    -    SETelem_(set, id)= elem;
    -} /* point_add */
    -
    -
    -/*---------------------------------
    -
    -  qh_pointfacet()
    -    return temporary set of facet for each point
    -    the set is indexed by point id
    -
    -  notes:
    -    vertices assigned to one of the facets
    -    coplanarset assigned to the facet
    -    outside set assigned to the facet
    -    NULL if no facet for point (inside)
    -      includes qh.GOODpointp
    -
    -  access:
    -    FOREACHfacet_i_(facets) { ... }
    -    SETelem_(facets, i)
    -
    -  design:
    -    for each facet
    -      add each vertex
    -      add each coplanar point
    -      add each outside point
    -*/
    -setT *qh_pointfacet(void /*qh facet_list*/) {
    -  int numpoints= qh num_points + qh_setsize(qh other_points);
    -  setT *facets;
    -  facetT *facet;
    -  vertexT *vertex, **vertexp;
    -  pointT *point, **pointp;
    -
    -  facets= qh_settemp(numpoints);
    -  qh_setzero(facets, 0, numpoints);
    -  qh vertex_visit++;
    -  FORALLfacets {
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->visitid != qh vertex_visit) {
    -        vertex->visitid= qh vertex_visit;
    -        qh_point_add(facets, vertex->point, facet);
    -      }
    -    }
    -    FOREACHpoint_(facet->coplanarset)
    -      qh_point_add(facets, point, facet);
    -    FOREACHpoint_(facet->outsideset)
    -      qh_point_add(facets, point, facet);
    -  }
    -  return facets;
    -} /* pointfacet */
    -
    -/*---------------------------------
    -
    -  qh_pointvertex(  )
    -    return temporary set of vertices indexed by point id
    -    entry is NULL if no vertex for a point
    -      this will include qh.GOODpointp
    -
    -  access:
    -    FOREACHvertex_i_(vertices) { ... }
    -    SETelem_(vertices, i)
    -*/
    -setT *qh_pointvertex(void /*qh facet_list*/) {
    -  int numpoints= qh num_points + qh_setsize(qh other_points);
    -  setT *vertices;
    -  vertexT *vertex;
    -
    -  vertices= qh_settemp(numpoints);
    -  qh_setzero(vertices, 0, numpoints);
    -  FORALLvertices
    -    qh_point_add(vertices, vertex->point, vertex);
    -  return vertices;
    -} /* pointvertex */
    -
    -
    -/*---------------------------------
    -
    -  qh_prependfacet( facet, facetlist )
    -    prepend facet to the start of a facetlist
    -
    -  returns:
    -    increments qh.numfacets
    -    updates facetlist, qh.facet_list, facet_next
    -
    -  notes:
    -    be careful of prepending since it can lose a pointer.
    -      e.g., can lose _next by deleting and then prepending before _next
    -*/
    -void qh_prependfacet(facetT *facet, facetT **facetlist) {
    -  facetT *prevfacet, *list;
    -
    -
    -  trace4((qh ferr, 4061, "qh_prependfacet: prepend f%d before f%d\n",
    -          facet->id, getid_(*facetlist)));
    -  if (!*facetlist)
    -    (*facetlist)= qh facet_tail;
    -  list= *facetlist;
    -  prevfacet= list->previous;
    -  facet->previous= prevfacet;
    -  if (prevfacet)
    -    prevfacet->next= facet;
    -  list->previous= facet;
    -  facet->next= *facetlist;
    -  if (qh facet_list == list)  /* this may change *facetlist */
    -    qh facet_list= facet;
    -  if (qh facet_next == list)
    -    qh facet_next= facet;
    -  *facetlist= facet;
    -  qh num_facets++;
    -} /* prependfacet */
    -
    -
    -/*---------------------------------
    -
    -  qh_printhashtable( fp )
    -    print hash table to fp
    -
    -  notes:
    -    not in I/O to avoid bringing io.c in
    -
    -  design:
    -    for each hash entry
    -      if defined
    -        if unmatched or will merge (NULL, qh_MERGEridge, qh_DUPLICATEridge)
    -          print entry and neighbors
    -*/
    -void qh_printhashtable(FILE *fp) {
    -  facetT *facet, *neighbor;
    -  int id, facet_i, facet_n, neighbor_i= 0, neighbor_n= 0;
    -  vertexT *vertex, **vertexp;
    -
    -  FOREACHfacet_i_(qh hash_table) {
    -    if (facet) {
    -      FOREACHneighbor_i_(facet) {
    -        if (!neighbor || neighbor == qh_MERGEridge || neighbor == qh_DUPLICATEridge)
    -          break;
    -      }
    -      if (neighbor_i == neighbor_n)
    -        continue;
    -      qh_fprintf(fp, 9283, "hash %d f%d ", facet_i, facet->id);
    -      FOREACHvertex_(facet->vertices)
    -        qh_fprintf(fp, 9284, "v%d ", vertex->id);
    -      qh_fprintf(fp, 9285, "\n neighbors:");
    -      FOREACHneighbor_i_(facet) {
    -        if (neighbor == qh_MERGEridge)
    -          id= -3;
    -        else if (neighbor == qh_DUPLICATEridge)
    -          id= -2;
    -        else
    -          id= getid_(neighbor);
    -        qh_fprintf(fp, 9286, " %d", id);
    -      }
    -      qh_fprintf(fp, 9287, "\n");
    -    }
    -  }
    -} /* printhashtable */
    -
    -
    -/*---------------------------------
    -
    -  qh_printlists( fp )
    -    print out facet and vertex list for debugging (without 'f/v' tags)
    -*/
    -void qh_printlists(void) {
    -  facetT *facet;
    -  vertexT *vertex;
    -  int count= 0;
    -
    -  qh_fprintf(qh ferr, 8108, "qh_printlists: facets:");
    -  FORALLfacets {
    -    if (++count % 100 == 0)
    -      qh_fprintf(qh ferr, 8109, "\n     ");
    -    qh_fprintf(qh ferr, 8110, " %d", facet->id);
    -  }
    -  qh_fprintf(qh ferr, 8111, "\n  new facets %d visible facets %d next facet for qh_addpoint %d\n  vertices(new %d):",
    -     getid_(qh newfacet_list), getid_(qh visible_list), getid_(qh facet_next),
    -     getid_(qh newvertex_list));
    -  count = 0;
    -  FORALLvertices {
    -    if (++count % 100 == 0)
    -      qh_fprintf(qh ferr, 8112, "\n     ");
    -    qh_fprintf(qh ferr, 8113, " %d", vertex->id);
    -  }
    -  qh_fprintf(qh ferr, 8114, "\n");
    -} /* printlists */
    -
    -/*---------------------------------
    -
    -  qh_resetlists( stats, qh_RESETvisible )
    -    reset newvertex_list, newfacet_list, visible_list
    -    if stats,
    -      maintains statistics
    -
    -  returns:
    -    visible_list is empty if qh_deletevisible was called
    -*/
    -void qh_resetlists(boolT stats, boolT resetVisible /*qh newvertex_list newfacet_list visible_list*/) {
    -  vertexT *vertex;
    -  facetT *newfacet, *visible;
    -  int totnew=0, totver=0;
    -
    -  if (stats) {
    -    FORALLvertex_(qh newvertex_list)
    -      totver++;
    -    FORALLnew_facets
    -      totnew++;
    -    zadd_(Zvisvertextot, totver);
    -    zmax_(Zvisvertexmax, totver);
    -    zadd_(Znewfacettot, totnew);
    -    zmax_(Znewfacetmax, totnew);
    -  }
    -  FORALLvertex_(qh newvertex_list)
    -    vertex->newlist= False;
    -  qh newvertex_list= NULL;
    -  FORALLnew_facets
    -    newfacet->newfacet= False;
    -  qh newfacet_list= NULL;
    -  if (resetVisible) {
    -    FORALLvisible_facets {
    -      visible->f.replace= NULL;
    -      visible->visible= False;
    -    }
    -    qh num_visible= 0;
    -  }
    -  qh visible_list= NULL; /* may still have visible facets via qh_triangulate */
    -  qh NEWfacets= False;
    -} /* resetlists */
    -
    -/*---------------------------------
    -
    -  qh_setvoronoi_all()
    -    compute Voronoi centers for all facets
    -    includes upperDelaunay facets if qh.UPPERdelaunay ('Qu')
    -
    -  returns:
    -    facet->center is the Voronoi center
    -
    -  notes:
    -    this is unused/untested code
    -      please email bradb@shore.net if this works ok for you
    -
    -  use:
    -    FORALLvertices {...} to locate the vertex for a point.
    -    FOREACHneighbor_(vertex) {...} to visit the Voronoi centers for a Voronoi cell.
    -*/
    -void qh_setvoronoi_all(void) {
    -  facetT *facet;
    -
    -  qh_clearcenters(qh_ASvoronoi);
    -  qh_vertexneighbors();
    -
    -  FORALLfacets {
    -    if (!facet->normal || !facet->upperdelaunay || qh UPPERdelaunay) {
    -      if (!facet->center)
    -        facet->center= qh_facetcenter(facet->vertices);
    -    }
    -  }
    -} /* setvoronoi_all */
    -
    -#ifndef qh_NOmerge
    -
    -/*---------------------------------
    -
    -  qh_triangulate()
    -    triangulate non-simplicial facets on qh.facet_list,
    -    if qh VORONOI, sets Voronoi centers of non-simplicial facets
    -    nop if hasTriangulation
    -
    -  returns:
    -    all facets simplicial
    -    each tricoplanar facet has ->f.triowner == owner of ->center,normal,etc.
    -
    -  notes:
    -    call after qh_check_output since may switch to Voronoi centers
    -    Output may overwrite ->f.triowner with ->f.area
    -*/
    -void qh_triangulate(void /*qh facet_list*/) {
    -  facetT *facet, *nextfacet, *owner;
    -  int onlygood= qh ONLYgood;
    -  facetT *neighbor, *visible= NULL, *facet1, *facet2, *new_facet_list= NULL;
    -  facetT *orig_neighbor= NULL, *otherfacet;
    -  vertexT *new_vertex_list= NULL;
    -  mergeT *merge;
    -  mergeType mergetype;
    -  int neighbor_i, neighbor_n;
    -
    -  if (qh hasTriangulation)
    -      return;
    -  trace1((qh ferr, 1034, "qh_triangulate: triangulate non-simplicial facets\n"));
    -  if (qh hull_dim == 2)
    -    return;
    -  if (qh VORONOI) {  /* otherwise lose Voronoi centers [could rebuild vertex set from tricoplanar] */
    -    qh_clearcenters(qh_ASvoronoi);
    -    qh_vertexneighbors();
    -  }
    -  qh ONLYgood= False; /* for makenew_nonsimplicial */
    -  qh visit_id++;
    -  qh NEWfacets= True;
    -  qh degen_mergeset= qh_settemp(qh TEMPsize);
    -  qh newvertex_list= qh vertex_tail;
    -  for (facet= qh facet_list; facet && facet->next; facet= nextfacet) { /* non-simplicial facets moved to end */
    -    nextfacet= facet->next;
    -    if (facet->visible || facet->simplicial)
    -      continue;
    -    /* triangulate all non-simplicial facets, otherwise merging does not work, e.g., RBOX c P-0.1 P+0.1 P+0.1 D3 | QHULL d Qt Tv */
    -    if (!new_facet_list)
    -      new_facet_list= facet;  /* will be moved to end */
    -    qh_triangulate_facet(facet, &new_vertex_list);
    -  }
    -  trace2((qh ferr, 2047, "qh_triangulate: delete null facets from f%d -- apex same as second vertex\n", getid_(new_facet_list)));
    -  for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* null facets moved to end */
    -    nextfacet= facet->next;
    -    if (facet->visible)
    -      continue;
    -    if (facet->ridges) {
    -      if (qh_setsize(facet->ridges) > 0) {
    -        qh_fprintf(qh ferr, 6161, "qhull error (qh_triangulate): ridges still defined for f%d\n", facet->id);
    -        qh_errexit(qh_ERRqhull, facet, NULL);
    -      }
    -      qh_setfree(&facet->ridges);
    -    }
    -    if (SETfirst_(facet->vertices) == SETsecond_(facet->vertices)) {
    -      zinc_(Ztrinull);
    -      qh_triangulate_null(facet);
    -    }
    -  }
    -  trace2((qh ferr, 2048, "qh_triangulate: delete %d or more mirror facets -- same vertices and neighbors\n", qh_setsize(qh degen_mergeset)));
    -  qh visible_list= qh facet_tail;
    -  while ((merge= (mergeT*)qh_setdellast(qh degen_mergeset))) {
    -    facet1= merge->facet1;
    -    facet2= merge->facet2;
    -    mergetype= merge->type;
    -    qh_memfree(merge, (int)sizeof(mergeT));
    -    if (mergetype == MRGmirror) {
    -      zinc_(Ztrimirror);
    -      qh_triangulate_mirror(facet1, facet2);
    -    }
    -  }
    -  qh_settempfree(&qh degen_mergeset);
    -  trace2((qh ferr, 2049, "qh_triangulate: update neighbor lists for vertices from v%d\n", getid_(new_vertex_list)));
    -  qh newvertex_list= new_vertex_list;  /* all vertices of new facets */
    -  qh visible_list= NULL;
    -  qh_updatevertices(/*qh newvertex_list, empty newfacet_list and visible_list*/);
    -  qh_resetlists(False, !qh_RESETvisible /*qh newvertex_list, empty newfacet_list and visible_list*/);
    -
    -  trace2((qh ferr, 2050, "qh_triangulate: identify degenerate tricoplanar facets from f%d\n", getid_(new_facet_list)));
    -  trace2((qh ferr, 2051, "qh_triangulate: and replace facet->f.triowner with tricoplanar facets that own center, normal, etc.\n"));
    -  FORALLfacet_(new_facet_list) {
    -    if (facet->tricoplanar && !facet->visible) {
    -      FOREACHneighbor_i_(facet) {
    -        if (neighbor_i == 0) {  /* first iteration */
    -          if (neighbor->tricoplanar)
    -            orig_neighbor= neighbor->f.triowner;
    -          else
    -            orig_neighbor= neighbor;
    -        }else {
    -          if (neighbor->tricoplanar)
    -            otherfacet= neighbor->f.triowner;
    -          else
    -            otherfacet= neighbor;
    -          if (orig_neighbor == otherfacet) {
    -            zinc_(Ztridegen);
    -            facet->degenerate= True;
    -            break;
    -          }
    -        }
    -      }
    -    }
    -  }
    -
    -  trace2((qh ferr, 2052, "qh_triangulate: delete visible facets -- non-simplicial, null, and mirrored facets\n"));
    -  owner= NULL;
    -  visible= NULL;
    -  for (facet= new_facet_list; facet && facet->next; facet= nextfacet) { /* may delete facet */
    -    nextfacet= facet->next;
    -    if (facet->visible) {
    -      if (facet->tricoplanar) { /* a null or mirrored facet */
    -        qh_delfacet(facet);
    -        qh num_visible--;
    -      }else {  /* a non-simplicial facet followed by its tricoplanars */
    -        if (visible && !owner) {
    -          /*  RBOX 200 s D5 t1001471447 | QHULL Qt C-0.01 Qx Qc Tv Qt -- f4483 had 6 vertices/neighbors and 8 ridges */
    -          trace2((qh ferr, 2053, "qh_triangulate: all tricoplanar facets degenerate for non-simplicial facet f%d\n",
    -                       visible->id));
    -          qh_delfacet(visible);
    -          qh num_visible--;
    -        }
    -        visible= facet;
    -        owner= NULL;
    -      }
    -    }else if (facet->tricoplanar) {
    -      if (facet->f.triowner != visible) {
    -        qh_fprintf(qh ferr, 6162, "qhull error (qh_triangulate): tricoplanar facet f%d not owned by its visible, non-simplicial facet f%d\n", facet->id, getid_(visible));
    -        qh_errexit2 (qh_ERRqhull, facet, visible);
    -      }
    -      if (owner)
    -        facet->f.triowner= owner;
    -      else if (!facet->degenerate) {
    -        owner= facet;
    -        nextfacet= visible->next; /* rescan tricoplanar facets with owner */
    -        facet->keepcentrum= True;  /* one facet owns ->normal, etc. */
    -        facet->coplanarset= visible->coplanarset;
    -        facet->outsideset= visible->outsideset;
    -        visible->coplanarset= NULL;
    -        visible->outsideset= NULL;
    -        if (!qh TRInormals) { /* center and normal copied to tricoplanar facets */
    -          visible->center= NULL;
    -          visible->normal= NULL;
    -        }
    -        qh_delfacet(visible);
    -        qh num_visible--;
    -      }
    -    }
    -  }
    -  if (visible && !owner) {
    -    trace2((qh ferr, 2054, "qh_triangulate: all tricoplanar facets degenerate for last non-simplicial facet f%d\n",
    -                 visible->id));
    -    qh_delfacet(visible);
    -    qh num_visible--;
    -  }
    -  qh NEWfacets= False;
    -  qh ONLYgood= onlygood; /* restore value */
    -  if (qh CHECKfrequently)
    -    qh_checkpolygon(qh facet_list);
    -  qh hasTriangulation= True;
    -} /* triangulate */
    -
    -
    -/*---------------------------------
    -
    -  qh_triangulate_facet(facetA)
    -    triangulate a non-simplicial facet
    -      if qh.CENTERtype=qh_ASvoronoi, sets its Voronoi center
    -  returns:
    -    qh.newfacet_list == simplicial facets
    -      facet->tricoplanar set and ->keepcentrum false
    -      facet->degenerate set if duplicated apex
    -      facet->f.trivisible set to facetA
    -      facet->center copied from facetA (created if qh_ASvoronoi)
    -        qh_eachvoronoi, qh_detvridge, qh_detvridge3 assume centers copied
    -      facet->normal,offset,maxoutside copied from facetA
    -
    -  notes:
    -      qh_makenew_nonsimplicial uses neighbor->seen for the same
    -
    -  see also:
    -      qh_addpoint() -- add a point
    -      qh_makenewfacets() -- construct a cone of facets for a new vertex
    -
    -  design:
    -      if qh_ASvoronoi,
    -         compute Voronoi center (facet->center)
    -      select first vertex (highest ID to preserve ID ordering of ->vertices)
    -      triangulate from vertex to ridges
    -      copy facet->center, normal, offset
    -      update vertex neighbors
    -*/
    -void qh_triangulate_facet(facetT *facetA, vertexT **first_vertex) {
    -  facetT *newfacet;
    -  facetT *neighbor, **neighborp;
    -  vertexT *apex;
    -  int numnew=0;
    -
    -  trace3((qh ferr, 3020, "qh_triangulate_facet: triangulate facet f%d\n", facetA->id));
    -
    -  if (qh IStracing >= 4)
    -    qh_printfacet(qh ferr, facetA);
    -  FOREACHneighbor_(facetA) {
    -    neighbor->seen= False;
    -    neighbor->coplanar= False;
    -  }
    -  if (qh CENTERtype == qh_ASvoronoi && !facetA->center  /* matches upperdelaunay in qh_setfacetplane() */
    -        && fabs_(facetA->normal[qh hull_dim -1]) >= qh ANGLEround * qh_ZEROdelaunay) {
    -    facetA->center= qh_facetcenter(facetA->vertices);
    -  }
    -  qh_willdelete(facetA, NULL);
    -  qh newfacet_list= qh facet_tail;
    -  facetA->visitid= qh visit_id;
    -  apex= SETfirstt_(facetA->vertices, vertexT);
    -  qh_makenew_nonsimplicial(facetA, apex, &numnew);
    -  SETfirst_(facetA->neighbors)= NULL;
    -  FORALLnew_facets {
    -    newfacet->tricoplanar= True;
    -    newfacet->f.trivisible= facetA;
    -    newfacet->degenerate= False;
    -    newfacet->upperdelaunay= facetA->upperdelaunay;
    -    newfacet->good= facetA->good;
    -    if (qh TRInormals) {
    -      newfacet->keepcentrum= True;
    -      newfacet->normal= qh_copypoints(facetA->normal, 1, qh hull_dim);
    -      if (qh CENTERtype == qh_AScentrum)
    -        newfacet->center= qh_getcentrum(newfacet);
    -      else
    -        newfacet->center= qh_copypoints(facetA->center, 1, qh hull_dim);
    -    }else {
    -      newfacet->keepcentrum= False;
    -      newfacet->normal= facetA->normal;
    -      newfacet->center= facetA->center;
    -    }
    -    newfacet->offset= facetA->offset;
    -#if qh_MAXoutside
    -    newfacet->maxoutside= facetA->maxoutside;
    -#endif
    -  }
    -  qh_matchnewfacets(/*qh newfacet_list*/);
    -  zinc_(Ztricoplanar);
    -  zadd_(Ztricoplanartot, numnew);
    -  zmax_(Ztricoplanarmax, numnew);
    -  qh visible_list= NULL;
    -  if (!(*first_vertex))
    -    (*first_vertex)= qh newvertex_list;
    -  qh newvertex_list= NULL;
    -  qh_updatevertices(/*qh newfacet_list, empty visible_list and newvertex_list*/);
    -  qh_resetlists(False, !qh_RESETvisible /*qh newfacet_list, empty visible_list and newvertex_list*/);
    -} /* triangulate_facet */
    -
    -/*---------------------------------
    -
    -  qh_triangulate_link(oldfacetA, facetA, oldfacetB, facetB)
    -    relink facetA to facetB via oldfacets
    -  returns:
    -    adds mirror facets to qh degen_mergeset (4-d and up only)
    -  design:
    -    if they are already neighbors, the opposing neighbors become MRGmirror facets
    -*/
    -void qh_triangulate_link(facetT *oldfacetA, facetT *facetA, facetT *oldfacetB, facetT *facetB) {
    -  int errmirror= False;
    -
    -  trace3((qh ferr, 3021, "qh_triangulate_link: relink old facets f%d and f%d between neighbors f%d and f%d\n",
    -         oldfacetA->id, oldfacetB->id, facetA->id, facetB->id));
    -  if (qh_setin(facetA->neighbors, facetB)) {
    -    if (!qh_setin(facetB->neighbors, facetA))
    -      errmirror= True;
    -    else
    -      qh_appendmergeset(facetA, facetB, MRGmirror, NULL);
    -  }else if (qh_setin(facetB->neighbors, facetA))
    -    errmirror= True;
    -  if (errmirror) {
    -    qh_fprintf(qh ferr, 6163, "qhull error (qh_triangulate_link): mirror facets f%d and f%d do not match for old facets f%d and f%d\n",
    -       facetA->id, facetB->id, oldfacetA->id, oldfacetB->id);
    -    qh_errexit2 (qh_ERRqhull, facetA, facetB);
    -  }
    -  qh_setreplace(facetB->neighbors, oldfacetB, facetA);
    -  qh_setreplace(facetA->neighbors, oldfacetA, facetB);
    -} /* triangulate_link */
    -
    -/*---------------------------------
    -
    -  qh_triangulate_mirror(facetA, facetB)
    -    delete mirrored facets from qh_triangulate_null() and qh_triangulate_mirror
    -      a mirrored facet shares the same vertices of a logical ridge
    -  design:
    -    since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
    -    if they are already neighbors, the opposing neighbors become MRGmirror facets
    -*/
    -void qh_triangulate_mirror(facetT *facetA, facetT *facetB) {
    -  facetT *neighbor, *neighborB;
    -  int neighbor_i, neighbor_n;
    -
    -  trace3((qh ferr, 3022, "qh_triangulate_mirror: delete mirrored facets f%d and f%d\n",
    -         facetA->id, facetB->id));
    -  FOREACHneighbor_i_(facetA) {
    -    neighborB= SETelemt_(facetB->neighbors, neighbor_i, facetT);
    -    if (neighbor == neighborB)
    -      continue; /* occurs twice */
    -    qh_triangulate_link(facetA, neighbor, facetB, neighborB);
    -  }
    -  qh_willdelete(facetA, NULL);
    -  qh_willdelete(facetB, NULL);
    -} /* triangulate_mirror */
    -
    -/*---------------------------------
    -
    -  qh_triangulate_null(facetA)
    -    remove null facetA from qh_triangulate_facet()
    -      a null facet has vertex #1 (apex) == vertex #2
    -  returns:
    -    adds facetA to ->visible for deletion after qh_updatevertices
    -    qh degen_mergeset contains mirror facets (4-d and up only)
    -  design:
    -    since a null facet duplicates the first two vertices, the opposing neighbors absorb the null facet
    -    if they are already neighbors, the opposing neighbors become MRGmirror facets
    -*/
    -void qh_triangulate_null(facetT *facetA) {
    -  facetT *neighbor, *otherfacet;
    -
    -  trace3((qh ferr, 3023, "qh_triangulate_null: delete null facet f%d\n", facetA->id));
    -  neighbor= SETfirstt_(facetA->neighbors, facetT);
    -  otherfacet= SETsecondt_(facetA->neighbors, facetT);
    -  qh_triangulate_link(facetA, neighbor, facetA, otherfacet);
    -  qh_willdelete(facetA, NULL);
    -} /* triangulate_null */
    -
    -#else /* qh_NOmerge */
    -void qh_triangulate(void) {
    -}
    -#endif /* qh_NOmerge */
    -
    -   /*---------------------------------
    -
    -  qh_vertexintersect( vertexsetA, vertexsetB )
    -    intersects two vertex sets (inverse id ordered)
    -    vertexsetA is a temporary set at the top of qhmem.tempstack
    -
    -  returns:
    -    replaces vertexsetA with the intersection
    -
    -  notes:
    -    could overwrite vertexsetA if currently too slow
    -*/
    -void qh_vertexintersect(setT **vertexsetA,setT *vertexsetB) {
    -  setT *intersection;
    -
    -  intersection= qh_vertexintersect_new(*vertexsetA, vertexsetB);
    -  qh_settempfree(vertexsetA);
    -  *vertexsetA= intersection;
    -  qh_settemppush(intersection);
    -} /* vertexintersect */
    -
    -/*---------------------------------
    -
    -  qh_vertexintersect_new(  )
    -    intersects two vertex sets (inverse id ordered)
    -
    -  returns:
    -    a new set
    -*/
    -setT *qh_vertexintersect_new(setT *vertexsetA,setT *vertexsetB) {
    -  setT *intersection= qh_setnew(qh hull_dim - 1);
    -  vertexT **vertexA= SETaddr_(vertexsetA, vertexT);
    -  vertexT **vertexB= SETaddr_(vertexsetB, vertexT);
    -
    -  while (*vertexA && *vertexB) {
    -    if (*vertexA  == *vertexB) {
    -      qh_setappend(&intersection, *vertexA);
    -      vertexA++; vertexB++;
    -    }else {
    -      if ((*vertexA)->id > (*vertexB)->id)
    -        vertexA++;
    -      else
    -        vertexB++;
    -    }
    -  }
    -  return intersection;
    -} /* vertexintersect_new */
    -
    -/*---------------------------------
    -
    -  qh_vertexneighbors()
    -    for each vertex in qh.facet_list,
    -      determine its neighboring facets
    -
    -  returns:
    -    sets qh.VERTEXneighbors
    -      nop if qh.VERTEXneighbors already set
    -      qh_addpoint() will maintain them
    -
    -  notes:
    -    assumes all vertex->neighbors are NULL
    -
    -  design:
    -    for each facet
    -      for each vertex
    -        append facet to vertex->neighbors
    -*/
    -void qh_vertexneighbors(void /*qh facet_list*/) {
    -  facetT *facet;
    -  vertexT *vertex, **vertexp;
    -
    -  if (qh VERTEXneighbors)
    -    return;
    -  trace1((qh ferr, 1035, "qh_vertexneighbors: determing neighboring facets for each vertex\n"));
    -  qh vertex_visit++;
    -  FORALLfacets {
    -    if (facet->visible)
    -      continue;
    -    FOREACHvertex_(facet->vertices) {
    -      if (vertex->visitid != qh vertex_visit) {
    -        vertex->visitid= qh vertex_visit;
    -        vertex->neighbors= qh_setnew(qh hull_dim);
    -      }
    -      qh_setappend(&vertex->neighbors, facet);
    -    }
    -  }
    -  qh VERTEXneighbors= True;
    -} /* vertexneighbors */
    -
    -/*---------------------------------
    -
    -  qh_vertexsubset( vertexsetA, vertexsetB )
    -    returns True if vertexsetA is a subset of vertexsetB
    -    assumes vertexsets are sorted
    -
    -  note:
    -    empty set is a subset of any other set
    -*/
    -boolT qh_vertexsubset(setT *vertexsetA, setT *vertexsetB) {
    -  vertexT **vertexA= (vertexT **) SETaddr_(vertexsetA, vertexT);
    -  vertexT **vertexB= (vertexT **) SETaddr_(vertexsetB, vertexT);
    -
    -  while (True) {
    -    if (!*vertexA)
    -      return True;
    -    if (!*vertexB)
    -      return False;
    -    if ((*vertexA)->id > (*vertexB)->id)
    -      return False;
    -    if (*vertexA  == *vertexB)
    -      vertexA++;
    -    vertexB++;
    -  }
    -  return False; /* avoid warnings */
    -} /* vertexsubset */
    diff --git a/extern/qhull/qhull_a.h b/extern/qhull/qhull_a.h
    deleted file mode 100644
    index 36bbe9652a9c..000000000000
    --- a/extern/qhull/qhull_a.h
    +++ /dev/null
    @@ -1,146 +0,0 @@
    -/*
      ---------------------------------
    -
    -   qhull_a.h
    -   all header files for compiling qhull
    -
    -   see qh-qhull.htm
    -
    -   see libqhull.h for user-level definitions
    -
    -   see user.h for user-defineable constants
    -
    -   defines internal functions for libqhull.c global.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/qhull_a.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -
    -   Notes:  grep for ((" and (" to catch fprintf("lkasdjf");
    -           full parens around (x?y:z)
    -           use '#include qhull/qhull_a.h' to avoid name clashes
    -*/
    -
    -#ifndef qhDEFqhulla
    -#define qhDEFqhulla 1
    -
    -#include "libqhull.h"  /* Defines data types */
    -
    -#include "stat.h"
    -#include "random.h"
    -#include "mem.h"
    -#include "qset.h"
    -#include "geom.h"
    -#include "merge.h"
    -#include "poly.h"
    -#include "io.h"
    -
    -#include 
    -#include 
    -#include 
    -#include     /* some compilers will not need float.h */
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -/*** uncomment here and qset.c
    -     if string.h does not define memcpy()
    -#include 
    -*/
    -
    -#if qh_CLOCKtype == 2  /* defined in user.h from libqhull.h */
    -#include 
    -#include 
    -#include 
    -#endif
    -
    -#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    -#pragma warning( disable : 4100)  /* unreferenced formal parameter */
    -#pragma warning( disable : 4127)  /* conditional expression is constant */
    -#pragma warning( disable : 4706)  /* assignment within conditional function */
    -#pragma warning( disable : 4996)  /* function was declared deprecated(strcpy, localtime, etc.) */
    -#endif
    -
    -/* ======= -macros- =========== */
    -
    -/*----------------------------------
    -
    -  traceN((qh ferr, 0Nnnn, "format\n", vars));
    -    calls qh_fprintf if qh.IStracing >= N
    -
    -    Add debugging traps to the end of qh_fprintf
    -
    -  notes:
    -    removing tracing reduces code size but doesn't change execution speed
    -*/
    -#ifndef qh_NOtrace
    -#define trace0(args) {if (qh IStracing) qh_fprintf args;}
    -#define trace1(args) {if (qh IStracing >= 1) qh_fprintf args;}
    -#define trace2(args) {if (qh IStracing >= 2) qh_fprintf args;}
    -#define trace3(args) {if (qh IStracing >= 3) qh_fprintf args;}
    -#define trace4(args) {if (qh IStracing >= 4) qh_fprintf args;}
    -#define trace5(args) {if (qh IStracing >= 5) qh_fprintf args;}
    -#else /* qh_NOtrace */
    -#define trace0(args) {}
    -#define trace1(args) {}
    -#define trace2(args) {}
    -#define trace3(args) {}
    -#define trace4(args) {}
    -#define trace5(args) {}
    -#endif /* qh_NOtrace */
    -
    -/*----------------------------------
    -
    -*/
    -
    -/* See Qt's qglobal.h */
    -#if !defined(SAG_COM) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
    -#   define QHULL_OS_WIN
    -#elif defined(__MWERKS__) && defined(__INTEL__)
    -#   define QHULL_OS_WIN
    -#endif
    -
    -#define QHULL_UNUSED(x) (void)x;
    -
    -/***** -libqhull.c prototypes (alphabetical after qhull) ********************/
    -
    -void    qh_qhull(void);
    -boolT   qh_addpoint(pointT *furthest, facetT *facet, boolT checkdist);
    -void    qh_buildhull(void);
    -void    qh_buildtracing(pointT *furthest, facetT *facet);
    -void    qh_build_withrestart(void);
    -void    qh_errexit2(int exitcode, facetT *facet, facetT *otherfacet);
    -void    qh_findhorizon(pointT *point, facetT *facet, int *goodvisible,int *goodhorizon);
    -pointT *qh_nextfurthest(facetT **visible);
    -void    qh_partitionall(setT *vertices, pointT *points,int npoints);
    -void    qh_partitioncoplanar(pointT *point, facetT *facet, realT *dist);
    -void    qh_partitionpoint(pointT *point, facetT *facet);
    -void    qh_partitionvisible(boolT allpoints, int *numpoints);
    -void    qh_precision(const char *reason);
    -void    qh_printsummary(FILE *fp);
    -
    -/***** -global.c internal prototypes (alphabetical) ***********************/
    -
    -void    qh_appendprint(qh_PRINT format);
    -void    qh_freebuild(boolT allmem);
    -void    qh_freebuffers(void);
    -void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    -
    -/***** -stat.c internal prototypes (alphabetical) ***********************/
    -
    -void    qh_allstatA(void);
    -void    qh_allstatB(void);
    -void    qh_allstatC(void);
    -void    qh_allstatD(void);
    -void    qh_allstatE(void);
    -void    qh_allstatE2 (void);
    -void    qh_allstatF(void);
    -void    qh_allstatG(void);
    -void    qh_allstatH(void);
    -void    qh_freebuffers(void);
    -void    qh_initbuffers(coordT *points, int numpoints, int dim, boolT ismalloc);
    -
    -#endif /* qhDEFqhulla */
    diff --git a/extern/qhull/qset.c b/extern/qhull/qset.c
    deleted file mode 100644
    index e5038f4ac192..000000000000
    --- a/extern/qhull/qset.c
    +++ /dev/null
    @@ -1,1337 +0,0 @@
    -/*
      ---------------------------------
    -
    -   qset.c
    -   implements set manipulations needed for quickhull
    -
    -   see qh-set.htm and qset.h
    -
    -   Be careful of strict aliasing (two pointers of different types
    -   that reference the same location).  The last slot of a set is
    -   either the actual size of the set plus 1, or the NULL terminator
    -   of the set (i.e., setelemT).
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/qset.c#6 $$Change: 1475 $
    -   $DateTime: 2012/01/27 22:32:16 $$Author: bbarber $
    -*/
    -
    -#include "qset.h"
    -#include "mem.h"
    -#include 
    -#include 
    -/*** uncomment here and qhull_a.h
    -     if string.h does not define memcpy()
    -#include 
    -*/
    -
    -#ifndef qhDEFlibqhull
    -typedef struct ridgeT ridgeT;
    -typedef struct facetT facetT;
    -void    qh_errexit(int exitcode, facetT *, ridgeT *);
    -void    qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... );
    -#  ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    -#  pragma warning( disable : 4127)  /* conditional expression is constant */
    -#  pragma warning( disable : 4706)  /* assignment within conditional function */
    -#  endif
    -#endif
    -
    -/*=============== internal macros ===========================*/
    -
    -/*============ functions in alphabetical order ===================*/
    -
    -/*----------------------------------
    -
    -  qh_setaddnth( setp, nth, newelem)
    -    adds newelem as n'th element of sorted or unsorted *setp
    -
    -  notes:
    -    *setp and newelem must be defined
    -    *setp may be a temp set
    -    nth=0 is first element
    -    errors if nth is out of bounds
    -
    -  design:
    -    expand *setp if empty or full
    -    move tail of *setp up one
    -    insert newelem
    -*/
    -void qh_setaddnth(setT **setp, int nth, void *newelem) {
    -  int oldsize, i;
    -  setelemT *sizep;          /* avoid strict aliasing */
    -  setelemT *oldp, *newp;
    -
    -  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
    -    qh_setlarger(setp);
    -    sizep= SETsizeaddr_(*setp);
    -  }
    -  oldsize= sizep->i - 1;
    -  if (nth < 0 || nth > oldsize) {
    -    qh_fprintf(qhmem.ferr, 6171, "qhull internal error (qh_setaddnth): nth %d is out-of-bounds for set:\n", nth);
    -    qh_setprint(qhmem.ferr, "", *setp);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  sizep->i++;
    -  oldp= (setelemT *)SETelemaddr_(*setp, oldsize, void);   /* NULL */
    -  newp= oldp+1;
    -  for (i=oldsize-nth+1; i--; )  /* move at least NULL  */
    -    (newp--)->p= (oldp--)->p;       /* may overwrite *sizep */
    -  newp->p= newelem;
    -} /* setaddnth */
    -
    -
    -/*----------------------------------
    -
    -  setaddsorted( setp, newelem )
    -    adds an newelem into sorted *setp
    -
    -  notes:
    -    *setp and newelem must be defined
    -    *setp may be a temp set
    -    nop if newelem already in set
    -
    -  design:
    -    find newelem's position in *setp
    -    insert newelem
    -*/
    -void qh_setaddsorted(setT **setp, void *newelem) {
    -  int newindex=0;
    -  void *elem, **elemp;
    -
    -  FOREACHelem_(*setp) {          /* could use binary search instead */
    -    if (elem < newelem)
    -      newindex++;
    -    else if (elem == newelem)
    -      return;
    -    else
    -      break;
    -  }
    -  qh_setaddnth(setp, newindex, newelem);
    -} /* setaddsorted */
    -
    -
    -/*---------------------------------
    -
    -  qh_setappend( setp, newelem)
    -    append newelem to *setp
    -
    -  notes:
    -    *setp may be a temp set
    -    *setp and newelem may be NULL
    -
    -  design:
    -    expand *setp if empty or full
    -    append newelem to *setp
    -
    -*/
    -void qh_setappend(setT **setp, void *newelem) {
    -  setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
    -  setelemT *endp;
    -  int count;
    -
    -  if (!newelem)
    -    return;
    -  if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
    -    qh_setlarger(setp);
    -    sizep= SETsizeaddr_(*setp);
    -  }
    -  count= (sizep->i)++ - 1;
    -  endp= (setelemT *)SETelemaddr_(*setp, count, void);
    -  (endp++)->p= newelem;
    -  endp->p= NULL;
    -} /* setappend */
    -
    -/*---------------------------------
    -
    -  qh_setappend_set( setp, setA)
    -    appends setA to *setp
    -
    -  notes:
    -    *setp can not be a temp set
    -    *setp and setA may be NULL
    -
    -  design:
    -    setup for copy
    -    expand *setp if it is too small
    -    append all elements of setA to *setp
    -*/
    -void qh_setappend_set(setT **setp, setT *setA) {
    -  int sizeA, size;
    -  setT *oldset;
    -  setelemT *sizep;
    -
    -  if (!setA)
    -    return;
    -  SETreturnsize_(setA, sizeA);
    -  if (!*setp)
    -    *setp= qh_setnew(sizeA);
    -  sizep= SETsizeaddr_(*setp);
    -  if (!(size= sizep->i))
    -    size= (*setp)->maxsize;
    -  else
    -    size--;
    -  if (size + sizeA > (*setp)->maxsize) {
    -    oldset= *setp;
    -    *setp= qh_setcopy(oldset, sizeA);
    -    qh_setfree(&oldset);
    -    sizep= SETsizeaddr_(*setp);
    -  }
    -  if (sizeA > 0) {
    -    sizep->i= size+sizeA+1;   /* memcpy may overwrite */
    -    memcpy((char *)&((*setp)->e[size].p), (char *)&(setA->e[0].p), (size_t)(sizeA+1) * SETelemsize);
    -  }
    -} /* setappend_set */
    -
    -
    -/*---------------------------------
    -
    -  qh_setappend2ndlast( setp, newelem )
    -    makes newelem the next to the last element in *setp
    -
    -  notes:
    -    *setp must have at least one element
    -    newelem must be defined
    -    *setp may be a temp set
    -
    -  design:
    -    expand *setp if empty or full
    -    move last element of *setp up one
    -    insert newelem
    -*/
    -void qh_setappend2ndlast(setT **setp, void *newelem) {
    -    setelemT *sizep;  /* Avoid strict aliasing.  Writing to *endp may overwrite *sizep */
    -    setelemT *endp, *lastp;
    -    int count;
    -
    -    if (!*setp || (sizep= SETsizeaddr_(*setp))->i==0) {
    -        qh_setlarger(setp);
    -        sizep= SETsizeaddr_(*setp);
    -    }
    -    count= (sizep->i)++ - 1;
    -    endp= (setelemT *)SETelemaddr_(*setp, count, void); /* NULL */
    -    lastp= endp-1;
    -    *(endp++)= *lastp;
    -    endp->p= NULL;    /* may overwrite *sizep */
    -    lastp->p= newelem;
    -} /* setappend2ndlast */
    -
    -/*---------------------------------
    -
    -  qh_setcheck( set, typename, id )
    -    check set for validity
    -    report errors with typename and id
    -
    -  design:
    -    checks that maxsize, actual size, and NULL terminator agree
    -*/
    -void qh_setcheck(setT *set, const char *tname, unsigned id) {
    -  int maxsize, size;
    -  int waserr= 0;
    -
    -  if (!set)
    -    return;
    -  SETreturnsize_(set, size);
    -  maxsize= set->maxsize;
    -  if (size > maxsize || !maxsize) {
    -    qh_fprintf(qhmem.ferr, 6172, "qhull internal error (qh_setcheck): actual size %d of %s%d is greater than max size %d\n",
    -             size, tname, id, maxsize);
    -    waserr= 1;
    -  }else if (set->e[size].p) {
    -    qh_fprintf(qhmem.ferr, 6173, "qhull internal error (qh_setcheck): %s%d(size %d max %d) is not null terminated.\n",
    -             tname, id, size-1, maxsize);
    -    waserr= 1;
    -  }
    -  if (waserr) {
    -    qh_setprint(qhmem.ferr, "ERRONEOUS", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -} /* setcheck */
    -
    -
    -/*---------------------------------
    -
    -  qh_setcompact( set )
    -    remove internal NULLs from an unsorted set
    -
    -  returns:
    -    updated set
    -
    -  notes:
    -    set may be NULL
    -    it would be faster to swap tail of set into holes, like qh_setdel
    -
    -  design:
    -    setup pointers into set
    -    skip NULLs while copying elements to start of set
    -    update the actual size
    -*/
    -void qh_setcompact(setT *set) {
    -  int size;
    -  void **destp, **elemp, **endp, **firstp;
    -
    -  if (!set)
    -    return;
    -  SETreturnsize_(set, size);
    -  destp= elemp= firstp= SETaddr_(set, void);
    -  endp= destp + size;
    -  while (1) {
    -    if (!(*destp++ = *elemp++)) {
    -      destp--;
    -      if (elemp > endp)
    -        break;
    -    }
    -  }
    -  qh_settruncate(set, (int)(destp-firstp));   /* WARN64 */
    -} /* setcompact */
    -
    -
    -/*---------------------------------
    -
    -  qh_setcopy( set, extra )
    -    make a copy of a sorted or unsorted set with extra slots
    -
    -  returns:
    -    new set
    -
    -  design:
    -    create a newset with extra slots
    -    copy the elements to the newset
    -
    -*/
    -setT *qh_setcopy(setT *set, int extra) {
    -  setT *newset;
    -  int size;
    -
    -  if (extra < 0)
    -    extra= 0;
    -  SETreturnsize_(set, size);
    -  newset= qh_setnew(size+extra);
    -  SETsizeaddr_(newset)->i= size+1;    /* memcpy may overwrite */
    -  memcpy((char *)&(newset->e[0].p), (char *)&(set->e[0].p), (size_t)(size+1) * SETelemsize);
    -  return(newset);
    -} /* setcopy */
    -
    -
    -/*---------------------------------
    -
    -  qh_setdel( set, oldelem )
    -    delete oldelem from an unsorted set
    -
    -  returns:
    -    returns oldelem if found
    -    returns NULL otherwise
    -
    -  notes:
    -    set may be NULL
    -    oldelem must not be NULL;
    -    only deletes one copy of oldelem in set
    -
    -  design:
    -    locate oldelem
    -    update actual size if it was full
    -    move the last element to the oldelem's location
    -*/
    -void *qh_setdel(setT *set, void *oldelem) {
    -  setelemT *sizep;
    -  setelemT *elemp;
    -  setelemT *lastp;
    -
    -  if (!set)
    -    return NULL;
    -  elemp= (setelemT *)SETaddr_(set, void);
    -  while (elemp->p != oldelem && elemp->p)
    -    elemp++;
    -  if (elemp->p) {
    -    sizep= SETsizeaddr_(set);
    -    if (!(sizep->i)--)         /*  if was a full set */
    -      sizep->i= set->maxsize;  /*     *sizep= (maxsize-1)+ 1 */
    -    lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
    -    elemp->p= lastp->p;      /* may overwrite itself */
    -    lastp->p= NULL;
    -    return oldelem;
    -  }
    -  return NULL;
    -} /* setdel */
    -
    -
    -/*---------------------------------
    -
    -  qh_setdellast( set)
    -    return last element of set or NULL
    -
    -  notes:
    -    deletes element from set
    -    set may be NULL
    -
    -  design:
    -    return NULL if empty
    -    if full set
    -      delete last element and set actual size
    -    else
    -      delete last element and update actual size
    -*/
    -void *qh_setdellast(setT *set) {
    -  int setsize;  /* actually, actual_size + 1 */
    -  int maxsize;
    -  setelemT *sizep;
    -  void *returnvalue;
    -
    -  if (!set || !(set->e[0].p))
    -    return NULL;
    -  sizep= SETsizeaddr_(set);
    -  if ((setsize= sizep->i)) {
    -    returnvalue= set->e[setsize - 2].p;
    -    set->e[setsize - 2].p= NULL;
    -    sizep->i--;
    -  }else {
    -    maxsize= set->maxsize;
    -    returnvalue= set->e[maxsize - 1].p;
    -    set->e[maxsize - 1].p= NULL;
    -    sizep->i= maxsize;
    -  }
    -  return returnvalue;
    -} /* setdellast */
    -
    -
    -/*---------------------------------
    -
    -  qh_setdelnth( set, nth )
    -    deletes nth element from unsorted set
    -    0 is first element
    -
    -  returns:
    -    returns the element (needs type conversion)
    -
    -  notes:
    -    errors if nth invalid
    -
    -  design:
    -    setup points and check nth
    -    delete nth element and overwrite with last element
    -*/
    -void *qh_setdelnth(setT *set, int nth) {
    -  void *elem;
    -  setelemT *sizep;
    -  setelemT *elemp, *lastp;
    -
    -  elemp= (setelemT *)SETelemaddr_(set, nth, void);
    -  sizep= SETsizeaddr_(set);
    -  if ((sizep->i--)==0)         /*  if was a full set */
    -    sizep->i= set->maxsize;  /*     *sizep= (maxsize-1)+ 1 */
    -  if (nth < 0 || nth >= sizep->i) {
    -    qh_fprintf(qhmem.ferr, 6174, "qhull internal error (qh_setdelnth): nth %d is out-of-bounds for set:\n", nth);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  lastp= (setelemT *)SETelemaddr_(set, sizep->i-1, void);
    -  elem= elemp->p;
    -  elemp->p= lastp->p;      /* may overwrite itself */
    -  lastp->p= NULL;
    -  return elem;
    -} /* setdelnth */
    -
    -/*---------------------------------
    -
    -  qh_setdelnthsorted( set, nth )
    -    deletes nth element from sorted set
    -
    -  returns:
    -    returns the element (use type conversion)
    -
    -  notes:
    -    errors if nth invalid
    -
    -  see also:
    -    setnew_delnthsorted
    -
    -  design:
    -    setup points and check nth
    -    copy remaining elements down one
    -    update actual size
    -*/
    -void *qh_setdelnthsorted(setT *set, int nth) {
    -  void *elem;
    -  setelemT *sizep;
    -  setelemT *newp, *oldp;
    -
    -  sizep= SETsizeaddr_(set);
    -  if (nth < 0 || (sizep->i && nth >= sizep->i-1) || nth >= set->maxsize) {
    -    qh_fprintf(qhmem.ferr, 6175, "qhull internal error (qh_setdelnthsorted): nth %d is out-of-bounds for set:\n", nth);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  newp= (setelemT *)SETelemaddr_(set, nth, void);
    -  elem= newp->p;
    -  oldp= newp+1;
    -  while (((newp++)->p= (oldp++)->p))
    -    ; /* copy remaining elements and NULL */
    -  if ((sizep->i--)==0)         /*  if was a full set */
    -    sizep->i= set->maxsize;  /*     *sizep= (max size-1)+ 1 */
    -  return elem;
    -} /* setdelnthsorted */
    -
    -
    -/*---------------------------------
    -
    -  qh_setdelsorted( set, oldelem )
    -    deletes oldelem from sorted set
    -
    -  returns:
    -    returns oldelem if it was deleted
    -
    -  notes:
    -    set may be NULL
    -
    -  design:
    -    locate oldelem in set
    -    copy remaining elements down one
    -    update actual size
    -*/
    -void *qh_setdelsorted(setT *set, void *oldelem) {
    -  setelemT *sizep;
    -  setelemT *newp, *oldp;
    -
    -  if (!set)
    -    return NULL;
    -  newp= (setelemT *)SETaddr_(set, void);
    -  while(newp->p != oldelem && newp->p)
    -    newp++;
    -  if (newp->p) {
    -    oldp= newp+1;
    -    while (((newp++)->p= (oldp++)->p))
    -      ; /* copy remaining elements */
    -    sizep= SETsizeaddr_(set);
    -    if ((sizep->i--)==0)    /*  if was a full set */
    -      sizep->i= set->maxsize;  /*     *sizep= (max size-1)+ 1 */
    -    return oldelem;
    -  }
    -  return NULL;
    -} /* setdelsorted */
    -
    -
    -/*---------------------------------
    -
    -  qh_setduplicate( set, elemsize )
    -    duplicate a set of elemsize elements
    -
    -  notes:
    -    use setcopy if retaining old elements
    -
    -  design:
    -    create a new set
    -    for each elem of the old set
    -      create a newelem
    -      append newelem to newset
    -*/
    -setT *qh_setduplicate(setT *set, int elemsize) {
    -  void          *elem, **elemp, *newElem;
    -  setT          *newSet;
    -  int           size;
    -
    -  if (!(size= qh_setsize(set)))
    -    return NULL;
    -  newSet= qh_setnew(size);
    -  FOREACHelem_(set) {
    -    newElem= qh_memalloc(elemsize);
    -    memcpy(newElem, elem, (size_t)elemsize);
    -    qh_setappend(&newSet, newElem);
    -  }
    -  return newSet;
    -} /* setduplicate */
    -
    -
    -/*---------------------------------
    -
    -  qh_setendpointer( set )
    -    Returns pointer to NULL terminator of a set's elements
    -    set can not be NULL
    -
    -*/
    -void **qh_setendpointer(setT *set) {
    -
    -  setelemT *sizep= SETsizeaddr_(set);
    -  int n= sizep->i;
    -  return (n ? &set->e[n-1].p : &sizep->p);
    -} /* qh_setendpointer */
    -
    -/*---------------------------------
    -
    -  qh_setequal(  )
    -    returns 1 if two sorted sets are equal, otherwise returns 0
    -
    -  notes:
    -    either set may be NULL
    -
    -  design:
    -    check size of each set
    -    setup pointers
    -    compare elements of each set
    -*/
    -int qh_setequal(setT *setA, setT *setB) {
    -  void **elemAp, **elemBp;
    -  int sizeA= 0, sizeB= 0;
    -
    -  if (setA) {
    -    SETreturnsize_(setA, sizeA);
    -  }
    -  if (setB) {
    -    SETreturnsize_(setB, sizeB);
    -  }
    -  if (sizeA != sizeB)
    -    return 0;
    -  if (!sizeA)
    -    return 1;
    -  elemAp= SETaddr_(setA, void);
    -  elemBp= SETaddr_(setB, void);
    -  if (!memcmp((char *)elemAp, (char *)elemBp, sizeA*SETelemsize))
    -    return 1;
    -  return 0;
    -} /* setequal */
    -
    -
    -/*---------------------------------
    -
    -  qh_setequal_except( setA, skipelemA, setB, skipelemB )
    -    returns 1 if sorted setA and setB are equal except for skipelemA & B
    -
    -  returns:
    -    false if either skipelemA or skipelemB are missing
    -
    -  notes:
    -    neither set may be NULL
    -
    -    if skipelemB is NULL,
    -      can skip any one element of setB
    -
    -  design:
    -    setup pointers
    -    search for skipelemA, skipelemB, and mismatches
    -    check results
    -*/
    -int qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB) {
    -  void **elemA, **elemB;
    -  int skip=0;
    -
    -  elemA= SETaddr_(setA, void);
    -  elemB= SETaddr_(setB, void);
    -  while (1) {
    -    if (*elemA == skipelemA) {
    -      skip++;
    -      elemA++;
    -    }
    -    if (skipelemB) {
    -      if (*elemB == skipelemB) {
    -        skip++;
    -        elemB++;
    -      }
    -    }else if (*elemA != *elemB) {
    -      skip++;
    -      if (!(skipelemB= *elemB++))
    -        return 0;
    -    }
    -    if (!*elemA)
    -      break;
    -    if (*elemA++ != *elemB++)
    -      return 0;
    -  }
    -  if (skip != 2 || *elemB)
    -    return 0;
    -  return 1;
    -} /* setequal_except */
    -
    -
    -/*---------------------------------
    -
    -  qh_setequal_skip( setA, skipA, setB, skipB )
    -    returns 1 if sorted setA and setB are equal except for elements skipA & B
    -
    -  returns:
    -    false if different size
    -
    -  notes:
    -    neither set may be NULL
    -
    -  design:
    -    setup pointers
    -    search for mismatches while skipping skipA and skipB
    -*/
    -int qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB) {
    -  void **elemA, **elemB, **skipAp, **skipBp;
    -
    -  elemA= SETaddr_(setA, void);
    -  elemB= SETaddr_(setB, void);
    -  skipAp= SETelemaddr_(setA, skipA, void);
    -  skipBp= SETelemaddr_(setB, skipB, void);
    -  while (1) {
    -    if (elemA == skipAp)
    -      elemA++;
    -    if (elemB == skipBp)
    -      elemB++;
    -    if (!*elemA)
    -      break;
    -    if (*elemA++ != *elemB++)
    -      return 0;
    -  }
    -  if (*elemB)
    -    return 0;
    -  return 1;
    -} /* setequal_skip */
    -
    -
    -/*---------------------------------
    -
    -  qh_setfree( setp )
    -    frees the space occupied by a sorted or unsorted set
    -
    -  returns:
    -    sets setp to NULL
    -
    -  notes:
    -    set may be NULL
    -
    -  design:
    -    free array
    -    free set
    -*/
    -void qh_setfree(setT **setp) {
    -  int size;
    -  void **freelistp;  /* used !qh_NOmem */
    -
    -  if (*setp) {
    -    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    -    if (size <= qhmem.LASTsize) {
    -      qh_memfree_(*setp, size, freelistp);
    -    }else
    -      qh_memfree(*setp, size);
    -    *setp= NULL;
    -  }
    -} /* setfree */
    -
    -
    -/*---------------------------------
    -
    -  qh_setfree2( setp, elemsize )
    -    frees the space occupied by a set and its elements
    -
    -  notes:
    -    set may be NULL
    -
    -  design:
    -    free each element
    -    free set
    -*/
    -void qh_setfree2 (setT **setp, int elemsize) {
    -  void          *elem, **elemp;
    -
    -  FOREACHelem_(*setp)
    -    qh_memfree(elem, elemsize);
    -  qh_setfree(setp);
    -} /* setfree2 */
    -
    -
    -
    -/*---------------------------------
    -
    -  qh_setfreelong( setp )
    -    frees a set only if it's in long memory
    -
    -  returns:
    -    sets setp to NULL if it is freed
    -
    -  notes:
    -    set may be NULL
    -
    -  design:
    -    if set is large
    -      free it
    -*/
    -void qh_setfreelong(setT **setp) {
    -  int size;
    -
    -  if (*setp) {
    -    size= sizeof(setT) + ((*setp)->maxsize)*SETelemsize;
    -    if (size > qhmem.LASTsize) {
    -      qh_memfree(*setp, size);
    -      *setp= NULL;
    -    }
    -  }
    -} /* setfreelong */
    -
    -
    -/*---------------------------------
    -
    -  qh_setin( set, setelem )
    -    returns 1 if setelem is in a set, 0 otherwise
    -
    -  notes:
    -    set may be NULL or unsorted
    -
    -  design:
    -    scans set for setelem
    -*/
    -int qh_setin(setT *set, void *setelem) {
    -  void *elem, **elemp;
    -
    -  FOREACHelem_(set) {
    -    if (elem == setelem)
    -      return 1;
    -  }
    -  return 0;
    -} /* setin */
    -
    -
    -/*---------------------------------
    -
    -  qh_setindex( set, atelem )
    -    returns the index of atelem in set.
    -    returns -1, if not in set or maxsize wrong
    -
    -  notes:
    -    set may be NULL and may contain nulls.
    -    NOerrors returned (qh_pointid, QhullPoint::id)
    -
    -  design:
    -    checks maxsize
    -    scans set for atelem
    -*/
    -int qh_setindex(setT *set, void *atelem) {
    -  void **elem;
    -  int size, i;
    -
    -  if (!set)
    -    return -1;
    -  SETreturnsize_(set, size);
    -  if (size > set->maxsize)
    -    return -1;
    -  elem= SETaddr_(set, void);
    -  for (i=0; i < size; i++) {
    -    if (*elem++ == atelem)
    -      return i;
    -  }
    -  return -1;
    -} /* setindex */
    -
    -
    -/*---------------------------------
    -
    -  qh_setlarger( oldsetp )
    -    returns a larger set that contains all elements of *oldsetp
    -
    -  notes:
    -    the set is at least twice as large
    -    if temp set, updates qhmem.tempstack
    -
    -  design:
    -    creates a new set
    -    copies the old set to the new set
    -    updates pointers in tempstack
    -    deletes the old set
    -*/
    -void qh_setlarger(setT **oldsetp) {
    -  int size= 1;
    -  setT *newset, *set, **setp, *oldset;
    -  setelemT *sizep;
    -  setelemT *newp, *oldp;
    -
    -  if (*oldsetp) {
    -    oldset= *oldsetp;
    -    SETreturnsize_(oldset, size);
    -    qhmem.cntlarger++;
    -    qhmem.totlarger += size+1;
    -    newset= qh_setnew(2 * size);
    -    oldp= (setelemT *)SETaddr_(oldset, void);
    -    newp= (setelemT *)SETaddr_(newset, void);
    -    memcpy((char *)newp, (char *)oldp, (size_t)(size+1) * SETelemsize);
    -    sizep= SETsizeaddr_(newset);
    -    sizep->i= size+1;
    -    FOREACHset_((setT *)qhmem.tempstack) {
    -      if (set == oldset)
    -        *(setp-1)= newset;
    -    }
    -    qh_setfree(oldsetp);
    -  }else
    -    newset= qh_setnew(3);
    -  *oldsetp= newset;
    -} /* setlarger */
    -
    -
    -/*---------------------------------
    -
    -  qh_setlast(  )
    -    return last element of set or NULL (use type conversion)
    -
    -  notes:
    -    set may be NULL
    -
    -  design:
    -    return last element
    -*/
    -void *qh_setlast(setT *set) {
    -  int size;
    -
    -  if (set) {
    -    size= SETsizeaddr_(set)->i;
    -    if (!size)
    -      return SETelem_(set, set->maxsize - 1);
    -    else if (size > 1)
    -      return SETelem_(set, size - 2);
    -  }
    -  return NULL;
    -} /* setlast */
    -
    -
    -/*---------------------------------
    -
    -  qh_setnew( setsize )
    -    creates and allocates space for a set
    -
    -  notes:
    -    setsize means the number of elements (!including the NULL terminator)
    -    use qh_settemp/qh_setfreetemp if set is temporary
    -
    -  design:
    -    allocate memory for set
    -    roundup memory if small set
    -    initialize as empty set
    -*/
    -setT *qh_setnew(int setsize) {
    -  setT *set;
    -  int sizereceived; /* used !qh_NOmem */
    -  int size;
    -  void **freelistp; /* used !qh_NOmem */
    -
    -  if (!setsize)
    -    setsize++;
    -  size= sizeof(setT) + setsize * SETelemsize;
    -  if (size>0 && size <= qhmem.LASTsize) {
    -    qh_memalloc_(size, freelistp, set, setT);
    -#ifndef qh_NOmem
    -    sizereceived= qhmem.sizetable[ qhmem.indextable[size]];
    -    if (sizereceived > size)
    -      setsize += (sizereceived - size)/SETelemsize;
    -#endif
    -  }else
    -    set= (setT*)qh_memalloc(size);
    -  set->maxsize= setsize;
    -  set->e[setsize].i= 1;
    -  set->e[0].p= NULL;
    -  return(set);
    -} /* setnew */
    -
    -
    -/*---------------------------------
    -
    -  qh_setnew_delnthsorted( set, size, nth, prepend )
    -    creates a sorted set not containing nth element
    -    if prepend, the first prepend elements are undefined
    -
    -  notes:
    -    set must be defined
    -    checks nth
    -    see also: setdelnthsorted
    -
    -  design:
    -    create new set
    -    setup pointers and allocate room for prepend'ed entries
    -    append head of old set to new set
    -    append tail of old set to new set
    -*/
    -setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend) {
    -  setT *newset;
    -  void **oldp, **newp;
    -  int tailsize= size - nth -1, newsize;
    -
    -  if (tailsize < 0) {
    -    qh_fprintf(qhmem.ferr, 6176, "qhull internal error (qh_setnew_delnthsorted): nth %d is out-of-bounds for set:\n", nth);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  newsize= size-1 + prepend;
    -  newset= qh_setnew(newsize);
    -  newset->e[newset->maxsize].i= newsize+1;  /* may be overwritten */
    -  oldp= SETaddr_(set, void);
    -  newp= SETaddr_(newset, void) + prepend;
    -  switch (nth) {
    -  case 0:
    -    break;
    -  case 1:
    -    *(newp++)= *oldp++;
    -    break;
    -  case 2:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  case 3:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  case 4:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  default:
    -    memcpy((char *)newp, (char *)oldp, (size_t)nth * SETelemsize);
    -    newp += nth;
    -    oldp += nth;
    -    break;
    -  }
    -  oldp++;
    -  switch (tailsize) {
    -  case 0:
    -    break;
    -  case 1:
    -    *(newp++)= *oldp++;
    -    break;
    -  case 2:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  case 3:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  case 4:
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    *(newp++)= *oldp++;
    -    break;
    -  default:
    -    memcpy((char *)newp, (char *)oldp, (size_t)tailsize * SETelemsize);
    -    newp += tailsize;
    -  }
    -  *newp= NULL;
    -  return(newset);
    -} /* setnew_delnthsorted */
    -
    -
    -/*---------------------------------
    -
    -  qh_setprint( fp, string, set )
    -    print set elements to fp with identifying string
    -
    -  notes:
    -    never errors
    -*/
    -void qh_setprint(FILE *fp, const char* string, setT *set) {
    -  int size, k;
    -
    -  if (!set)
    -    qh_fprintf(fp, 9346, "%s set is null\n", string);
    -  else {
    -    SETreturnsize_(set, size);
    -    qh_fprintf(fp, 9347, "%s set=%p maxsize=%d size=%d elems=",
    -             string, set, set->maxsize, size);
    -    if (size > set->maxsize)
    -      size= set->maxsize+1;
    -    for (k=0; k < size; k++)
    -      qh_fprintf(fp, 9348, " %p", set->e[k].p);
    -    qh_fprintf(fp, 9349, "\n");
    -  }
    -} /* setprint */
    -
    -/*---------------------------------
    -
    -  qh_setreplace( set, oldelem, newelem )
    -    replaces oldelem in set with newelem
    -
    -  notes:
    -    errors if oldelem not in the set
    -    newelem may be NULL, but it turns the set into an indexed set (no FOREACH)
    -
    -  design:
    -    find oldelem
    -    replace with newelem
    -*/
    -void qh_setreplace(setT *set, void *oldelem, void *newelem) {
    -  void **elemp;
    -
    -  elemp= SETaddr_(set, void);
    -  while (*elemp != oldelem && *elemp)
    -    elemp++;
    -  if (*elemp)
    -    *elemp= newelem;
    -  else {
    -    qh_fprintf(qhmem.ferr, 6177, "qhull internal error (qh_setreplace): elem %p not found in set\n",
    -       oldelem);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -} /* setreplace */
    -
    -
    -/*---------------------------------
    -
    -  qh_setsize( set )
    -    returns the size of a set
    -
    -  notes:
    -    errors if set's maxsize is incorrect
    -    same as SETreturnsize_(set)
    -    same code for qh_setsize [qset.c] and QhullSetBase::count
    -
    -  design:
    -    determine actual size of set from maxsize
    -*/
    -int qh_setsize(setT *set) {
    -  int size;
    -  setelemT *sizep;
    -
    -  if (!set)
    -    return(0);
    -  sizep= SETsizeaddr_(set);
    -  if ((size= sizep->i)) {
    -    size--;
    -    if (size > set->maxsize) {
    -      qh_fprintf(qhmem.ferr, 6178, "qhull internal error (qh_setsize): current set size %d is greater than maximum size %d\n",
    -               size, set->maxsize);
    -      qh_setprint(qhmem.ferr, "set: ", set);
    -      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -    }
    -  }else
    -    size= set->maxsize;
    -  return size;
    -} /* setsize */
    -
    -/*---------------------------------
    -
    -  qh_settemp( setsize )
    -    return a stacked, temporary set of upto setsize elements
    -
    -  notes:
    -    use settempfree or settempfree_all to release from qhmem.tempstack
    -    see also qh_setnew
    -
    -  design:
    -    allocate set
    -    append to qhmem.tempstack
    -
    -*/
    -setT *qh_settemp(int setsize) {
    -  setT *newset;
    -
    -  newset= qh_setnew(setsize);
    -  qh_setappend(&qhmem.tempstack, newset);
    -  if (qhmem.IStracing >= 5)
    -    qh_fprintf(qhmem.ferr, 8123, "qh_settemp: temp set %p of %d elements, depth %d\n",
    -       newset, newset->maxsize, qh_setsize(qhmem.tempstack));
    -  return newset;
    -} /* settemp */
    -
    -/*---------------------------------
    -
    -  qh_settempfree( set )
    -    free temporary set at top of qhmem.tempstack
    -
    -  notes:
    -    nop if set is NULL
    -    errors if set not from previous   qh_settemp
    -
    -  to locate errors:
    -    use 'T2' to find source and then find mis-matching qh_settemp
    -
    -  design:
    -    check top of qhmem.tempstack
    -    free it
    -*/
    -void qh_settempfree(setT **set) {
    -  setT *stackedset;
    -
    -  if (!*set)
    -    return;
    -  stackedset= qh_settemppop();
    -  if (stackedset != *set) {
    -    qh_settemppush(stackedset);
    -    qh_fprintf(qhmem.ferr, 6179, "qhull internal error (qh_settempfree): set %p(size %d) was not last temporary allocated(depth %d, set %p, size %d)\n",
    -             *set, qh_setsize(*set), qh_setsize(qhmem.tempstack)+1,
    -             stackedset, qh_setsize(stackedset));
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  qh_setfree(set);
    -} /* settempfree */
    -
    -/*---------------------------------
    -
    -  qh_settempfree_all(  )
    -    free all temporary sets in qhmem.tempstack
    -
    -  design:
    -    for each set in tempstack
    -      free set
    -    free qhmem.tempstack
    -*/
    -void qh_settempfree_all(void) {
    -  setT *set, **setp;
    -
    -  FOREACHset_(qhmem.tempstack)
    -    qh_setfree(&set);
    -  qh_setfree(&qhmem.tempstack);
    -} /* settempfree_all */
    -
    -/*---------------------------------
    -
    -  qh_settemppop(  )
    -    pop and return temporary set from qhmem.tempstack
    -
    -  notes:
    -    the returned set is permanent
    -
    -  design:
    -    pop and check top of qhmem.tempstack
    -*/
    -setT *qh_settemppop(void) {
    -  setT *stackedset;
    -
    -  stackedset= (setT*)qh_setdellast(qhmem.tempstack);
    -  if (!stackedset) {
    -    qh_fprintf(qhmem.ferr, 6180, "qhull internal error (qh_settemppop): pop from empty temporary stack\n");
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  if (qhmem.IStracing >= 5)
    -    qh_fprintf(qhmem.ferr, 8124, "qh_settemppop: depth %d temp set %p of %d elements\n",
    -       qh_setsize(qhmem.tempstack)+1, stackedset, qh_setsize(stackedset));
    -  return stackedset;
    -} /* settemppop */
    -
    -/*---------------------------------
    -
    -  qh_settemppush( set )
    -    push temporary set unto qhmem.tempstack (makes it temporary)
    -
    -  notes:
    -    duplicates settemp() for tracing
    -
    -  design:
    -    append set to tempstack
    -*/
    -void qh_settemppush(setT *set) {
    -    if (!set) {
    -        fprintf (qhmem.ferr, "qhull error (qh_settemppush): can not push a NULL temp\n");
    -        qh_errexit (qhmem_ERRqhull, NULL, NULL);
    -    }
    -  qh_setappend(&qhmem.tempstack, set);
    -  if (qhmem.IStracing >= 5)
    -    qh_fprintf(qhmem.ferr, 8125, "qh_settemppush: depth %d temp set %p of %d elements\n",
    -      qh_setsize(qhmem.tempstack), set, qh_setsize(set));
    -} /* settemppush */
    -
    -
    -/*---------------------------------
    -
    -  qh_settruncate( set, size )
    -    truncate set to size elements
    -
    -  notes:
    -    set must be defined
    -
    -  see:
    -    SETtruncate_
    -
    -  design:
    -    check size
    -    update actual size of set
    -*/
    -void qh_settruncate(setT *set, int size) {
    -
    -  if (size < 0 || size > set->maxsize) {
    -    qh_fprintf(qhmem.ferr, 6181, "qhull internal error (qh_settruncate): size %d out of bounds for set:\n", size);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  set->e[set->maxsize].i= size+1;   /* maybe overwritten */
    -  set->e[size].p= NULL;
    -} /* settruncate */
    -
    -/*---------------------------------
    -
    -  qh_setunique( set, elem )
    -    add elem to unsorted set unless it is already in set
    -
    -  notes:
    -    returns 1 if it is appended
    -
    -  design:
    -    if elem not in set
    -      append elem to set
    -*/
    -int qh_setunique(setT **set, void *elem) {
    -
    -  if (!qh_setin(*set, elem)) {
    -    qh_setappend(set, elem);
    -    return 1;
    -  }
    -  return 0;
    -} /* setunique */
    -
    -/*---------------------------------
    -
    -  qh_setzero( set, index, size )
    -    zero elements from index on
    -    set actual size of set to size
    -
    -  notes:
    -    set must be defined
    -    the set becomes an indexed set (can not use FOREACH...)
    -
    -  see also:
    -    qh_settruncate
    -
    -  design:
    -    check index and size
    -    update actual size
    -    zero elements starting at e[index]
    -*/
    -void qh_setzero(setT *set, int idx, int size) {
    -  int count;
    -
    -  if (idx < 0 || idx >= size || size > set->maxsize) {
    -    qh_fprintf(qhmem.ferr, 6182, "qhull internal error (qh_setzero): index %d or size %d out of bounds for set:\n", idx, size);
    -    qh_setprint(qhmem.ferr, "", set);
    -    qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -  }
    -  set->e[set->maxsize].i=  size+1;  /* may be overwritten */
    -  count= size - idx + 1;   /* +1 for NULL terminator */
    -  memset((char *)SETelemaddr_(set, idx, void), 0, (size_t)count * SETelemsize);
    -} /* setzero */
    diff --git a/extern/qhull/qset.h b/extern/qhull/qset.h
    deleted file mode 100644
    index 759b501a8017..000000000000
    --- a/extern/qhull/qset.h
    +++ /dev/null
    @@ -1,488 +0,0 @@
    -/*
      ---------------------------------
    -
    -   qset.h
    -     header file for qset.c that implements set
    -
    -   see qh-set.htm and qset.c
    -
    -   only uses mem.c, malloc/free
    -
    -   for error handling, writes message and calls
    -      qh_errexit(qhmem_ERRqhull, NULL, NULL);
    -
    -   set operations satisfy the following properties:
    -    - sets have a max size, the actual size (if different) is stored at the end
    -    - every set is NULL terminated
    -    - sets may be sorted or unsorted, the caller must distinguish this
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/qset.h#4 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFset
    -#define qhDEFset 1
    -
    -#include 
    -
    -/*================= -structures- ===============*/
    -
    -#ifndef DEFsetT
    -#define DEFsetT 1
    -typedef struct setT setT;   /* a set is a sorted or unsorted array of pointers */
    -#endif
    -
    -/*------------------------------------------
    -
    -setT
    -  a set or list of pointers with maximum size and actual size.
    -
    -variations:
    -  unsorted, unique   -- a list of unique pointers with NULL terminator
    -                           user guarantees uniqueness
    -  sorted             -- a sorted list of unique pointers with NULL terminator
    -                           qset.c guarantees uniqueness
    -  unsorted           -- a list of pointers terminated with NULL
    -  indexed            -- an array of pointers with NULL elements
    -
    -structure for set of n elements:
    -
    -        --------------
    -        |  maxsize
    -        --------------
    -        |  e[0] - a pointer, may be NULL for indexed sets
    -        --------------
    -        |  e[1]
    -
    -        --------------
    -        |  ...
    -        --------------
    -        |  e[n-1]
    -        --------------
    -        |  e[n] = NULL
    -        --------------
    -        |  ...
    -        --------------
    -        |  e[maxsize] - n+1 or NULL (determines actual size of set)
    -        --------------
    -
    -*/
    -
    -/*-- setelemT -- internal type to allow both pointers and indices
    -*/
    -typedef union setelemT setelemT;
    -union setelemT {
    -  void    *p;
    -  int      i;         /* integer used for e[maxSize] */
    -};
    -
    -struct setT {
    -  int maxsize;          /* maximum number of elements (except NULL) */
    -  setelemT e[1];        /* array of pointers, tail is NULL */
    -                        /* last slot (unless NULL) is actual size+1
    -                           e[maxsize]==NULL or e[e[maxsize]-1]==NULL */
    -                        /* this may generate a warning since e[] contains
    -                           maxsize elements */
    -};
    -
    -/*=========== -constants- =========================*/
    -
    -/*-------------------------------------
    -
    -  SETelemsize
    -    size of a set element in bytes
    -*/
    -#define SETelemsize ((int)sizeof(setelemT))
    -
    -
    -/*=========== -macros- =========================*/
    -
    -/*-------------------------------------
    -
    -   FOREACHsetelement_(type, set, variable)
    -     define FOREACH iterator
    -
    -   declare:
    -     assumes *variable and **variablep are declared
    -     no space in "variable)" [DEC Alpha cc compiler]
    -
    -   each iteration:
    -     variable is set element
    -     variablep is one beyond variable.
    -
    -   to repeat an element:
    -     variablep--; / *repeat* /
    -
    -   at exit:
    -     variable is NULL at end of loop
    -
    -   example:
    -     #define FOREACHfacet_( facets ) FOREACHsetelement_( facetT, facets, facet )
    -
    -   notes:
    -     use FOREACHsetelement_i_() if need index or include NULLs
    -
    -   WARNING:
    -     nested loops can't use the same variable (define another FOREACH)
    -
    -     needs braces if nested inside another FOREACH
    -     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    -*/
    -#define FOREACHsetelement_(type, set, variable) \
    -        if (((variable= NULL), set)) for (\
    -          variable##p= (type **)&((set)->e[0].p); \
    -          (variable= *variable##p++);)
    -
    -/*------------------------------------------
    -
    -   FOREACHsetelement_i_(type, set, variable)
    -     define indexed FOREACH iterator
    -
    -   declare:
    -     type *variable, variable_n, variable_i;
    -
    -   each iteration:
    -     variable is set element, may be NULL
    -     variable_i is index, variable_n is qh_setsize()
    -
    -   to repeat an element:
    -     variable_i--; variable_n-- repeats for deleted element
    -
    -   at exit:
    -     variable==NULL and variable_i==variable_n
    -
    -   example:
    -     #define FOREACHfacet_i_( facets ) FOREACHsetelement_i_( facetT, facets, facet )
    -
    -   WARNING:
    -     nested loops can't use the same variable (define another FOREACH)
    -
    -     needs braces if nested inside another FOREACH
    -     this includes intervening blocks, e.g. FOREACH...{ if () FOREACH...} )
    -*/
    -#define FOREACHsetelement_i_(type, set, variable) \
    -        if (((variable= NULL), set)) for (\
    -          variable##_i= 0, variable= (type *)((set)->e[0].p), \
    -                   variable##_n= qh_setsize(set);\
    -          variable##_i < variable##_n;\
    -          variable= (type *)((set)->e[++variable##_i].p) )
    -
    -/*----------------------------------------
    -
    -   FOREACHsetelementreverse_(type, set, variable)-
    -     define FOREACH iterator in reverse order
    -
    -   declare:
    -     assumes *variable and **variablep are declared
    -     also declare 'int variabletemp'
    -
    -   each iteration:
    -     variable is set element
    -
    -   to repeat an element:
    -     variabletemp++; / *repeat* /
    -
    -   at exit:
    -     variable is NULL
    -
    -   example:
    -     #define FOREACHvertexreverse_( vertices ) FOREACHsetelementreverse_( vertexT, vertices, vertex )
    -
    -   notes:
    -     use FOREACHsetelementreverse12_() to reverse first two elements
    -     WARNING: needs braces if nested inside another FOREACH
    -*/
    -#define FOREACHsetelementreverse_(type, set, variable) \
    -        if (((variable= NULL), set)) for (\
    -           variable##temp= qh_setsize(set)-1, variable= qh_setlast(set);\
    -           variable; variable= \
    -           ((--variable##temp >= 0) ? SETelemt_(set, variable##temp, type) : NULL))
    -
    -/*-------------------------------------
    -
    -   FOREACHsetelementreverse12_(type, set, variable)-
    -     define FOREACH iterator with e[1] and e[0] reversed
    -
    -   declare:
    -     assumes *variable and **variablep are declared
    -
    -   each iteration:
    -     variable is set element
    -     variablep is one after variable.
    -
    -   to repeat an element:
    -     variablep--; / *repeat* /
    -
    -   at exit:
    -     variable is NULL at end of loop
    -
    -   example
    -     #define FOREACHvertexreverse12_( vertices ) FOREACHsetelementreverse12_( vertexT, vertices, vertex )
    -
    -   notes:
    -     WARNING: needs braces if nested inside another FOREACH
    -*/
    -#define FOREACHsetelementreverse12_(type, set, variable) \
    -        if (((variable= NULL), set)) for (\
    -          variable##p= (type **)&((set)->e[1].p); \
    -          (variable= *variable##p); \
    -          variable##p == ((type **)&((set)->e[0].p))?variable##p += 2: \
    -              (variable##p == ((type **)&((set)->e[1].p))?variable##p--:variable##p++))
    -
    -/*-------------------------------------
    -
    -   FOREACHelem_( set )-
    -     iterate elements in a set
    -
    -   declare:
    -     void *elem, *elemp;
    -
    -   each iteration:
    -     elem is set element
    -     elemp is one beyond
    -
    -   to repeat an element:
    -     elemp--; / *repeat* /
    -
    -   at exit:
    -     elem == NULL at end of loop
    -
    -   example:
    -     FOREACHelem_(set) {
    -
    -   notes:
    -     WARNING: needs braces if nested inside another FOREACH
    -*/
    -#define FOREACHelem_(set) FOREACHsetelement_(void, set, elem)
    -
    -/*-------------------------------------
    -
    -   FOREACHset_( set )-
    -     iterate a set of sets
    -
    -   declare:
    -     setT *set, **setp;
    -
    -   each iteration:
    -     set is set element
    -     setp is one beyond
    -
    -   to repeat an element:
    -     setp--; / *repeat* /
    -
    -   at exit:
    -     set == NULL at end of loop
    -
    -   example
    -     FOREACHset_(sets) {
    -
    -   notes:
    -     WARNING: needs braces if nested inside another FOREACH
    -*/
    -#define FOREACHset_(sets) FOREACHsetelement_(setT, sets, set)
    -
    -/*-------------------------------------------
    -
    -   SETindex_( set, elem )
    -     return index of elem in set
    -
    -   notes:
    -     for use with FOREACH iteration
    -     WARN64 -- Maximum set size is 2G
    -
    -   example:
    -     i= SETindex_(ridges, ridge)
    -*/
    -#define SETindex_(set, elem) ((int)((void **)elem##p - (void **)&(set)->e[1].p))
    -
    -/*-----------------------------------------
    -
    -   SETref_( elem )
    -     l.h.s. for modifying the current element in a FOREACH iteration
    -
    -   example:
    -     SETref_(ridge)= anotherridge;
    -*/
    -#define SETref_(elem) (elem##p[-1])
    -
    -/*-----------------------------------------
    -
    -   SETelem_(set, n)
    -     return the n'th element of set
    -
    -   notes:
    -      assumes that n is valid [0..size] and that set is defined
    -      use SETelemt_() for type cast
    -*/
    -#define SETelem_(set, n)           ((set)->e[n].p)
    -
    -/*-----------------------------------------
    -
    -   SETelemt_(set, n, type)
    -     return the n'th element of set as a type
    -
    -   notes:
    -      assumes that n is valid [0..size] and that set is defined
    -*/
    -#define SETelemt_(set, n, type)    ((type*)((set)->e[n].p))
    -
    -/*-----------------------------------------
    -
    -   SETelemaddr_(set, n, type)
    -     return address of the n'th element of a set
    -
    -   notes:
    -      assumes that n is valid [0..size] and set is defined
    -*/
    -#define SETelemaddr_(set, n, type) ((type **)(&((set)->e[n].p)))
    -
    -/*-----------------------------------------
    -
    -   SETfirst_(set)
    -     return first element of set
    -
    -*/
    -#define SETfirst_(set)             ((set)->e[0].p)
    -
    -/*-----------------------------------------
    -
    -   SETfirstt_(set, type)
    -     return first element of set as a type
    -
    -*/
    -#define SETfirstt_(set, type)      ((type*)((set)->e[0].p))
    -
    -/*-----------------------------------------
    -
    -   SETsecond_(set)
    -     return second element of set
    -
    -*/
    -#define SETsecond_(set)            ((set)->e[1].p)
    -
    -/*-----------------------------------------
    -
    -   SETsecondt_(set, type)
    -     return second element of set as a type
    -*/
    -#define SETsecondt_(set, type)     ((type*)((set)->e[1].p))
    -
    -/*-----------------------------------------
    -
    -   SETaddr_(set, type)
    -       return address of set's elements
    -*/
    -#define SETaddr_(set,type)         ((type **)(&((set)->e[0].p)))
    -
    -/*-----------------------------------------
    -
    -   SETreturnsize_(set, size)
    -     return size of a set
    -
    -   notes:
    -      set must be defined
    -      use qh_setsize(set) unless speed is critical
    -*/
    -#define SETreturnsize_(set, size) (((size)= ((set)->e[(set)->maxsize].i))?(--(size)):((size)= (set)->maxsize))
    -
    -/*-----------------------------------------
    -
    -   SETempty_(set)
    -     return true(1) if set is empty
    -
    -   notes:
    -      set may be NULL
    -*/
    -#define SETempty_(set)            (!set || (SETfirst_(set) ? 0 : 1))
    -
    -/*---------------------------------
    -
    -  SETsizeaddr_(set)
    -    return pointer to 'actual size+1' of set (set CANNOT be NULL!!)
    -    Its type is setelemT* for strict aliasing
    -    All SETelemaddr_ must be cast to setelemT
    -
    -
    -  notes:
    -    *SETsizeaddr==NULL or e[*SETsizeaddr-1].p==NULL
    -*/
    -#define SETsizeaddr_(set) (&((set)->e[(set)->maxsize]))
    -
    -/*-----------------------------------------
    -
    -   SETtruncate_(set, size)
    -     truncate set to size
    -
    -   see:
    -     qh_settruncate()
    -
    -*/
    -#define SETtruncate_(set, size) {set->e[set->maxsize].i= size+1; /* maybe overwritten */ \
    -      set->e[size].p= NULL;}
    -
    -/*======= prototypes in alphabetical order ============*/
    -
    -void  qh_setaddsorted(setT **setp, void *elem);
    -void  qh_setaddnth(setT **setp, int nth, void *newelem);
    -void  qh_setappend(setT **setp, void *elem);
    -void  qh_setappend_set(setT **setp, setT *setA);
    -void  qh_setappend2ndlast(setT **setp, void *elem);
    -void  qh_setcheck(setT *set, const char *tname, unsigned id);
    -void  qh_setcompact(setT *set);
    -setT *qh_setcopy(setT *set, int extra);
    -void *qh_setdel(setT *set, void *elem);
    -void *qh_setdellast(setT *set);
    -void *qh_setdelnth(setT *set, int nth);
    -void *qh_setdelnthsorted(setT *set, int nth);
    -void *qh_setdelsorted(setT *set, void *newelem);
    -setT *qh_setduplicate( setT *set, int elemsize);
    -int   qh_setequal(setT *setA, setT *setB);
    -int   qh_setequal_except(setT *setA, void *skipelemA, setT *setB, void *skipelemB);
    -int   qh_setequal_skip(setT *setA, int skipA, setT *setB, int skipB);
    -void **qh_setendpointer(setT *set);
    -void  qh_setfree(setT **set);
    -void  qh_setfree2( setT **setp, int elemsize);
    -void  qh_setfreelong(setT **set);
    -int   qh_setin(setT *set, void *setelem);
    -int   qh_setindex(setT *set, void *setelem);
    -void  qh_setlarger(setT **setp);
    -void *qh_setlast(setT *set);
    -setT *qh_setnew(int size);
    -setT *qh_setnew_delnthsorted(setT *set, int size, int nth, int prepend);
    -void  qh_setprint(FILE *fp, const char* string, setT *set);
    -void  qh_setreplace(setT *set, void *oldelem, void *newelem);
    -int   qh_setsize(setT *set);
    -setT *qh_settemp(int setsize);
    -void  qh_settempfree(setT **set);
    -void  qh_settempfree_all(void);
    -setT *qh_settemppop(void);
    -void  qh_settemppush(setT *set);
    -void  qh_settruncate(setT *set, int size);
    -int   qh_setunique(setT **set, void *elem);
    -void  qh_setzero(setT *set, int idx, int size);
    -
    -
    -#endif /* qhDEFset */
    diff --git a/extern/qhull/random.c b/extern/qhull/random.c
    deleted file mode 100644
    index 21b15025afd6..000000000000
    --- a/extern/qhull/random.c
    +++ /dev/null
    @@ -1,243 +0,0 @@
    -/*
      ---------------------------------
    -
    -   random.c -- utilities
    -     Park & Miller's minimimal standard random number generator
    -     argc/argv conversion
    -
    -     Used by rbox.  Do not use 'qh'
    -*/
    -
    -#include "libqhull.h"
    -#include 
    -#include 
    -#include 
    -
    -#ifdef _MSC_VER  /* Microsoft Visual C++ -- warning level 4 */
    -#pragma warning( disable : 4706)  /* assignment within conditional function */
    -#pragma warning( disable : 4996)  /* function was declared deprecated(strcpy, localtime, etc.) */
    -#endif
    -
    -/*---------------------------------
    -
    - qh_argv_to_command( argc, argv, command, max_size )
    -
    -    build command from argc/argv
    -    max_size is at least
    -
    - returns:
    -    a space-delimited string of options (just as typed)
    -    returns false if max_size is too short
    -
    - notes:
    -    silently removes
    -    makes option string easy to input and output
    -    matches qh_argv_to_command_size()
    -
    -    argc may be 0
    -*/
    -int qh_argv_to_command(int argc, char *argv[], char* command, int max_size) {
    -  int i, remaining;
    -  char *s;
    -  *command= '\0';  /* max_size > 0 */
    -
    -  if (argc) {
    -    if ((s= strrchr( argv[0], '\\')) /* get filename w/o .exe extension */
    -    || (s= strrchr( argv[0], '/')))
    -        s++;
    -    else
    -        s= argv[0];
    -    if ((int)strlen(s) < max_size)   /* WARN64 */
    -        strcpy(command, s);
    -    else
    -        goto error_argv;
    -    if ((s= strstr(command, ".EXE"))
    -    ||  (s= strstr(command, ".exe")))
    -        *s= '\0';
    -  }
    -  for (i=1; i < argc; i++) {
    -    s= argv[i];
    -    remaining= max_size - (int)strlen(command) - (int)strlen(s) - 2;   /* WARN64 */
    -    if (!*s || strchr(s, ' ')) {
    -      char *t= command + strlen(command);
    -      remaining -= 2;
    -      if (remaining < 0) {
    -        goto error_argv;
    -      }
    -      *t++= ' ';
    -      *t++= '"';
    -      while (*s) {
    -        if (*s == '"') {
    -          if (--remaining < 0)
    -            goto error_argv;
    -          *t++= '\\';
    -        }
    -        *t++= *s++;
    -      }
    -      *t++= '"';
    -      *t= '\0';
    -    }else if (remaining < 0) {
    -      goto error_argv;
    -    }else
    -      strcat(command, " ");
    -      strcat(command, s);
    -  }
    -  return 1;
    -
    -error_argv:
    -  return 0;
    -} /* argv_to_command */
    -
    -/*---------------------------------
    -
    -qh_argv_to_command_size( argc, argv )
    -
    -    return size to allocate for qh_argv_to_command()
    -
    -notes:
    -    argc may be 0
    -    actual size is usually shorter
    -*/
    -int qh_argv_to_command_size(int argc, char *argv[]) {
    -    unsigned int count= 1; /* null-terminator if argc==0 */
    -    int i;
    -    char *s;
    -
    -    for (i=0; i0 && strchr(argv[i], ' ')) {
    -        count += 2;  /* quote delimiters */
    -        for (s=argv[i]; *s; s++) {
    -          if (*s == '"') {
    -            count++;
    -          }
    -        }
    -      }
    -    }
    -    return count;
    -} /* argv_to_command_size */
    -
    -/*---------------------------------
    -
    -  qh_rand()
    -  qh_srand( seed )
    -    generate pseudo-random number between 1 and 2^31 -2
    -
    -  notes:
    -    For qhull and rbox, called from qh_RANDOMint(),etc. [user.h]
    -
    -    From Park & Miller's minimal standard random number generator
    -      Communications of the ACM, 31:1192-1201, 1988.
    -    Does not use 0 or 2^31 -1
    -      this is silently enforced by qh_srand()
    -    Can make 'Rn' much faster by moving qh_rand to qh_distplane
    -*/
    -
    -/* Global variables and constants */
    -
    -int qh_rand_seed= 1;  /* define as global variable instead of using qh */
    -
    -#define qh_rand_a 16807
    -#define qh_rand_m 2147483647
    -#define qh_rand_q 127773  /* m div a */
    -#define qh_rand_r 2836    /* m mod a */
    -
    -int qh_rand( void) {
    -    int lo, hi, test;
    -    int seed = qh_rand_seed;
    -
    -    hi = seed / qh_rand_q;  /* seed div q */
    -    lo = seed % qh_rand_q;  /* seed mod q */
    -    test = qh_rand_a * lo - qh_rand_r * hi;
    -    if (test > 0)
    -        seed= test;
    -    else
    -        seed= test + qh_rand_m;
    -    qh_rand_seed= seed;
    -    /* seed = seed < qh_RANDOMmax/2 ? 0 : qh_RANDOMmax;  for testing */
    -    /* seed = qh_RANDOMmax;  for testing */
    -    return seed;
    -} /* rand */
    -
    -void qh_srand( int seed) {
    -    if (seed < 1)
    -        qh_rand_seed= 1;
    -    else if (seed >= qh_rand_m)
    -        qh_rand_seed= qh_rand_m - 1;
    -    else
    -        qh_rand_seed= seed;
    -} /* qh_srand */
    -
    -/*---------------------------------
    -
    -qh_randomfactor( scale, offset )
    -return a random factor r * scale + offset
    -
    -notes:
    -qh.RANDOMa/b are defined in global.c
    -*/
    -realT qh_randomfactor(realT scale, realT offset) {
    -    realT randr;
    -
    -    randr= qh_RANDOMint;
    -    return randr * scale + offset;
    -} /* randomfactor */
    -
    -/*---------------------------------
    -
    -qh_randommatrix( buffer, dim, rows )
    -generate a random dim X dim matrix in range [-1,1]
    -assumes buffer is [dim+1, dim]
    -
    -returns:
    -sets buffer to random numbers
    -sets rows to rows of buffer
    -sets row[dim] as scratch row
    -*/
    -void qh_randommatrix(realT *buffer, int dim, realT **rows) {
    -    int i, k;
    -    realT **rowi, *coord, realr;
    -
    -    coord= buffer;
    -    rowi= rows;
    -    for (i=0; i < dim; i++) {
    -        *(rowi++)= coord;
    -        for (k=0; k < dim; k++) {
    -            realr= qh_RANDOMint;
    -            *(coord++)= 2.0 * realr/(qh_RANDOMmax+1) - 1.0;
    -        }
    -    }
    -    *rowi= coord;
    -} /* randommatrix */
    -
    -/*---------------------------------
    -
    -  qh_strtol( s, endp) qh_strtod( s, endp)
    -    internal versions of strtol() and strtod()
    -    does not skip trailing spaces
    -  notes:
    -    some implementations of strtol()/strtod() skip trailing spaces
    -*/
    -double qh_strtod(const char *s, char **endp) {
    -  double result;
    -
    -  result= strtod(s, endp);
    -  if (s < (*endp) && (*endp)[-1] == ' ')
    -    (*endp)--;
    -  return result;
    -} /* strtod */
    -
    -int qh_strtol(const char *s, char **endp) {
    -  int result;
    -
    -  result= (int) strtol(s, endp, 10);     /* WARN64 */
    -  if (s< (*endp) && (*endp)[-1] == ' ')
    -    (*endp)--;
    -  return result;
    -} /* strtol */
    diff --git a/extern/qhull/random.h b/extern/qhull/random.h
    deleted file mode 100644
    index e1e29270419a..000000000000
    --- a/extern/qhull/random.h
    +++ /dev/null
    @@ -1,31 +0,0 @@
    -/*
      ---------------------------------
    -
    -  random.h
    -    header file for random routines
    -
    -   see qh-geom.htm and random.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/random.h#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#ifndef qhDEFrandom
    -#define qhDEFrandom 1
    -
    -#include "libqhull.h"
    -
    -/*============= prototypes in alphabetical order ======= */
    -
    -
    -int     qh_argv_to_command(int argc, char *argv[], char* command, int max_size);
    -int     qh_argv_to_command_size(int argc, char *argv[]);
    -int     qh_rand( void);
    -void    qh_srand( int seed);
    -realT   qh_randomfactor(realT scale, realT offset);
    -void    qh_randommatrix(realT *buffer, int dim, realT **row);
    -int     qh_strtol(const char *s, char **endp);
    -double  qh_strtod(const char *s, char **endp);
    -
    -#endif /* qhDEFrandom */
    diff --git a/extern/qhull/rboxlib.c b/extern/qhull/rboxlib.c
    deleted file mode 100644
    index 0122563f7062..000000000000
    --- a/extern/qhull/rboxlib.c
    +++ /dev/null
    @@ -1,794 +0,0 @@
    -/*
      ---------------------------------
    -
    -   rboxlib.c
    -     Generate input points
    -
    -   notes:
    -     For documentation, see prompt[] of rbox.c
    -     50 points generated for 'rbox D4'
    -
    -   WARNING:
    -     incorrect range if qh_RANDOMmax is defined wrong (user.h)
    -*/
    -
    -#include "random.h"
    -#include "libqhull.h"
    -
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -
    -#ifdef _MSC_VER  /* Microsoft Visual C++ */
    -#pragma warning( disable : 4706)  /* assignment within conditional expression. */
    -#pragma warning( disable : 4996)  /* this function (strncat,sprintf,strcpy) or variable may be unsafe. */
    -#endif
    -
    -#define MAXdim 200
    -#define PI 3.1415926535897932384
    -
    -/* ------------------------------ prototypes ----------------*/
    -int roundi( double a);
    -void out1( double a);
    -void out2n( double a, double b);
    -void out3n( double a, double b, double c);
    -
    -void    qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... );
    -void    qh_free(void *mem);
    -void   *qh_malloc(size_t size);
    -int     qh_rand( void);
    -void    qh_srand( int seed);
    -
    -
    -/* ------------------------------ globals -------------------*/
    -
    -/* No state is carried between rbox requests */
    -typedef struct rboxT rboxT;
    -struct rboxT {
    -  FILE *fout;
    -  FILE *ferr;
    -  int isinteger;
    -  double out_offset;
    -  jmp_buf errexit;        /* exit label for rboxpoints, defined by setjmp(), called by qh_errexit_rbox() */
    -};
    -
    -
    -int rbox_inuse= 0;
    -rboxT rbox;
    -
    -/*---------------------------------
    -
    -  qh_rboxpoints( fout, ferr, rbox_command )
    -    Generate points to fout according to rbox options
    -    Report errors on ferr
    -
    -  returns:
    -    0 (qh_ERRnone) on success
    -    1 (qh_ERRinput) on input error
    -    4 (qh_ERRmem) on memory error
    -    5 (qh_ERRqhull) on internal error
    -
    -  notes:
    -    To avoid stdio, redefine qh_malloc, qh_free, and qh_fprintf_rbox (user.c)
    -    Rbox is not multithreaded.
    -
    -  design:
    -    Straight line code (consider defining a struct and functions):
    -
    -    Parse arguments into variables
    -    Determine the number of points
    -    Generate the points
    -*/
    -int qh_rboxpoints(FILE* fout, FILE* ferr, char* rbox_command) {
    -  int i,j,k;
    -  int gendim;
    -  int cubesize, diamondsize, seed=0, count, apex;
    -  int dim=3 , numpoints= 0, totpoints, addpoints=0;
    -  int issphere=0, isaxis=0,  iscdd= 0, islens= 0, isregular=0, iswidth=0, addcube=0;
    -  int isgap=0, isspiral=0, NOcommand= 0, adddiamond=0;
    -  int israndom=0, istime=0;
    -  int isbox=0, issimplex=0, issimplex2=0, ismesh=0;
    -  double width=0.0, gap=0.0, radius= 0.0;
    -  double coord[MAXdim], offset, meshm=3.0, meshn=4.0, meshr=5.0;
    -  double *simplex= NULL, *simplexp;
    -  int nthroot, mult[MAXdim];
    -  double norm, factor, randr, rangap, lensangle= 0, lensbase= 1;
    -  double anglediff, angle, x, y, cube= 0.0, diamond= 0.0;
    -  double box= qh_DEFAULTbox; /* scale all numbers before output */
    -  double randmax= qh_RANDOMmax;
    -  char command[200], seedbuf[200];
    -  char *s= command, *t, *first_point= NULL;
    -  time_t timedata;
    -  int exitcode;
    -
    -  if (rbox_inuse) {
    -    qh_fprintf_rbox(rbox.ferr, 6188, "rbox error: rbox in use by another process.  Please lock calls to rbox.\n");
    -    return qh_ERRqhull;
    -  }
    -  rbox_inuse= True;
    -  rbox.ferr= ferr;
    -  rbox.fout= fout;
    -
    -  exitcode= setjmp(rbox.errexit);
    -  if (exitcode) {
    -    /* same code for error exit and normal return */
    -    if (simplex)
    -        qh_free(simplex);
    -    rbox_inuse= False;
    -    return exitcode;
    -  }
    -
    -  *command= '\0';
    -  strncat(command, rbox_command, sizeof(command)-strlen(command)-1);
    -
    -  while (*s && !isspace(*s))  /* skip program name */
    -    s++;
    -  while (*s) {
    -    while (*s && isspace(*s))
    -      s++;
    -    if (*s == '-')
    -      s++;
    -    if (!*s)
    -      break;
    -    if (isdigit(*s)) {
    -      numpoints= qh_strtol(s, &s);
    -      continue;
    -    }
    -    /* ============= read flags =============== */
    -    switch (*s++) {
    -    case 'c':
    -      addcube= 1;
    -      t= s;
    -      while (isspace(*t))
    -        t++;
    -      if (*t == 'G')
    -        cube= qh_strtod(++t, &s);
    -      break;
    -    case 'd':
    -      adddiamond= 1;
    -      t= s;
    -      while (isspace(*t))
    -        t++;
    -      if (*t == 'G')
    -        diamond= qh_strtod(++t, &s);
    -      break;
    -    case 'h':
    -      iscdd= 1;
    -      break;
    -    case 'l':
    -      isspiral= 1;
    -      break;
    -    case 'n':
    -      NOcommand= 1;
    -      break;
    -    case 'r':
    -      isregular= 1;
    -      break;
    -    case 's':
    -      issphere= 1;
    -      break;
    -    case 't':
    -      istime= 1;
    -      if (isdigit(*s)) {
    -        seed= qh_strtol(s, &s);
    -        israndom= 0;
    -      }else
    -        israndom= 1;
    -      break;
    -    case 'x':
    -      issimplex= 1;
    -      break;
    -    case 'y':
    -      issimplex2= 1;
    -      break;
    -    case 'z':
    -      rbox.isinteger= 1;
    -      break;
    -    case 'B':
    -      box= qh_strtod(s, &s);
    -      isbox= 1;
    -      break;
    -    case 'D':
    -      dim= qh_strtol(s, &s);
    -      if (dim < 1
    -      || dim > MAXdim) {
    -        qh_fprintf_rbox(rbox.ferr, 6189, "rbox error: dimension, D%d, out of bounds (>=%d or <=0)", dim, MAXdim);
    -        qh_errexit_rbox(qh_ERRinput);
    -      }
    -      break;
    -    case 'G':
    -      if (isdigit(*s))
    -        gap= qh_strtod(s, &s);
    -      else
    -        gap= 0.5;
    -      isgap= 1;
    -      break;
    -    case 'L':
    -      if (isdigit(*s))
    -        radius= qh_strtod(s, &s);
    -      else
    -        radius= 10;
    -      islens= 1;
    -      break;
    -    case 'M':
    -      ismesh= 1;
    -      if (*s)
    -        meshn= qh_strtod(s, &s);
    -      if (*s == ',') {
    -        ++s;
    -        meshm= qh_strtod(s, &s);
    -      }else
    -        meshm= 0.0;
    -      if (*s == ',') {
    -        ++s;
    -        meshr= qh_strtod(s, &s);
    -      }else
    -        meshr= sqrt(meshn*meshn + meshm*meshm);
    -      if (*s && !isspace(*s)) {
    -        qh_fprintf_rbox(rbox.ferr, 7069, "rbox warning: assuming 'M3,4,5' since mesh args are not integers or reals\n");
    -        meshn= 3.0, meshm=4.0, meshr=5.0;
    -      }
    -      break;
    -    case 'O':
    -      rbox.out_offset= qh_strtod(s, &s);
    -      break;
    -    case 'P':
    -      if (!first_point)
    -        first_point= s-1;
    -      addpoints++;
    -      while (*s && !isspace(*s))   /* read points later */
    -        s++;
    -      break;
    -    case 'W':
    -      width= qh_strtod(s, &s);
    -      iswidth= 1;
    -      break;
    -    case 'Z':
    -      if (isdigit(*s))
    -        radius= qh_strtod(s, &s);
    -      else
    -        radius= 1.0;
    -      isaxis= 1;
    -      break;
    -    default:
    -      qh_fprintf_rbox(rbox.ferr, 7070, "rbox error: unknown flag at %s.\nExecute 'rbox' without arguments for documentation.\n", s);
    -      qh_errexit_rbox(qh_ERRinput);
    -    }
    -    if (*s && !isspace(*s)) {
    -      qh_fprintf_rbox(rbox.ferr, 7071, "rbox error: missing space between flags at %s.\n", s);
    -      qh_errexit_rbox(qh_ERRinput);
    -    }
    -  }
    -
    -  /* ============= defaults, constants, and sizes =============== */
    -  if (rbox.isinteger && !isbox)
    -    box= qh_DEFAULTzbox;
    -  if (addcube) {
    -    cubesize= (int)floor(ldexp(1.0,dim)+0.5);
    -    if (cube == 0.0)
    -      cube= box;
    -  }else
    -    cubesize= 0;
    -  if (adddiamond) {
    -    diamondsize= 2*dim;
    -    if (diamond == 0.0)
    -      diamond= box;
    -  }else
    -    diamondsize= 0;
    -  if (islens) {
    -    if (isaxis) {
    -        qh_fprintf_rbox(rbox.ferr, 6190, "rbox error: can not combine 'Ln' with 'Zn'\n");
    -        qh_errexit_rbox(qh_ERRinput);
    -    }
    -    if (radius <= 1.0) {
    -        qh_fprintf_rbox(rbox.ferr, 6191, "rbox error: lens radius %.2g should be greater than 1.0\n",
    -               radius);
    -        qh_errexit_rbox(qh_ERRinput);
    -    }
    -    lensangle= asin(1.0/radius);
    -    lensbase= radius * cos(lensangle);
    -  }
    -
    -  if (!numpoints) {
    -    if (issimplex2)
    -        ; /* ok */
    -    else if (isregular + issimplex + islens + issphere + isaxis + isspiral + iswidth + ismesh) {
    -        qh_fprintf_rbox(rbox.ferr, 6192, "rbox error: missing count\n");
    -        qh_errexit_rbox(qh_ERRinput);
    -    }else if (adddiamond + addcube + addpoints)
    -        ; /* ok */
    -    else {
    -        numpoints= 50;  /* ./rbox D4 is the test case */
    -        issphere= 1;
    -    }
    -  }
    -  if ((issimplex + islens + isspiral + ismesh > 1)
    -  || (issimplex + issphere + isspiral + ismesh > 1)) {
    -    qh_fprintf_rbox(rbox.ferr, 6193, "rbox error: can only specify one of 'l', 's', 'x', 'Ln', or 'Mn,m,r' ('Ln s' is ok).\n");
    -    qh_errexit_rbox(qh_ERRinput);
    -  }
    -
    -  /* ============= print header with total points =============== */
    -  if (issimplex || ismesh)
    -    totpoints= numpoints;
    -  else if (issimplex2)
    -    totpoints= numpoints+dim+1;
    -  else if (isregular) {
    -    totpoints= numpoints;
    -    if (dim == 2) {
    -        if (islens)
    -          totpoints += numpoints - 2;
    -    }else if (dim == 3) {
    -        if (islens)
    -          totpoints += 2 * numpoints;
    -      else if (isgap)
    -        totpoints += 1 + numpoints;
    -      else
    -        totpoints += 2;
    -    }
    -  }else
    -    totpoints= numpoints + isaxis;
    -  totpoints += cubesize + diamondsize + addpoints;
    -
    -  /* ============= seed randoms =============== */
    -  if (istime == 0) {
    -    for (s=command; *s; s++) {
    -      if (issimplex2 && *s == 'y') /* make 'y' same seed as 'x' */
    -        i= 'x';
    -      else
    -        i= *s;
    -      seed= 11*seed + i;
    -    }
    -  }else if (israndom) {
    -    seed= (int)time(&timedata);
    -    sprintf(seedbuf, " t%d", seed);  /* appends an extra t, not worth removing */
    -    strncat(command, seedbuf, sizeof(command)-strlen(command)-1);
    -    t= strstr(command, " t ");
    -    if (t)
    -      strcpy(t+1, t+3); /* remove " t " */
    -  } /* else, seed explicitly set to n */
    -  qh_RANDOMseed_(seed);
    -
    -  /* ============= print header =============== */
    -
    -  if (iscdd)
    -      qh_fprintf_rbox(rbox.fout, 9391, "%s\nbegin\n        %d %d %s\n",
    -      NOcommand ? "" : command,
    -      totpoints, dim+1,
    -      rbox.isinteger ? "integer" : "real");
    -  else if (NOcommand)
    -      qh_fprintf_rbox(rbox.fout, 9392, "%d\n%d\n", dim, totpoints);
    -  else
    -      qh_fprintf_rbox(rbox.fout, 9393, "%d %s\n%d\n", dim, command, totpoints);
    -
    -  /* ============= explicit points =============== */
    -  if ((s= first_point)) {
    -    while (s && *s) { /* 'P' */
    -      count= 0;
    -      if (iscdd)
    -        out1( 1.0);
    -      while (*++s) {
    -        out1( qh_strtod(s, &s));
    -        count++;
    -        if (isspace(*s) || !*s)
    -          break;
    -        if (*s != ',') {
    -          qh_fprintf_rbox(rbox.ferr, 6194, "rbox error: missing comma after coordinate in %s\n\n", s);
    -          qh_errexit_rbox(qh_ERRinput);
    -        }
    -      }
    -      if (count < dim) {
    -        for (k=dim-count; k--; )
    -          out1( 0.0);
    -      }else if (count > dim) {
    -        qh_fprintf_rbox(rbox.ferr, 6195, "rbox error: %d coordinates instead of %d coordinates in %s\n\n",
    -                  count, dim, s);
    -        qh_errexit_rbox(qh_ERRinput);
    -      }
    -      qh_fprintf_rbox(rbox.fout, 9394, "\n");
    -      while ((s= strchr(s, 'P'))) {
    -        if (isspace(s[-1]))
    -          break;
    -      }
    -    }
    -  }
    -
    -  /* ============= simplex distribution =============== */
    -  if (issimplex+issimplex2) {
    -    if (!(simplex= (double*)qh_malloc( dim * (dim+1) * sizeof(double)))) {
    -      qh_fprintf_rbox(rbox.ferr, 6196, "rbox error: insufficient memory for simplex\n");
    -      qh_errexit_rbox(qh_ERRmem); /* qh_ERRmem */
    -    }
    -    simplexp= simplex;
    -    if (isregular) {
    -      for (i=0; i randmax/2)
    -          coord[dim-1]= -coord[dim-1];
    -      /* ============= project 'Wn' point toward boundary =============== */
    -      }else if (iswidth && !issphere) {
    -        j= qh_RANDOMint % gendim;
    -        if (coord[j] < 0)
    -          coord[j]= -1.0 - coord[j] * width;
    -        else
    -          coord[j]= 1.0 - coord[j] * width;
    -      }
    -      /* ============= write point =============== */
    -      if (iscdd)
    -        out1( 1.0);
    -      for (k=0; k < dim; k++)
    -        out1( coord[k] * box);
    -      qh_fprintf_rbox(rbox.fout, 9399, "\n");
    -    }
    -  }
    -
    -  /* ============= write cube vertices =============== */
    -  if (addcube) {
    -    for (j=0; j=0; k--) {
    -        if (j & ( 1 << k))
    -          out1( cube);
    -        else
    -          out1( -cube);
    -      }
    -      qh_fprintf_rbox(rbox.fout, 9400, "\n");
    -    }
    -  }
    -
    -  /* ============= write diamond vertices =============== */
    -  if (adddiamond) {
    -    for (j=0; j=0; k--) {
    -        if (j/2 != k)
    -          out1( 0.0);
    -        else if (j & 0x1)
    -          out1( diamond);
    -        else
    -          out1( -diamond);
    -      }
    -      qh_fprintf_rbox(rbox.fout, 9401, "\n");
    -    }
    -  }
    -
    -  if (iscdd)
    -    qh_fprintf_rbox(rbox.fout, 9402, "end\nhull\n");
    -
    -  /* same code for error exit and normal return */
    -  if (simplex)
    -    qh_free(simplex);
    -  rbox_inuse= False;
    -  return qh_ERRnone;
    -} /* rboxpoints */
    -
    -/*------------------------------------------------
    -outxxx - output functions
    -*/
    -int roundi( double a) {
    -  if (a < 0.0) {
    -    if (a - 0.5 < INT_MIN) {
    -      qh_fprintf_rbox(rbox.ferr, 6200, "rbox input error: negative coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    -      qh_errexit_rbox(qh_ERRinput);
    -    }
    -    return (int)(a - 0.5);
    -  }else {
    -    if (a + 0.5 > INT_MAX) {
    -      qh_fprintf_rbox(rbox.ferr, 6201, "rbox input error: coordinate %2.2g is too large.  Reduce 'Bn'\n", a);
    -      qh_errexit_rbox(qh_ERRinput);
    -    }
    -    return (int)(a + 0.5);
    -  }
    -} /* roundi */
    -
    -void out1(double a) {
    -
    -  if (rbox.isinteger)
    -    qh_fprintf_rbox(rbox.fout, 9403, "%d ", roundi( a+rbox.out_offset));
    -  else
    -    qh_fprintf_rbox(rbox.fout, 9404, qh_REAL_1, a+rbox.out_offset);
    -} /* out1 */
    -
    -void out2n( double a, double b) {
    -
    -  if (rbox.isinteger)
    -    qh_fprintf_rbox(rbox.fout, 9405, "%d %d\n", roundi(a+rbox.out_offset), roundi(b+rbox.out_offset));
    -  else
    -    qh_fprintf_rbox(rbox.fout, 9406, qh_REAL_2n, a+rbox.out_offset, b+rbox.out_offset);
    -} /* out2n */
    -
    -void out3n( double a, double b, double c) {
    -
    -  if (rbox.isinteger)
    -    qh_fprintf_rbox(rbox.fout, 9407, "%d %d %d\n", roundi(a+rbox.out_offset), roundi(b+rbox.out_offset), roundi(c+rbox.out_offset));
    -  else
    -    qh_fprintf_rbox(rbox.fout, 9408, qh_REAL_3n, a+rbox.out_offset, b+rbox.out_offset, c+rbox.out_offset);
    -} /* out3n */
    -
    -void qh_errexit_rbox(int exitcode)
    -{
    -    longjmp(rbox.errexit, exitcode);
    -} /* rbox_errexit */
    diff --git a/extern/qhull/stat.c b/extern/qhull/stat.c
    deleted file mode 100644
    index 50ed9a291568..000000000000
    --- a/extern/qhull/stat.c
    +++ /dev/null
    @@ -1,713 +0,0 @@
    -/*
      ---------------------------------
    -
    -   stat.c
    -   contains all statistics that are collected for qhull
    -
    -   see qh-stat.htm and stat.h
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/stat.c#3 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -*/
    -
    -#include "qhull_a.h"
    -
    -/*============ global data structure ==========*/
    -
    -#if qh_QHpointer
    -qhstatT *qh_qhstat=NULL;  /* global data structure */
    -#else
    -qhstatT qh_qhstat;   /* add "={0}" if this causes a compiler error */
    -#endif
    -
    -/*========== functions in alphabetic order ================*/
    -
    -/*---------------------------------
    -
    -  qh_allstatA()
    -    define statistics in groups of 20
    -
    -  notes:
    -    (otherwise, 'gcc -O2' uses too much memory)
    -    uses qhstat.next
    -*/
    -void qh_allstatA(void) {
    -
    -   /* zdef_(type,name,doc,average) */
    -  zzdef_(zdoc, Zdoc2, "precision statistics", -1);
    -  zdef_(zinc, Znewvertex, NULL, -1);
    -  zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet(!0s)", Znewvertex);
    -  zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
    -  zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
    -  zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
    -  zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);
    -
    -  qhstat precision= qhstat next;  /* call qh_precision for each of these */
    -  zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
    -  zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
    -  zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
    -  zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
    -  zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
    -  zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
    -  zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
    -  zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
    -  zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
    -  zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
    -  zzdef_(zinc, Zmultiridge, "ridges with multiple neighbors", -1);
    -}
    -void qh_allstatB(void) {
    -  zzdef_(zdoc, Zdoc1, "summary information", -1);
    -  zdef_(zinc, Zvertices, "number of vertices in output", -1);
    -  zdef_(zinc, Znumfacets, "number of facets in output", -1);
    -  zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
    -  zdef_(zinc, Znowsimplicial, "number of simplicial facets that were merged", -1);
    -  zdef_(zinc, Znumridges, "number of ridges in output", -1);
    -  zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
    -  zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
    -  zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
    -  zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
    -  zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
    -  zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
    -  zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
    -  zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
    -  zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
    -  zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
    -  zzdef_(zinc, Zsetplane, "facets created altogether", -1);
    -  zdef_(zinc, Ztotridges, "ridges created altogether", -1);
    -  zdef_(zinc, Zpostfacets, "facets before post merge", -1);
    -  zdef_(zadd, Znummergetot, "average merges per facet(at most 511)", Znumfacets);
    -  zdef_(zmax, Znummergemax, "  maximum merges for a facet(at most 511)", -1);
    -  zdef_(zinc, Zangle, NULL, -1);
    -  zdef_(wadd, Wangle, "average angle(cosine) of facet normals for all ridges", Zangle);
    -  zdef_(wmax, Wanglemax, "  maximum angle(cosine) of facet normals across a ridge", -1);
    -  zdef_(wmin, Wanglemin, "  minimum angle(cosine) of facet normals across a ridge", -1);
    -  zdef_(wadd, Wareatot, "total area of facets", -1);
    -  zdef_(wmax, Wareamax, "  maximum facet area", -1);
    -  zdef_(wmin, Wareamin, "  minimum facet area", -1);
    -}
    -void qh_allstatC(void) {
    -  zdef_(zdoc, Zdoc9, "build hull statistics", -1);
    -  zzdef_(zinc, Zprocessed, "points processed", -1);
    -  zzdef_(zinc, Zretry, "retries due to precision problems", -1);
    -  zdef_(wmax, Wretrymax, "  max. random joggle", -1);
    -  zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
    -  zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
    -  zdef_(zinc, Zinsidevisible, "  ave. visible facets without an horizon neighbor", Zprocessed);
    -  zdef_(zadd, Zvisfacettot,  "  ave. facets deleted per iteration", Zprocessed);
    -  zdef_(zmax, Zvisfacetmax,  "    maximum", -1);
    -  zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
    -  zdef_(zmax, Zvisvertexmax, "    maximum", -1);
    -  zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
    -  zdef_(zadd, Znewfacettot,  "ave. new or merged facets per iteration", Zprocessed);
    -  zdef_(zmax, Znewfacetmax,  "    maximum(includes initial simplex)", -1);
    -  zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
    -  zdef_(wadd, Wnewbalance2, "  standard deviation", -1);
    -  zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
    -  zdef_(wadd, Wpbalance2, "  standard deviation", -1);
    -  zdef_(zinc, Zpbalance, "  number of trials", -1);
    -  zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
    -  zdef_(zinc, Zdetsimplex, "determinants computed(area & initial hull)", -1);
    -  zdef_(zinc, Znoarea, "determinants not computed because vertex too low", -1);
    -  zdef_(zinc, Znotmax, "points ignored(!above max_outside)", -1);
    -  zdef_(zinc, Znotgood, "points ignored(!above a good facet)", -1);
    -  zdef_(zinc, Znotgoodnew, "points ignored(didn't create a good new facet)", -1);
    -  zdef_(zinc, Zgoodfacet, "good facets found", -1);
    -  zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
    -  zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
    -  zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
    -  zzdef_(zinc, Zcheckpart, "  ave. distance tests per check", Ztotcheck);
    -}
    -void qh_allstatD(void) {
    -  zdef_(zinc, Zvisit, "resets of visit_id", -1);
    -  zdef_(zinc, Zvvisit, "  resets of vertex_visit", -1);
    -  zdef_(zmax, Zvisit2max, "  max visit_id/2", -1);
    -  zdef_(zmax, Zvvisit2max, "  max vertex_visit/2", -1);
    -
    -  zdef_(zdoc, Zdoc4, "partitioning statistics(see previous for outer planes)", -1);
    -  zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
    -  zdef_(zmax, Zdelvertexmax, "    maximum vertices deleted per iteration", -1);
    -  zdef_(zinc, Zfindbest, "calls to findbest", -1);
    -  zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
    -  zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
    -  zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
    -  zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
    -  zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
    -  zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
    -  zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
    -  zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
    -  zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
    -  zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
    -  zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
    -  zdef_(zinc, Zfindjump,       " ave. clearly better", Zfindhorizon);
    -  zdef_(zinc, Zparthorizon, " horizon facets better than bestfacet", -1);
    -  zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
    -  zdef_(zinc, Zpartflip, "  repartitioned coplanar points for flipped orientation", -1);
    -}
    -void qh_allstatE(void) {
    -  zdef_(zinc, Zpartinside, "inside points", -1);
    -  zdef_(zinc, Zpartnear, "  inside points kept with a facet", -1);
    -  zdef_(zinc, Zcoplanarinside, "  inside points that were coplanar with a facet", -1);
    -  zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
    -  zdef_(zinc, Zbestlowerv, "  with search of vertex neighbors", -1);
    -  zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
    -  zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
    -  zdef_(zinc, Ztotpartition, "partitions of a point", -1);
    -  zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
    -  zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
    -  zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
    -  zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
    -  zdef_(zinc, Zdistio, "distance tests for output", -1);
    -  zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
    -  zdef_(zinc, Zdistplane, "total number of distance tests", -1);
    -  zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
    -  zzdef_(zinc, Zpartcoplanar, "   distance tests for these partitions", -1);
    -  zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
    -}
    -void qh_allstatE2(void) {
    -  zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
    -  zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
    -  zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
    -  zdef_(zinc, Zhashridge, "total lookups of subridges(duplicates and boundary)", -1);
    -  zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
    -  zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
    -  zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);
    -
    -  zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
    -  zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
    -  zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
    -  zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
    -  zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
    -  zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
    -  zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
    -  zdef_(zinc, Zcoplanarcentrum, "coplanar centrums in getmergeset", -1);
    -  zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
    -}
    -void qh_allstatF(void) {
    -  zdef_(zdoc, Zdoc7, "statistics for merging", -1);
    -  zdef_(zinc, Zpremergetot, "merge iterations", -1);
    -  zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
    -  zdef_(zadd, Zmergeinitmax, "  maximum", -1);
    -  zdef_(zadd, Zmergesettot, "  ave. additional non-convex ridges per iteration", Zpremergetot);
    -  zdef_(zadd, Zmergesetmax, "  maximum additional in one pass", -1);
    -  zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
    -  zdef_(zadd, Zmergesettot2, "  additional non-convex ridges", -1);
    -  zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet(w/roundoff)", -1);
    -  zdef_(wmin, Wminvertex, "max distance of merged vertex below facet(or roundoff)", -1);
    -  zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
    -  zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
    -  zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
    -  zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
    -  zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
    -  zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
    -  zzdef_(zadd, Zcyclefacettot, "  ave. facets per cycle", Zcyclehorizon);
    -  zdef_(zmax, Zcyclefacetmax, "  max. facets", -1);
    -  zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
    -  zdef_(zinc, Zmergenew, "new facets merged", -1);
    -  zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
    -  zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
    -  zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
    -  zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
    -  zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
    -  zdef_(zinc, Zneighbor, "merges due to redundant neighbors", -1);
    -  zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
    -}
    -void qh_allstatG(void) {
    -  zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
    -  zdef_(wadd, Wacoplanartot, "  average merge distance", Zacoplanar);
    -  zdef_(wmax, Wacoplanarmax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
    -  zdef_(wadd, Wcoplanartot, "  average merge distance", Zcoplanar);
    -  zdef_(wmax, Wcoplanarmax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zconcave, "merges due to concave facets", -1);
    -  zdef_(wadd, Wconcavetot, "  average merge distance", Zconcave);
    -  zdef_(wmax, Wconcavemax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
    -  zdef_(wadd, Wavoidoldtot, "  average merge distance", Zavoidold);
    -  zdef_(wmax, Wavoidoldmax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
    -  zdef_(wadd, Wdegentot, "  average merge distance", Zdegen);
    -  zdef_(wmax, Wdegenmax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
    -  zdef_(wadd, Wflippedtot, "  average merge distance", Zflipped);
    -  zdef_(wmax, Wflippedmax, "  maximum merge distance", -1);
    -  zdef_(zinc, Zduplicate, "merges due to duplicated ridges", -1);
    -  zdef_(wadd, Wduplicatetot, "  average merge distance", Zduplicate);
    -  zdef_(wmax, Wduplicatemax, "  maximum merge distance", -1);
    -}
    -void qh_allstatH(void) {
    -  zdef_(zdoc, Zdoc8, "renamed vertex statistics", -1);
    -  zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
    -  zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
    -  zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
    -  zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
    -  zdef_(zinc, Zdupridge, "  duplicate ridges detected", -1);
    -  zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
    -  zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
    -  zdef_(zinc, Zdropdegen, "degenerate facets due to dropped neighbors", -1);
    -  zdef_(zinc, Zdelfacetdup, "  facets deleted because of no neighbors", -1);
    -  zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
    -  zdef_(zinc, Zremvertexdel, "  deleted", -1);
    -  zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
    -  zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
    -  zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
    -  zdef_(zadd, Zintersecttot, "   ave. number found per vertex", Zintersect);
    -  zdef_(zmax, Zintersectmax, "   max. found for a vertex", -1);
    -  zdef_(zinc, Zvertexridge, NULL, -1);
    -  zdef_(zadd, Zvertexridgetot, "  ave. number of ridges per tested vertex", Zvertexridge);
    -  zdef_(zmax, Zvertexridgemax, "  max. number of ridges per tested vertex", -1);
    -
    -  zdef_(zdoc, Zdoc10, "memory usage statistics(in bytes)", -1);
    -  zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
    -  zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
    -  zdef_(zadd, Zmempoints, "for input points and outside and coplanar sets",-1);
    -  zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
    -} /* allstat */
    -
    -void qh_allstatI(void) {
    -  qhstat vridges= qhstat next;
    -  zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
    -  zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
    -  zzdef_(wadd, Wridge, "  ave. distance to ridge", Zridge);
    -  zzdef_(wmax, Wridgemax, "  max. distance to ridge", -1);
    -  zzdef_(zinc, Zridgemid, "bounded ridges", -1);
    -  zzdef_(wadd, Wridgemid, "  ave. distance of midpoint to ridge", Zridgemid);
    -  zzdef_(wmax, Wridgemidmax, "  max. distance of midpoint to ridge", -1);
    -  zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
    -  zzdef_(wadd, Wridgeok, "  ave. angle to ridge", Zridgeok);
    -  zzdef_(wmax, Wridgeokmax, "  max. angle to ridge", -1);
    -  zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
    -  zzdef_(wadd, Wridge0, "  ave. angle to ridge", Zridge0);
    -  zzdef_(wmax, Wridge0max, "  max. angle to ridge", -1);
    -
    -  zdef_(zdoc, Zdoc12, "Triangulation statistics(Qt)", -1);
    -  zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
    -  zdef_(zadd, Ztricoplanartot, "  ave. new facets created(may be deleted)", Ztricoplanar);
    -  zdef_(zmax, Ztricoplanarmax, "  max. new facets created", -1);
    -  zdef_(zinc, Ztrinull, "null new facets deleted(duplicated vertex)", -1);
    -  zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted(same vertices)", -1);
    -  zdef_(zinc, Ztridegen, "degenerate new facets in output(same ridge)", -1);
    -} /* allstat */
    -
    -/*---------------------------------
    -
    -  qh_allstatistics()
    -    reset printed flag for all statistics
    -*/
    -void qh_allstatistics(void) {
    -  int i;
    -
    -  for(i=ZEND; i--; )
    -    qhstat printed[i]= False;
    -} /* allstatistics */
    -
    -#if qh_KEEPstatistics
    -/*---------------------------------
    -
    -  qh_collectstatistics()
    -    collect statistics for qh.facet_list
    -
    -*/
    -void qh_collectstatistics(void) {
    -  facetT *facet, *neighbor, **neighborp;
    -  vertexT *vertex, **vertexp;
    -  realT dotproduct, dist;
    -  int sizneighbors, sizridges, sizvertices, i;
    -
    -  qh old_randomdist= qh RANDOMdist;
    -  qh RANDOMdist= False;
    -  zval_(Zmempoints)= qh num_points * qh normal_size +
    -                             sizeof(qhT) + sizeof(qhstatT);
    -  zval_(Zmemfacets)= 0;
    -  zval_(Zmemridges)= 0;
    -  zval_(Zmemvertices)= 0;
    -  zval_(Zangle)= 0;
    -  wval_(Wangle)= 0.0;
    -  zval_(Znumridges)= 0;
    -  zval_(Znumfacets)= 0;
    -  zval_(Znumneighbors)= 0;
    -  zval_(Znumvertices)= 0;
    -  zval_(Znumvneighbors)= 0;
    -  zval_(Znummergetot)= 0;
    -  zval_(Znummergemax)= 0;
    -  zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices);
    -  if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2)
    -    wmax_(Wmaxoutside, qh max_outside);
    -  if (qh MERGING)
    -    wmin_(Wminvertex, qh min_vertex);
    -  FORALLfacets
    -    facet->seen= False;
    -  if (qh DELAUNAY) {
    -    FORALLfacets {
    -      if (facet->upperdelaunay != qh UPPERdelaunay)
    -        facet->seen= True; /* remove from angle statistics */
    -    }
    -  }
    -  FORALLfacets {
    -    if (facet->visible && qh NEWfacets)
    -      continue;
    -    sizvertices= qh_setsize(facet->vertices);
    -    sizneighbors= qh_setsize(facet->neighbors);
    -    sizridges= qh_setsize(facet->ridges);
    -    zinc_(Znumfacets);
    -    zadd_(Znumvertices, sizvertices);
    -    zmax_(Zmaxvertices, sizvertices);
    -    zadd_(Znumneighbors, sizneighbors);
    -    zmax_(Zmaxneighbors, sizneighbors);
    -    zadd_(Znummergetot, facet->nummerge);
    -    i= facet->nummerge; /* avoid warnings */
    -    zmax_(Znummergemax, i);
    -    if (!facet->simplicial) {
    -      if (sizvertices == qh hull_dim) {
    -        zinc_(Znowsimplicial);
    -      }else {
    -        zinc_(Znonsimplicial);
    -      }
    -    }
    -    if (sizridges) {
    -      zadd_(Znumridges, sizridges);
    -      zmax_(Zmaxridges, sizridges);
    -    }
    -    zadd_(Zmemfacets, sizeof(facetT) + qh normal_size + 2*sizeof(setT)
    -       + SETelemsize * (sizneighbors + sizvertices));
    -    if (facet->ridges) {
    -      zadd_(Zmemridges,
    -         sizeof(setT) + SETelemsize * sizridges + sizridges *
    -         (sizeof(ridgeT) + sizeof(setT) + SETelemsize * (qh hull_dim-1))/2);
    -    }
    -    if (facet->outsideset)
    -      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset));
    -    if (facet->coplanarset)
    -      zadd_(Zmempoints, sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset));
    -    if (facet->seen) /* Delaunay upper envelope */
    -      continue;
    -    facet->seen= True;
    -    FOREACHneighbor_(facet) {
    -      if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
    -          || neighbor->seen || !facet->normal || !neighbor->normal)
    -        continue;
    -      dotproduct= qh_getangle(facet->normal, neighbor->normal);
    -      zinc_(Zangle);
    -      wadd_(Wangle, dotproduct);
    -      wmax_(Wanglemax, dotproduct)
    -      wmin_(Wanglemin, dotproduct)
    -    }
    -    if (facet->normal) {
    -      FOREACHvertex_(facet->vertices) {
    -        zinc_(Zdiststat);
    -        qh_distplane(vertex->point, facet, &dist);
    -        wmax_(Wvertexmax, dist);
    -        wmin_(Wvertexmin, dist);
    -      }
    -    }
    -  }
    -  FORALLvertices {
    -    if (vertex->deleted)
    -      continue;
    -    zadd_(Zmemvertices, sizeof(vertexT));
    -    if (vertex->neighbors) {
    -      sizneighbors= qh_setsize(vertex->neighbors);
    -      zadd_(Znumvneighbors, sizneighbors);
    -      zmax_(Zmaxvneighbors, sizneighbors);
    -      zadd_(Zmemvertices, sizeof(vertexT) + SETelemsize * sizneighbors);
    -    }
    -  }
    -  qh RANDOMdist= qh old_randomdist;
    -} /* collectstatistics */
    -#endif /* qh_KEEPstatistics */
    -
    -/*---------------------------------
    -
    -  qh_freestatistics(  )
    -    free memory used for statistics
    -*/
    -void qh_freestatistics(void) {
    -
    -#if qh_QHpointer
    -  qh_free(qh_qhstat);
    -  qh_qhstat= NULL;
    -#endif
    -} /* freestatistics */
    -
    -/*---------------------------------
    -
    -  qh_initstatistics(  )
    -    allocate and initialize statistics
    -
    -  notes:
    -    uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet
    -    NOerrors -- qh_initstatistics can not use qh_errexit().  One first call, qh_memalloc is not initialized.  Also invoked by QhullQh().
    -*/
    -void qh_initstatistics(void) {
    -  int i;
    -  realT realx;
    -  int intx;
    -
    -#if qh_QHpointer
    -  if(qh_qhstat){  /* qh_initstatistics may be called from Qhull::resetStatistics() */
    -      qh_free(qh_qhstat);
    -      qh_qhstat= 0;
    -  }
    -  if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) {
    -    qh_fprintf(qhmem.ferr, 6183, "qhull error (qh_initstatistics): insufficient memory\n");
    -    qh_exit(qh_ERRmem);  /* can not use qh_errexit() */
    -  }
    -#endif
    -
    -  qhstat next= 0;
    -  qh_allstatA();
    -  qh_allstatB();
    -  qh_allstatC();
    -  qh_allstatD();
    -  qh_allstatE();
    -  qh_allstatE2();
    -  qh_allstatF();
    -  qh_allstatG();
    -  qh_allstatH();
    -  qh_allstatI();
    -  if (qhstat next > (int)sizeof(qhstat id)) {
    -    qh_fprintf(qhmem.ferr, 6184, "qhull error (qh_initstatistics): increase size of qhstat.id[].\n\
    -      qhstat.next %d should be <= sizeof(qhstat id) %d\n", qhstat next, (int)sizeof(qhstat id));
    -#if 0 /* for locating error, Znumridges should be duplicated */
    -    for(i=0; i < ZEND; i++) {
    -      int j;
    -      for(j=i+1; j < ZEND; j++) {
    -        if (qhstat id[i] == qhstat id[j]) {
    -          qh_fprintf(qhmem.ferr, 6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
    -              qhstat id[i], i, j);
    -        }
    -      }
    -    }
    -#endif
    -    qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
    -  }
    -  qhstat init[zinc].i= 0;
    -  qhstat init[zadd].i= 0;
    -  qhstat init[zmin].i= INT_MAX;
    -  qhstat init[zmax].i= INT_MIN;
    -  qhstat init[wadd].r= 0;
    -  qhstat init[wmin].r= REALmax;
    -  qhstat init[wmax].r= -REALmax;
    -  for(i=0; i < ZEND; i++) {
    -    if (qhstat type[i] > ZTYPEreal) {
    -      realx= qhstat init[(unsigned char)(qhstat type[i])].r;
    -      qhstat stats[i].r= realx;
    -    }else if (qhstat type[i] != zdoc) {
    -      intx= qhstat init[(unsigned char)(qhstat type[i])].i;
    -      qhstat stats[i].i= intx;
    -    }
    -  }
    -} /* initstatistics */
    -
    -/*---------------------------------
    -
    -  qh_newstats(  )
    -    returns True if statistics for zdoc
    -
    -  returns:
    -    next zdoc
    -*/
    -boolT qh_newstats(int idx, int *nextindex) {
    -  boolT isnew= False;
    -  int start, i;
    -
    -  if (qhstat type[qhstat id[idx]] == zdoc)
    -    start= idx+1;
    -  else
    -    start= idx;
    -  for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) {
    -    if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]])
    -        isnew= True;
    -  }
    -  *nextindex= i;
    -  return isnew;
    -} /* newstats */
    -
    -/*---------------------------------
    -
    -  qh_nostatistic( index )
    -    true if no statistic to print
    -*/
    -boolT qh_nostatistic(int i) {
    -
    -  if ((qhstat type[i] > ZTYPEreal
    -       &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r)
    -      || (qhstat type[i] < ZTYPEreal
    -          &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i))
    -    return True;
    -  return False;
    -} /* nostatistic */
    -
    -#if qh_KEEPstatistics
    -/*---------------------------------
    -
    -  qh_printallstatistics( fp, string )
    -    print all statistics with header 'string'
    -*/
    -void qh_printallstatistics(FILE *fp, const char *string) {
    -
    -  qh_allstatistics();
    -  qh_collectstatistics();
    -  qh_printstatistics(fp, string);
    -  qh_memstatistics(fp);
    -}
    -
    -
    -/*---------------------------------
    -
    -  qh_printstatistics( fp, string )
    -    print statistics to a file with header 'string'
    -    skips statistics with qhstat.printed[] (reset with qh_allstatistics)
    -
    -  see:
    -    qh_printallstatistics()
    -*/
    -void qh_printstatistics(FILE *fp, const char *string) {
    -  int i, k;
    -  realT ave;
    -
    -  if (qh num_points != qh num_vertices) {
    -    wval_(Wpbalance)= 0;
    -    wval_(Wpbalance2)= 0;
    -  }else
    -    wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
    -                                 wval_(Wpbalance2), &ave);
    -  wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
    -                                 wval_(Wnewbalance2), &ave);
    -  qh_fprintf(fp, 9350, "\n\
    -%s\n\
    - qhull invoked by: %s | %s\n%s with options:\n%s\n", string, qh rbox_command,
    -     qh qhull_command, qh_version, qh qhull_options);
    -  qh_fprintf(fp, 9351, "\nprecision constants:\n\
    - %6.2g max. abs. coordinate in the (transformed) input('Qbd:n')\n\
    - %6.2g max. roundoff error for distance computation('En')\n\
    - %6.2g max. roundoff error for angle computations\n\
    - %6.2g min. distance for outside points ('Wn')\n\
    - %6.2g min. distance for visible facets ('Vn')\n\
    - %6.2g max. distance for coplanar facets ('Un')\n\
    - %6.2g max. facet width for recomputing centrum and area\n\
    -",
    -  qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside,
    -        qh MINvisible, qh MAXcoplanar, qh WIDEfacet);
    -  if (qh KEEPnearinside)
    -    qh_fprintf(fp, 9352, "\
    - %6.2g max. distance for near-inside points\n", qh NEARinside);
    -  if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\
    - %6.2g max. cosine for pre-merge angle\n", qh premerge_cos);
    -  if (qh PREmerge) qh_fprintf(fp, 9354, "\
    - %6.2g radius of pre-merge centrum\n", qh premerge_centrum);
    -  if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\
    - %6.2g max. cosine for post-merge angle\n", qh postmerge_cos);
    -  if (qh POSTmerge) qh_fprintf(fp, 9356, "\
    - %6.2g radius of post-merge centrum\n", qh postmerge_centrum);
    -  qh_fprintf(fp, 9357, "\
    - %6.2g max. distance for merging two simplicial facets\n\
    - %6.2g max. roundoff error for arithmetic operations\n\
    - %6.2g min. denominator for divisions\n\
    -  zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom);
    -  for(k=0; k < qh hull_dim; k++)
    -    qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]);
    -  qh_fprintf(fp, 9359, "\n\n");
    -  for(i=0 ; i < qhstat next; )
    -    qh_printstats(fp, i, &i);
    -} /* printstatistics */
    -#endif /* qh_KEEPstatistics */
    -
    -/*---------------------------------
    -
    -  qh_printstatlevel( fp, id )
    -    print level information for a statistic
    -
    -  notes:
    -    nop if id >= ZEND, printed, or same as initial value
    -*/
    -void qh_printstatlevel(FILE *fp, int id, int start) {
    -#define NULLfield "       "
    -
    -  if (id >= ZEND || qhstat printed[id])
    -    return;
    -  if (qhstat type[id] == zdoc) {
    -    qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]);
    -    return;
    -  }
    -  start= 0; /* not used */
    -  if (qh_nostatistic(id) || !qhstat doc[id])
    -    return;
    -  qhstat printed[id]= True;
    -  if (qhstat count[id] != -1
    -      && qhstat stats[(unsigned char)(qhstat count[id])].i == 0)
    -    qh_fprintf(fp, 9361, " *0 cnt*");
    -  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1)
    -    qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r);
    -  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1)
    -    qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i);
    -  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1)
    -    qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i);
    -  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1)
    -    qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i);
    -  qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]);
    -} /* printstatlevel */
    -
    -
    -/*---------------------------------
    -
    -  qh_printstats( fp, index, nextindex )
    -    print statistics for a zdoc group
    -
    -  returns:
    -    next zdoc if non-null
    -*/
    -void qh_printstats(FILE *fp, int idx, int *nextindex) {
    -  int j, nexti;
    -
    -  if (qh_newstats(idx, &nexti)) {
    -    qh_fprintf(fp, 9367, "\n");
    -    for (j=idx; j--------------------------------
    -
    -  qh_stddev( num, tot, tot2, ave )
    -    compute the standard deviation and average from statistics
    -
    -    tot2 is the sum of the squares
    -  notes:
    -    computes r.m.s.:
    -      (x-ave)^2
    -      == x^2 - 2x tot/num +   (tot/num)^2
    -      == tot2 - 2 tot tot/num + tot tot/num
    -      == tot2 - tot ave
    -*/
    -realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
    -  realT stddev;
    -
    -  *ave= tot/num;
    -  stddev= sqrt(tot2/num - *ave * *ave);
    -  return stddev;
    -} /* stddev */
    -
    -#endif /* qh_KEEPstatistics */
    -
    -#if !qh_KEEPstatistics
    -void    qh_collectstatistics(void) {}
    -void    qh_printallstatistics(FILE *fp, char *string) {};
    -void    qh_printstatistics(FILE *fp, char *string) {}
    -#endif
    diff --git a/extern/qhull/stat.h b/extern/qhull/stat.h
    deleted file mode 100644
    index 97d8efa585f3..000000000000
    --- a/extern/qhull/stat.h
    +++ /dev/null
    @@ -1,541 +0,0 @@
    -/*
      ---------------------------------
    -
    -   stat.h
    -     contains all statistics that are collected for qhull
    -
    -   see qh-stat.htm and stat.c
    -
    -   Copyright (c) 1993-2012 The Geometry Center.
    -   $Id: //main/2011/qhull/src/libqhull/stat.h#5 $$Change: 1464 $
    -   $DateTime: 2012/01/25 22:58:41 $$Author: bbarber $
    -
    -   recompile qhull if you change this file
    -
    -   Integer statistics are Z* while real statistics are W*.
    -
    -   define maydebugx to call a routine at every statistic event
    -
    -*/
    -
    -#ifndef qhDEFstat
    -#define qhDEFstat 1
    -
    -#include "libqhull.h"
    -
    -/*---------------------------------
    -
    -  qh_KEEPstatistics
    -    0 turns off statistic gathering (except zzdef/zzinc/zzadd/zzval/wwval)
    -*/
    -#ifndef qh_KEEPstatistics
    -#define qh_KEEPstatistics 1
    -#endif
    -
    -/*---------------------------------
    -
    -  Zxxx for integers, Wxxx for reals
    -
    -  notes:
    -    be sure that all statistics are defined in stat.c
    -      otherwise initialization may core dump
    -    can pick up all statistics by:
    -      grep '[zw].*_[(][ZW]' *.c >z.x
    -    remove trailers with query">-
    -    remove leaders with  query-replace-regexp [ ^I]+  (
    -*/
    -#if qh_KEEPstatistics
    -enum statistics {     /* alphabetical after Z/W */
    -    Zacoplanar,
    -    Wacoplanarmax,
    -    Wacoplanartot,
    -    Zangle,
    -    Wangle,
    -    Wanglemax,
    -    Wanglemin,
    -    Zangletests,
    -    Wareatot,
    -    Wareamax,
    -    Wareamin,
    -    Zavoidold,
    -    Wavoidoldmax,
    -    Wavoidoldtot,
    -    Zback0,
    -    Zbestcentrum,
    -    Zbestdist,
    -    Zbestlower,
    -    Zbestlowerv,
    -    Zcentrumtests,
    -    Zcheckpart,
    -    Zcomputefurthest,
    -    Zconcave,
    -    Wconcavemax,
    -    Wconcavetot,
    -    Zconcaveridges,
    -    Zconcaveridge,
    -    Zcoplanar,
    -    Wcoplanarmax,
    -    Wcoplanartot,
    -    Zcoplanarangle,
    -    Zcoplanarcentrum,
    -    Zcoplanarhorizon,
    -    Zcoplanarinside,
    -    Zcoplanarpart,
    -    Zcoplanarridges,
    -    Wcpu,
    -    Zcyclefacetmax,
    -    Zcyclefacettot,
    -    Zcyclehorizon,
    -    Zcyclevertex,
    -    Zdegen,
    -    Wdegenmax,
    -    Wdegentot,
    -    Zdegenvertex,
    -    Zdelfacetdup,
    -    Zdelridge,
    -    Zdelvertextot,
    -    Zdelvertexmax,
    -    Zdetsimplex,
    -    Zdistcheck,
    -    Zdistconvex,
    -    Zdistgood,
    -    Zdistio,
    -    Zdistplane,
    -    Zdiststat,
    -    Zdistvertex,
    -    Zdistzero,
    -    Zdoc1,
    -    Zdoc2,
    -    Zdoc3,
    -    Zdoc4,
    -    Zdoc5,
    -    Zdoc6,
    -    Zdoc7,
    -    Zdoc8,
    -    Zdoc9,
    -    Zdoc10,
    -    Zdoc11,
    -    Zdoc12,
    -    Zdropdegen,
    -    Zdropneighbor,
    -    Zdupflip,
    -    Zduplicate,
    -    Wduplicatemax,
    -    Wduplicatetot,
    -    Zdupridge,
    -    Zdupsame,
    -    Zflipped,
    -    Wflippedmax,
    -    Wflippedtot,
    -    Zflippedfacets,
    -    Zfindbest,
    -    Zfindbestmax,
    -    Zfindbesttot,
    -    Zfindcoplanar,
    -    Zfindfail,
    -    Zfindhorizon,
    -    Zfindhorizonmax,
    -    Zfindhorizontot,
    -    Zfindjump,
    -    Zfindnew,
    -    Zfindnewmax,
    -    Zfindnewtot,
    -    Zfindnewjump,
    -    Zfindnewsharp,
    -    Zgauss0,
    -    Zgoodfacet,
    -    Zhashlookup,
    -    Zhashridge,
    -    Zhashridgetest,
    -    Zhashtests,
    -    Zinsidevisible,
    -    Zintersect,
    -    Zintersectfail,
    -    Zintersectmax,
    -    Zintersectnum,
    -    Zintersecttot,
    -    Zmaxneighbors,
    -    Wmaxout,
    -    Wmaxoutside,
    -    Zmaxridges,
    -    Zmaxvertex,
    -    Zmaxvertices,
    -    Zmaxvneighbors,
    -    Zmemfacets,
    -    Zmempoints,
    -    Zmemridges,
    -    Zmemvertices,
    -    Zmergeflipdup,
    -    Zmergehorizon,
    -    Zmergeinittot,
    -    Zmergeinitmax,
    -    Zmergeinittot2,
    -    Zmergeintohorizon,
    -    Zmergenew,
    -    Zmergesettot,
    -    Zmergesetmax,
    -    Zmergesettot2,
    -    Zmergesimplex,
    -    Zmergevertex,
    -    Wmindenom,
    -    Wminvertex,
    -    Zminnorm,
    -    Zmultiridge,
    -    Znearlysingular,
    -    Zneighbor,
    -    Wnewbalance,
    -    Wnewbalance2,
    -    Znewfacettot,
    -    Znewfacetmax,
    -    Znewvertex,
    -    Wnewvertex,
    -    Wnewvertexmax,
    -    Znoarea,
    -    Znonsimplicial,
    -    Znowsimplicial,
    -    Znotgood,
    -    Znotgoodnew,
    -    Znotmax,
    -    Znumfacets,
    -    Znummergemax,
    -    Znummergetot,
    -    Znumneighbors,
    -    Znumridges,
    -    Znumvertices,
    -    Znumvisibility,
    -    Znumvneighbors,
    -    Zonehorizon,
    -    Zpartangle,
    -    Zpartcoplanar,
    -    Zpartflip,
    -    Zparthorizon,
    -    Zpartinside,
    -    Zpartition,
    -    Zpartitionall,
    -    Zpartnear,
    -    Zpbalance,
    -    Wpbalance,
    -    Wpbalance2,
    -    Zpostfacets,
    -    Zpremergetot,
    -    Zprocessed,
    -    Zremvertex,
    -    Zremvertexdel,
    -    Zrenameall,
    -    Zrenamepinch,
    -    Zrenameshare,
    -    Zretry,
    -    Wretrymax,
    -    Zridge,
    -    Wridge,
    -    Wridgemax,
    -    Zridge0,
    -    Wridge0,
    -    Wridge0max,
    -    Zridgemid,
    -    Wridgemid,
    -    Wridgemidmax,
    -    Zridgeok,
    -    Wridgeok,
    -    Wridgeokmax,
    -    Zsearchpoints,
    -    Zsetplane,
    -    Ztestvneighbor,
    -    Ztotcheck,
    -    Ztothorizon,
    -    Ztotmerge,
    -    Ztotpartcoplanar,
    -    Ztotpartition,
    -    Ztotridges,
    -    Ztotvertices,
    -    Ztotvisible,
    -    Ztricoplanar,
    -    Ztricoplanarmax,
    -    Ztricoplanartot,
    -    Ztridegen,
    -    Ztrimirror,
    -    Ztrinull,
    -    Wvertexmax,
    -    Wvertexmin,
    -    Zvertexridge,
    -    Zvertexridgetot,
    -    Zvertexridgemax,
    -    Zvertices,
    -    Zvisfacettot,
    -    Zvisfacetmax,
    -    Zvisit,
    -    Zvisit2max,
    -    Zvisvertextot,
    -    Zvisvertexmax,
    -    Zvvisit,
    -    Zvvisit2max,
    -    Zwidefacet,
    -    Zwidevertices,
    -    ZEND};
    -
    -/*---------------------------------
    -
    -  Zxxx/Wxxx statistics that remain defined if qh_KEEPstatistics=0
    -
    -  notes:
    -    be sure to use zzdef, zzinc, etc. with these statistics (no double checking!)
    -*/
    -#else
    -enum statistics {     /* for zzdef etc. macros */
    -  Zback0,
    -  Zbestdist,
    -  Zcentrumtests,
    -  Zcheckpart,
    -  Zconcaveridges,
    -  Zcoplanarhorizon,
    -  Zcoplanarpart,
    -  Zcoplanarridges,
    -  Zcyclefacettot,
    -  Zcyclehorizon,
    -  Zdelvertextot,
    -  Zdistcheck,
    -  Zdistconvex,
    -  Zdistzero,
    -  Zdoc1,
    -  Zdoc2,
    -  Zdoc3,
    -  Zdoc11,
    -  Zflippedfacets,
    -  Zgauss0,
    -  Zminnorm,
    -  Zmultiridge,
    -  Znearlysingular,
    -  Wnewvertexmax,
    -  Znumvisibility,
    -  Zpartcoplanar,
    -  Zpartition,
    -  Zpartitionall,
    -  Zprocessed,
    -  Zretry,
    -  Zridge,
    -  Wridge,
    -  Wridgemax,
    -  Zridge0,
    -  Wridge0,
    -  Wridge0max,
    -  Zridgemid,
    -  Wridgemid,
    -  Wridgemidmax,
    -  Zridgeok,
    -  Wridgeok,
    -  Wridgeokmax,
    -  Zsetplane,
    -  Ztotcheck,
    -  Ztotmerge,
    -    ZEND};
    -#endif
    -
    -/*---------------------------------
    -
    -  ztype
    -    the type of a statistic sets its initial value.
    -
    -  notes:
    -    The type should be the same as the macro for collecting the statistic
    -*/
    -enum ztypes {zdoc,zinc,zadd,zmax,zmin,ZTYPEreal,wadd,wmax,wmin,ZTYPEend};
    -
    -/*========== macros and constants =============*/
    -
    -/*----------------------------------
    -
    -  MAYdebugx
    -    define as maydebug() to be called frequently for error trapping
    -*/
    -#define MAYdebugx
    -
    -/*----------------------------------
    -
    -  zzdef_, zdef_( type, name, doc, -1)
    -    define a statistic (assumes 'qhstat.next= 0;')
    -
    -  zdef_( type, name, doc, count)
    -    define an averaged statistic
    -    printed as name/count
    -*/
    -#define zzdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
    -   qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
    -#if qh_KEEPstatistics
    -#define zdef_(stype,name,string,cnt) qhstat id[qhstat next++]=name; \
    -   qhstat doc[name]= string; qhstat count[name]= cnt; qhstat type[name]= stype
    -#else
    -#define zdef_(type,name,doc,count)
    -#endif
    -
    -/*----------------------------------
    -
    -  zzinc_( name ), zinc_( name)
    -    increment an integer statistic
    -*/
    -#define zzinc_(id) {MAYdebugx; qhstat stats[id].i++;}
    -#if qh_KEEPstatistics
    -#define zinc_(id) {MAYdebugx; qhstat stats[id].i++;}
    -#else
    -#define zinc_(id) {}
    -#endif
    -
    -/*----------------------------------
    -
    -  zzadd_( name, value ), zadd_( name, value ), wadd_( name, value )
    -    add value to an integer or real statistic
    -*/
    -#define zzadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
    -#define wwadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
    -#if qh_KEEPstatistics
    -#define zadd_(id, val) {MAYdebugx; qhstat stats[id].i += (val);}
    -#define wadd_(id, val) {MAYdebugx; qhstat stats[id].r += (val);}
    -#else
    -#define zadd_(id, val) {}
    -#define wadd_(id, val) {}
    -#endif
    -
    -/*----------------------------------
    -
    -  zzval_( name ), zval_( name ), wwval_( name )
    -    set or return value of a statistic
    -*/
    -#define zzval_(id) ((qhstat stats[id]).i)
    -#define wwval_(id) ((qhstat stats[id]).r)
    -#if qh_KEEPstatistics
    -#define zval_(id) ((qhstat stats[id]).i)
    -#define wval_(id) ((qhstat stats[id]).r)
    -#else
    -#define zval_(id) qhstat tempi
    -#define wval_(id) qhstat tempr
    -#endif
    -
    -/*----------------------------------
    -
    -  zmax_( id, val ), wmax_( id, value )
    -    maximize id with val
    -*/
    -#define wwmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
    -#if qh_KEEPstatistics
    -#define zmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].i,(val));}
    -#define wmax_(id, val) {MAYdebugx; maximize_(qhstat stats[id].r,(val));}
    -#else
    -#define zmax_(id, val) {}
    -#define wmax_(id, val) {}
    -#endif
    -
    -/*----------------------------------
    -
    -  zmin_( id, val ), wmin_( id, value )
    -    minimize id with val
    -*/
    -#if qh_KEEPstatistics
    -#define zmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].i,(val));}
    -#define wmin_(id, val) {MAYdebugx; minimize_(qhstat stats[id].r,(val));}
    -#else
    -#define zmin_(id, val) {}
    -#define wmin_(id, val) {}
    -#endif
    -
    -/*================== stat.h types ==============*/
    -
    -
    -/*----------------------------------
    -
    -  intrealT
    -    union of integer and real, used for statistics
    -*/
    -typedef union intrealT intrealT;    /* union of int and realT */
    -union intrealT {
    -    int i;
    -    realT r;
    -};
    -
    -/*----------------------------------
    -
    -  qhstat
    -    global data structure for statistics, similar to qh and qhrbox
    -
    -  notes:
    -   access to qh_qhstat is via the "qhstat" macro.  There are two choices
    -   qh_QHpointer = 1     access globals via a pointer
    -                        enables qh_saveqhull() and qh_restoreqhull()
    -                = 0     qh_qhstat is a static data structure
    -                        only one instance of qhull() can be active at a time
    -                        default value
    -   qh_QHpointer is defined in libqhull.h
    -   qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
    -
    -   allocated in stat.c using qh_malloc()
    -*/
    -#ifndef DEFqhstatT
    -#define DEFqhstatT 1
    -typedef struct qhstatT qhstatT;
    -#endif
    -
    -#if qh_QHpointer_dllimport
    -#define qhstat qh_qhstat->
    -__declspec(dllimport) extern qhstatT *qh_qhstat;
    -#elif qh_QHpointer
    -#define qhstat qh_qhstat->
    -extern qhstatT *qh_qhstat;
    -#elif qh_dllimport
    -#define qhstat qh_qhstat.
    -__declspec(dllimport) extern qhstatT qh_qhstat;
    -#else
    -#define qhstat qh_qhstat.
    -extern qhstatT qh_qhstat;
    -#endif
    -struct qhstatT {
    -  intrealT   stats[ZEND];     /* integer and real statistics */
    -  unsigned   char id[ZEND+10]; /* id's in print order */
    -  const char *doc[ZEND];       /* array of documentation strings */
    -  short int  count[ZEND];     /* -1 if none, else index of count to use */
    -  char       type[ZEND];      /* type, see ztypes above */
    -  char       printed[ZEND];   /* true, if statistic has been printed */
    -  intrealT   init[ZTYPEend];  /* initial values by types, set initstatistics */
    -
    -  int        next;            /* next index for zdef_ */
    -  int        precision;       /* index for precision problems */
    -  int        vridges;         /* index for Voronoi ridges */
    -  int        tempi;
    -  realT      tempr;
    -};
    -
    -/*========== function prototypes ===========*/
    -
    -void    qh_allstatA(void);
    -void    qh_allstatB(void);
    -void    qh_allstatC(void);
    -void    qh_allstatD(void);
    -void    qh_allstatE(void);
    -void    qh_allstatE2(void);
    -void    qh_allstatF(void);
    -void    qh_allstatG(void);
    -void    qh_allstatH(void);
    -void    qh_allstatI(void);
    -void    qh_allstatistics(void);
    -void    qh_collectstatistics(void);
    -void    qh_freestatistics(void);
    -void    qh_initstatistics(void);
    -boolT   qh_newstats(int idx, int *nextindex);
    -boolT   qh_nostatistic(int i);
    -void    qh_printallstatistics(FILE *fp, const char *string);
    -void    qh_printstatistics(FILE *fp, const char *string);
    -void    qh_printstatlevel(FILE *fp, int id, int start);
    -void    qh_printstats(FILE *fp, int idx, int *nextindex);
    -realT   qh_stddev(int num, realT tot, realT tot2, realT *ave);
    -
    -#endif   /* qhDEFstat */
    diff --git a/extern/qhull/user.c b/extern/qhull/user.c
    deleted file mode 100644
    index ce2f76410d1e..000000000000
    --- a/extern/qhull/user.c
    +++ /dev/null
    @@ -1,525 +0,0 @@
    -/*
      ---------------------------------
    -
    -   user.c
    -   user redefinable functions
    -
    -   see user2.c for qh_fprintf, qh_malloc, qh_free
    -
    -   see README.txt  see COPYING.txt for copyright information.
    -
    -   see libqhull.h for data structures, macros, and user-callable functions.
    -
    -   see user_eg.c, unix.c, and qhull_interface.cpp for examples.
    -
    -   see user.h for user-definable constants
    -
    -      use qh_NOmem in mem.h to turn off memory management
    -      use qh_NOmerge in user.h to turn off facet merging
    -      set qh_KEEPstatistics in user.h to 0 to turn off statistics
    -
    -   This is unsupported software.  You're welcome to make changes,
    -   but you're on your own if something goes wrong.  Use 'Tc' to
    -   check frequently.  Usually qhull will report an error if
    -   a data structure becomes inconsistent.  If so, it also reports
    -   the last point added to the hull, e.g., 102.  You can then trace
    -   the execution of qhull with "T4P102".
    -
    -   Please report any errors that you fix to qhull@qhull.org
    -
    -   call_qhull is a template for calling qhull from within your application
    -
    -   if you recompile and load this module, then user.o will not be loaded
    -   from qhull.a
    -
    -   you can add additional quick allocation sizes in qh_user_memsizes
    -
    -   if the other functions here are redefined to not use qh_print...,
    -   then io.o will not be loaded from qhull.a.  See user_eg.c for an
    -   example.  We recommend keeping io.o for the extra debugging
    -   information it supplies.
    -*/
    -
    -#include "qhull_a.h"
    -
    -#include 
    -
    -/*---------------------------------
    -
    -  qh_call_qhull( void )
    -    template for calling qhull from inside your program
    -    remove #if 0, #endif to compile
    -
    -  returns:
    -    exit code(see qh_ERR... in libqhull.h)
    -    all memory freed
    -
    -  notes:
    -    This can be called any number of times.
    -
    -  see:
    -    qh_call_qhull_once()
    -
    -*/
    -#if 0
    -{
    -  int dim;                  /* dimension of points */
    -  int numpoints;            /* number of points */
    -  coordT *points;           /* array of coordinates for each point */
    -  boolT ismalloc;           /* True if qhull should free points in qh_freeqhull() or reallocation */
    -  char flags[]= "qhull Tv"; /* option flags for qhull, see qh_opt.htm */
    -  FILE *outfile= stdout;    /* output from qh_produce_output()
    -                               use NULL to skip qh_produce_output() */
    -  FILE *errfile= stderr;    /* error messages from qhull code */
    -  int exitcode;             /* 0 if no error from qhull */
    -  facetT *facet;            /* set by FORALLfacets */
    -  int curlong, totlong;     /* memory remaining after qh_memfreeshort */
    -
    -#if qh_QHpointer  /* see user.h */
    -  if (qh_qh){
    -      printf ("QH6238: Qhull link error.  The global variable qh_qh was not initialized\n\
    -              to NULL by global.c.  Please compile this program with -Dqh_QHpointer_dllimport\n\
    -              as well as -Dqh_QHpointer, or use libqhullstatic, or use a different tool chain.\n\n");
    -      exit -1;
    -  }
    -#endif
    -
    -  /* initialize dim, numpoints, points[], ismalloc here */
    -  exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
    -                      flags, outfile, errfile);
    -  if (!exitcode) {                  /* if no error */
    -    /* 'qh facet_list' contains the convex hull */
    -    FORALLfacets {
    -       /* ... your code ... */
    -    }
    -  }
    -  qh_freeqhull(!qh_ALL);
    -  qh_memfreeshort(&curlong, &totlong);
    -  if (curlong || totlong)
    -    qh_fprintf(errfile, 7068, "qhull internal warning (main): did not free %d bytes of long memory(%d pieces)\n", totlong, curlong);
    -}
    -#endif
    -
    -/*---------------------------------
    -
    -  qh_new_qhull( dim, numpoints, points, ismalloc, qhull_cmd, outfile, errfile )
    -    build new qhull data structure and return exitcode (0 if no errors)
    -
    -  notes:
    -    do not modify points until finished with results.
    -      The qhull data structure contains pointers into the points array.
    -    do not call qhull functions before qh_new_qhull().
    -      The qhull data structure is not initialized until qh_new_qhull().
    -
    -    outfile may be null
    -    qhull_cmd must start with "qhull "
    -    projects points to a new point array for Delaunay triangulations ('d' and 'v')
    -    transforms points into a new point array for halfspace intersection ('H')
    -
    -
    -  To allow multiple, concurrent calls to qhull()
    -    - set qh_QHpointer in user.h
    -    - use qh_save_qhull and qh_restore_qhull to swap the global data structure between calls.
    -    - use qh_freeqhull(qh_ALL) to free intermediate convex hulls
    -
    -  see:
    -    user_eg.c for an example
    -*/
    -int qh_new_qhull(int dim, int numpoints, coordT *points, boolT ismalloc,
    -                char *qhull_cmd, FILE *outfile, FILE *errfile) {
    -  int exitcode, hulldim;
    -  boolT new_ismalloc;
    -  static boolT firstcall = True;
    -  coordT *new_points;
    -
    -  if (firstcall) {
    -    qh_meminit(errfile);
    -    firstcall= False;
    -  }
    -  if (strncmp(qhull_cmd,"qhull ", (size_t)6)) {
    -    qh_fprintf(errfile, 6186, "qhull error (qh_new_qhull): start qhull_cmd argument with \"qhull \"\n");
    -    qh_exit(qh_ERRinput);
    -  }
    -  qh_initqhull_start(NULL, outfile, errfile);
    -  trace1((qh ferr, 1044, "qh_new_qhull: build new Qhull for %d %d-d points with %s\n", numpoints, dim, qhull_cmd));
    -  exitcode = setjmp(qh errexit);
    -  if (!exitcode)
    -  {
    -    qh NOerrexit = False;
    -    qh_initflags(qhull_cmd);
    -    if (qh DELAUNAY)
    -      qh PROJECTdelaunay= True;
    -    if (qh HALFspace) {
    -      /* points is an array of halfspaces,
    -         the last coordinate of each halfspace is its offset */
    -      hulldim= dim-1;
    -      qh_setfeasible(hulldim);
    -      new_points= qh_sethalfspace_all(dim, numpoints, points, qh feasible_point);
    -      new_ismalloc= True;
    -      if (ismalloc)
    -        qh_free(points);
    -    }else {
    -      hulldim= dim;
    -      new_points= points;
    -      new_ismalloc= ismalloc;
    -    }
    -    qh_init_B(new_points, numpoints, hulldim, new_ismalloc);
    -    qh_qhull();
    -    qh_check_output();
    -    if (outfile) {
    -      qh_produce_output();
    -    }else {
    -      qh_prepare_output();
    -    }
    -    if (qh VERIFYoutput && !qh STOPpoint && !qh STOPcone)
    -      qh_check_points();
    -  }
    -  qh NOerrexit = True;
    -  return exitcode;
    -} /* new_qhull */
    -
    -/*---------------------------------
    -
    -  qh_errexit( exitcode, facet, ridge )
    -    report and exit from an error
    -    report facet and ridge if non-NULL
    -    reports useful information such as last point processed
    -    set qh.FORCEoutput to print neighborhood of facet
    -
    -  see:
    -    qh_errexit2() in libqhull.c for printing 2 facets
    -
    -  design:
    -    check for error within error processing
    -    compute qh.hulltime
    -    print facet and ridge (if any)
    -    report commandString, options, qh.furthest_id
    -    print summary and statistics (including precision statistics)
    -    if qh_ERRsingular
    -      print help text for singular data set
    -    exit program via long jump (if defined) or exit()
    -*/
    -void qh_errexit(int exitcode, facetT *facet, ridgeT *ridge) {
    -
    -  if (qh ERREXITcalled) {
    -    qh_fprintf(qh ferr, 8126, "\nqhull error while processing previous error.  Exit program\n");
    -    qh_exit(qh_ERRqhull);
    -  }
    -  qh ERREXITcalled= True;
    -  if (!qh QHULLfinished)
    -    qh hulltime= qh_CPUclock - qh hulltime;
    -  qh_errprint("ERRONEOUS", facet, NULL, ridge, NULL);
    -  qh_fprintf(qh ferr, 8127, "\nWhile executing: %s | %s\n", qh rbox_command, qh qhull_command);
    -  qh_fprintf(qh ferr, 8128, "Options selected for Qhull %s:\n%s\n", qh_version, qh qhull_options);
    -  if (qh furthest_id >= 0) {
    -    qh_fprintf(qh ferr, 8129, "Last point added to hull was p%d.", qh furthest_id);
    -    if (zzval_(Ztotmerge))
    -      qh_fprintf(qh ferr, 8130, "  Last merge was #%d.", zzval_(Ztotmerge));
    -    if (qh QHULLfinished)
    -      qh_fprintf(qh ferr, 8131, "\nQhull has finished constructing the hull.");
    -    else if (qh POSTmerging)
    -      qh_fprintf(qh ferr, 8132, "\nQhull has started post-merging.");
    -    qh_fprintf(qh ferr, 8133, "\n");
    -  }
    -  if (qh FORCEoutput && (qh QHULLfinished || (!facet && !ridge)))
    -    qh_produce_output();
    -  else if (exitcode != qh_ERRinput) {
    -    if (exitcode != qh_ERRsingular && zzval_(Zsetplane) > qh hull_dim+1) {
    -      qh_fprintf(qh ferr, 8134, "\nAt error exit:\n");
    -      qh_printsummary(qh ferr);
    -      if (qh PRINTstatistics) {
    -        qh_collectstatistics();
    -        qh_printstatistics(qh ferr, "at error exit");
    -        qh_memstatistics(qh ferr);
    -      }
    -    }
    -    if (qh PRINTprecision)
    -      qh_printstats(qh ferr, qhstat precision, NULL);
    -  }
    -  if (!exitcode)
    -    exitcode= qh_ERRqhull;
    -  else if (exitcode == qh_ERRsingular)
    -    qh_printhelp_singular(qh ferr);
    -  else if (exitcode == qh_ERRprec && !qh PREmerge)
    -    qh_printhelp_degenerate(qh ferr);
    -  if (qh NOerrexit) {
    -    qh_fprintf(qh ferr, 6187, "qhull error while ending program.  Exit program\n");
    -    qh_exit(qh_ERRqhull);
    -  }
    -  qh ERREXITcalled= False;
    -  qh NOerrexit= True;
    -  longjmp(qh errexit, exitcode);
    -} /* errexit */
    -
    -
    -/*---------------------------------
    -
    -  qh_errprint( fp, string, atfacet, otherfacet, atridge, atvertex )
    -    prints out the information of facets and ridges to fp
    -    also prints neighbors and geomview output
    -
    -  notes:
    -    except for string, any parameter may be NULL
    -*/
    -void qh_errprint(const char *string, facetT *atfacet, facetT *otherfacet, ridgeT *atridge, vertexT *atvertex) {
    -  int i;
    -
    -  if (atfacet) {
    -    qh_fprintf(qh ferr, 8135, "%s FACET:\n", string);
    -    qh_printfacet(qh ferr, atfacet);
    -  }
    -  if (otherfacet) {
    -    qh_fprintf(qh ferr, 8136, "%s OTHER FACET:\n", string);
    -    qh_printfacet(qh ferr, otherfacet);
    -  }
    -  if (atridge) {
    -    qh_fprintf(qh ferr, 8137, "%s RIDGE:\n", string);
    -    qh_printridge(qh ferr, atridge);
    -    if (atridge->top && atridge->top != atfacet && atridge->top != otherfacet)
    -      qh_printfacet(qh ferr, atridge->top);
    -    if (atridge->bottom
    -        && atridge->bottom != atfacet && atridge->bottom != otherfacet)
    -      qh_printfacet(qh ferr, atridge->bottom);
    -    if (!atfacet)
    -      atfacet= atridge->top;
    -    if (!otherfacet)
    -      otherfacet= otherfacet_(atridge, atfacet);
    -  }
    -  if (atvertex) {
    -    qh_fprintf(qh ferr, 8138, "%s VERTEX:\n", string);
    -    qh_printvertex(qh ferr, atvertex);
    -  }
    -  if (qh fout && qh FORCEoutput && atfacet && !qh QHULLfinished && !qh IStracing) {
    -    qh_fprintf(qh ferr, 8139, "ERRONEOUS and NEIGHBORING FACETS to output\n");
    -    for (i=0; i < qh_PRINTEND; i++)  /* use fout for geomview output */
    -      qh_printneighborhood(qh fout, qh PRINTout[i], atfacet, otherfacet,
    -                            !qh_ALL);
    -  }
    -} /* errprint */
    -
    -
    -/*---------------------------------
    -
    -  qh_printfacetlist( fp, facetlist, facets, printall )
    -    print all fields for a facet list and/or set of facets to fp
    -    if !printall,
    -      only prints good facets
    -
    -  notes:
    -    also prints all vertices
    -*/
    -void qh_printfacetlist(facetT *facetlist, setT *facets, boolT printall) {
    -  facetT *facet, **facetp;
    -
    -  qh_printbegin(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
    -  FORALLfacet_(facetlist)
    -    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
    -  FOREACHfacet_(facets)
    -    qh_printafacet(qh ferr, qh_PRINTfacets, facet, printall);
    -  qh_printend(qh ferr, qh_PRINTfacets, facetlist, facets, printall);
    -} /* printfacetlist */
    -
    -
    -/*---------------------------------
    -
    -  qh_printhelp_degenerate( fp )
    -    prints descriptive message for precision error
    -
    -  notes:
    -    no message if qh_QUICKhelp
    -*/
    -void qh_printhelp_degenerate(FILE *fp) {
    -
    -  if (qh MERGEexact || qh PREmerge || qh JOGGLEmax < REALmax/2)
    -    qh_fprintf(fp, 9368, "\n\
    -A Qhull error has occurred.  Qhull should have corrected the above\n\
    -precision error.  Please send the input and all of the output to\n\
    -qhull_bug@qhull.org\n");
    -  else if (!qh_QUICKhelp) {
    -    qh_fprintf(fp, 9369, "\n\
    -Precision problems were detected during construction of the convex hull.\n\
    -This occurs because convex hull algorithms assume that calculations are\n\
    -exact, but floating-point arithmetic has roundoff errors.\n\
    -\n\
    -To correct for precision problems, do not use 'Q0'.  By default, Qhull\n\
    -selects 'C-0' or 'Qx' and merges non-convex facets.  With option 'QJ',\n\
    -Qhull joggles the input to prevent precision problems.  See \"Imprecision\n\
    -in Qhull\" (qh-impre.htm).\n\
    -\n\
    -If you use 'Q0', the output may include\n\
    -coplanar ridges, concave ridges, and flipped facets.  In 4-d and higher,\n\
    -Qhull may produce a ridge with four neighbors or two facets with the same \n\
    -vertices.  Qhull reports these events when they occur.  It stops when a\n\
    -concave ridge, flipped facet, or duplicate facet occurs.\n");
    -#if REALfloat
    -    qh_fprintf(fp, 9370, "\
    -\n\
    -Qhull is currently using single precision arithmetic.  The following\n\
    -will probably remove the precision problems:\n\
    -  - recompile qhull for realT precision(#define REALfloat 0 in user.h).\n");
    -#endif
    -    if (qh DELAUNAY && !qh SCALElast && qh MAXabs_coord > 1e4)
    -      qh_fprintf(fp, 9371, "\
    -\n\
    -When computing the Delaunay triangulation of coordinates > 1.0,\n\
    -  - use 'Qbb' to scale the last coordinate to [0,m] (max previous coordinate)\n");
    -    if (qh DELAUNAY && !qh ATinfinity)
    -      qh_fprintf(fp, 9372, "\
    -When computing the Delaunay triangulation:\n\
    -  - use 'Qz' to add a point at-infinity.  This reduces precision problems.\n");
    -
    -    qh_fprintf(fp, 9373, "\
    -\n\
    -If you need triangular output:\n\
    -  - use option 'Qt' to triangulate the output\n\
    -  - use option 'QJ' to joggle the input points and remove precision errors\n\
    -  - use option 'Ft'.  It triangulates non-simplicial facets with added points.\n\
    -\n\
    -If you must use 'Q0',\n\
    -try one or more of the following options.  They can not guarantee an output.\n\
    -  - use 'QbB' to scale the input to a cube.\n\
    -  - use 'Po' to produce output and prevent partitioning for flipped facets\n\
    -  - use 'V0' to set min. distance to visible facet as 0 instead of roundoff\n\
    -  - use 'En' to specify a maximum roundoff error less than %2.2g.\n\
    -  - options 'Qf', 'Qbb', and 'QR0' may also help\n",
    -               qh DISTround);
    -    qh_fprintf(fp, 9374, "\
    -\n\
    -To guarantee simplicial output:\n\
    -  - use option 'Qt' to triangulate the output\n\
    -  - use option 'QJ' to joggle the input points and remove precision errors\n\
    -  - use option 'Ft' to triangulate the output by adding points\n\
    -  - use exact arithmetic (see \"Imprecision in Qhull\", qh-impre.htm)\n\
    -");
    -  }
    -} /* printhelp_degenerate */
    -
    -
    -/*---------------------------------
    -
    -  qh_printhelp_narrowhull( minangle )
    -    Warn about a narrow hull
    -
    -  notes:
    -    Alternatively, reduce qh_WARNnarrow in user.h
    -
    -*/
    -void qh_printhelp_narrowhull(FILE *fp, realT minangle) {
    -
    -    qh_fprintf(fp, 9375, "qhull precision warning: \n\
    -The initial hull is narrow (cosine of min. angle is %.16f).\n\
    -Is the input lower dimensional (e.g., on a plane in 3-d)?  Qhull may\n\
    -produce a wide facet.  Options 'QbB' (scale to unit box) or 'Qbb' (scale\n\
    -last coordinate) may remove this warning.  Use 'Pp' to skip this warning.\n\
    -See 'Limitations' in qh-impre.htm.\n",
    -          -minangle);   /* convert from angle between normals to angle between facets */
    -} /* printhelp_narrowhull */
    -
    -/*---------------------------------
    -
    -  qh_printhelp_singular( fp )
    -    prints descriptive message for singular input
    -*/
    -void qh_printhelp_singular(FILE *fp) {
    -  facetT *facet;
    -  vertexT *vertex, **vertexp;
    -  realT min, max, *coord, dist;
    -  int i,k;
    -
    -  qh_fprintf(fp, 9376, "\n\
    -The input to qhull appears to be less than %d dimensional, or a\n\
    -computation has overflowed.\n\n\
    -Qhull could not construct a clearly convex simplex from points:\n",
    -           qh hull_dim);
    -  qh_printvertexlist(fp, "", qh facet_list, NULL, qh_ALL);
    -  if (!qh_QUICKhelp)
    -    qh_fprintf(fp, 9377, "\n\
    -The center point is coplanar with a facet, or a vertex is coplanar\n\
    -with a neighboring facet.  The maximum round off error for\n\
    -computing distances is %2.2g.  The center point, facets and distances\n\
    -to the center point are as follows:\n\n", qh DISTround);
    -  qh_printpointid(fp, "center point", qh hull_dim, qh interior_point, -1);
    -  qh_fprintf(fp, 9378, "\n");
    -  FORALLfacets {
    -    qh_fprintf(fp, 9379, "facet");
    -    FOREACHvertex_(facet->vertices)
    -      qh_fprintf(fp, 9380, " p%d", qh_pointid(vertex->point));
    -    zinc_(Zdistio);
    -    qh_distplane(qh interior_point, facet, &dist);
    -    qh_fprintf(fp, 9381, " distance= %4.2g\n", dist);
    -  }
    -  if (!qh_QUICKhelp) {
    -    if (qh HALFspace)
    -      qh_fprintf(fp, 9382, "\n\
    -These points are the dual of the given halfspaces.  They indicate that\n\
    -the intersection is degenerate.\n");
    -    qh_fprintf(fp, 9383,"\n\
    -These points either have a maximum or minimum x-coordinate, or\n\
    -they maximize the determinant for k coordinates.  Trial points\n\
    -are first selected from points that maximize a coordinate.\n");
    -    if (qh hull_dim >= qh_INITIALmax)
    -      qh_fprintf(fp, 9384, "\n\
    -Because of the high dimension, the min x-coordinate and max-coordinate\n\
    -points are used if the determinant is non-zero.  Option 'Qs' will\n\
    -do a better, though much slower, job.  Instead of 'Qs', you can change\n\
    -the points by randomly rotating the input with 'QR0'.\n");
    -  }
    -  qh_fprintf(fp, 9385, "\nThe min and max coordinates for each dimension are:\n");
    -  for (k=0; k < qh hull_dim; k++) {
    -    min= REALmax;
    -    max= -REALmin;
    -    for (i=qh num_points, coord= qh first_point+k; i--; coord += qh hull_dim) {
    -      maximize_(max, *coord);
    -      minimize_(min, *coord);
    -    }
    -    qh_fprintf(fp, 9386, "  %d:  %8.4g  %8.4g  difference= %4.4g\n", k, min, max, max-min);
    -  }
    -  if (!qh_QUICKhelp) {
    -    qh_fprintf(fp, 9387, "\n\
    -If the input should be full dimensional, you have several options that\n\
    -may determine an initial simplex:\n\
    -  - use 'QJ'  to joggle the input and make it full dimensional\n\
    -  - use 'QbB' to scale the points to the unit cube\n\
    -  - use 'QR0' to randomly rotate the input for different maximum points\n\
    -  - use 'Qs'  to search all points for the initial simplex\n\
    -  - use 'En'  to specify a maximum roundoff error less than %2.2g.\n\
    -  - trace execution with 'T3' to see the determinant for each point.\n",
    -                     qh DISTround);
    -#if REALfloat
    -    qh_fprintf(fp, 9388, "\
    -  - recompile qhull for realT precision(#define REALfloat 0 in libqhull.h).\n");
    -#endif
    -    qh_fprintf(fp, 9389, "\n\
    -If the input is lower dimensional:\n\
    -  - use 'QJ' to joggle the input and make it full dimensional\n\
    -  - use 'Qbk:0Bk:0' to delete coordinate k from the input.  You should\n\
    -    pick the coordinate with the least range.  The hull will have the\n\
    -    correct topology.\n\
    -  - determine the flat containing the points, rotate the points\n\
    -    into a coordinate plane, and delete the other coordinates.\n\
    -  - add one or more points to make the input full dimensional.\n\
    -");
    -  }
    -} /* printhelp_singular */
    -
    -/*---------------------------------
    -
    -  qh_user_memsizes()
    -    allocate up to 10 additional, quick allocation sizes
    -
    -  notes:
    -    increase maximum number of allocations in qh_initqhull_mem()
    -*/
    -void qh_user_memsizes(void) {
    -
    -  /* qh_memsize(size); */
    -} /* user_memsizes */
    diff --git a/extern/qhull/user.h b/extern/qhull/user.h
    deleted file mode 100644
    index 45b3202696a3..000000000000
    --- a/extern/qhull/user.h
    +++ /dev/null
    @@ -1,855 +0,0 @@
    -/*
      ---------------------------------
    -
    -   user.h
    -   user redefinable constants
    -
    -   see qh-user.htm.  see COPYING for copyright information.
    -
    -   before reading any code, review libqhull.h for data structure definitions and
    -   the "qh" macro.
    -
    -Sections:
    -   ============= qhull library constants ======================
    -   ============= data types and configuration macros ==========
    -   ============= performance related constants ================
    -   ============= memory constants =============================
    -   ============= joggle constants =============================
    -   ============= conditional compilation ======================
    -   ============= -merge constants- ============================
    -
    -Code flags --
    -  NOerrors -- the code does not call qh_errexit()
    -  WARN64 -- the code may be incompatible with 64-bit pointers
    -
    -*/
    -
    -#include 
    -
    -#ifndef qhDEFuser
    -#define qhDEFuser 1
    -
    -/*============================================================*/
    -/*============= qhull library constants ======================*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -  FILENAMElen -- max length for TI and TO filenames
    -
    -*/
    -
    -#define qh_FILENAMElen 500
    -
    -/*----------------------------------
    -
    -  msgcode -- Unique message codes for qh_fprintf
    -
    -  If add new messages, assign these values and increment.
    -
    -  def counters =  [27, 1047, 2059, 3025, 4068, 5003,
    -     6241, 7079, 8143, 9410, 10000, 11026]
    -
    -  See: qh_ERR* [libqhull.h]
    -*/
    -
    -#define MSG_TRACE0 0
    -#define MSG_TRACE1 1000
    -#define MSG_TRACE2 2000
    -#define MSG_TRACE3 3000
    -#define MSG_TRACE4 4000
    -#define MSG_TRACE5 5000
    -#define MSG_ERROR  6000   /* errors written to qh.ferr */
    -#define MSG_WARNING 7000
    -#define MSG_STDERR  8000  /* log messages Written to qh.ferr */
    -#define MSG_OUTPUT  9000
    -#define MSG_QHULL_ERROR 10000 /* errors thrown by QhullError [QhullError.h] */
    -#define MSG_FIXUP  11000  /* FIXUP QH11... */
    -#define MSG_MAXLEN  3000 /* qh_printhelp_degenerate() in user.c */
    -
    -
    -/*----------------------------------
    -
    -  qh_OPTIONline -- max length of an option line 'FO'
    -*/
    -#define qh_OPTIONline 80
    -
    -/*============================================================*/
    -/*============= data types and configuration macros ==========*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -  realT
    -    set the size of floating point numbers
    -
    -  qh_REALdigits
    -    maximimum number of significant digits
    -
    -  qh_REAL_1, qh_REAL_2n, qh_REAL_3n
    -    format strings for printf
    -
    -  qh_REALmax, qh_REALmin
    -    maximum and minimum (near zero) values
    -
    -  qh_REALepsilon
    -    machine roundoff.  Maximum roundoff error for addition and multiplication.
    -
    -  notes:
    -   Select whether to store floating point numbers in single precision (float)
    -   or double precision (double).
    -
    -   Use 'float' to save about 8% in time and 25% in space.  This is particularly
    -   helpful if high-d where convex hulls are space limited.  Using 'float' also
    -   reduces the printed size of Qhull's output since numbers have 8 digits of
    -   precision.
    -
    -   Use 'double' when greater arithmetic precision is needed.  This is needed
    -   for Delaunay triangulations and Voronoi diagrams when you are not merging
    -   facets.
    -
    -   If 'double' gives insufficient precision, your data probably includes
    -   degeneracies.  If so you should use facet merging (done by default)
    -   or exact arithmetic (see imprecision section of manual, qh-impre.htm).
    -   You may also use option 'Po' to force output despite precision errors.
    -
    -   You may use 'long double', but many format statements need to be changed
    -   and you may need a 'long double' square root routine.  S. Grundmann
    -   (sg@eeiwzb.et.tu-dresden.de) has done this.  He reports that the code runs
    -   much slower with little gain in precision.
    -
    -   WARNING: on some machines,    int f(){realT a= REALmax;return (a == REALmax);}
    -      returns False.  Use (a > REALmax/2) instead of (a == REALmax).
    -
    -   REALfloat =   1      all numbers are 'float' type
    -             =   0      all numbers are 'double' type
    -*/
    -#define REALfloat 0
    -
    -#if (REALfloat == 1)
    -#define realT float
    -#define REALmax FLT_MAX
    -#define REALmin FLT_MIN
    -#define REALepsilon FLT_EPSILON
    -#define qh_REALdigits 8   /* maximum number of significant digits */
    -#define qh_REAL_1 "%6.8g "
    -#define qh_REAL_2n "%6.8g %6.8g\n"
    -#define qh_REAL_3n "%6.8g %6.8g %6.8g\n"
    -
    -#elif (REALfloat == 0)
    -#define realT double
    -#define REALmax DBL_MAX
    -#define REALmin DBL_MIN
    -#define REALepsilon DBL_EPSILON
    -#define qh_REALdigits 16    /* maximum number of significant digits */
    -#define qh_REAL_1 "%6.16g "
    -#define qh_REAL_2n "%6.16g %6.16g\n"
    -#define qh_REAL_3n "%6.16g %6.16g %6.16g\n"
    -
    -#else
    -#error unknown float option
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_CPUclock
    -    define the clock() function for reporting the total time spent by Qhull
    -    returns CPU ticks as a 'long int'
    -    qh_CPUclock is only used for reporting the total time spent by Qhull
    -
    -  qh_SECticks
    -    the number of clock ticks per second
    -
    -  notes:
    -    looks for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or assumes microseconds
    -    to define a custom clock, set qh_CLOCKtype to 0
    -
    -    if your system does not use clock() to return CPU ticks, replace
    -    qh_CPUclock with the corresponding function.  It is converted
    -    to 'unsigned long' to prevent wrap-around during long runs.  By default,
    -     defines clock_t as 'long'
    -
    -   Set qh_CLOCKtype to
    -
    -     1          for CLOCKS_PER_SEC, CLOCKS_PER_SECOND, or microsecond
    -                Note:  may fail if more than 1 hour elapsed time
    -
    -     2          use qh_clock() with POSIX times() (see global.c)
    -*/
    -#define qh_CLOCKtype 1  /* change to the desired number */
    -
    -#if (qh_CLOCKtype == 1)
    -
    -#if defined(CLOCKS_PER_SECOND)
    -#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    -#define qh_SECticks CLOCKS_PER_SECOND
    -
    -#elif defined(CLOCKS_PER_SEC)
    -#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    -#define qh_SECticks CLOCKS_PER_SEC
    -
    -#elif defined(CLK_TCK)
    -#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    -#define qh_SECticks CLK_TCK
    -
    -#else
    -#define qh_CPUclock    ((unsigned long)clock())  /* return CPU clock */
    -#define qh_SECticks 1E6
    -#endif
    -
    -#elif (qh_CLOCKtype == 2)
    -#define qh_CPUclock    qh_clock()  /* return CPU clock */
    -#define qh_SECticks 100
    -
    -#else /* qh_CLOCKtype == ? */
    -#error unknown clock option
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_RANDOMtype, qh_RANDOMmax, qh_RANDOMseed
    -    define random number generator
    -
    -    qh_RANDOMint generates a random integer between 0 and qh_RANDOMmax.
    -    qh_RANDOMseed sets the random number seed for qh_RANDOMint
    -
    -  Set qh_RANDOMtype (default 5) to:
    -    1       for random() with 31 bits (UCB)
    -    2       for rand() with RAND_MAX or 15 bits (system 5)
    -    3       for rand() with 31 bits (Sun)
    -    4       for lrand48() with 31 bits (Solaris)
    -    5       for qh_rand() with 31 bits (included with Qhull)
    -
    -  notes:
    -    Random numbers are used by rbox to generate point sets.  Random
    -    numbers are used by Qhull to rotate the input ('QRn' option),
    -    simulate a randomized algorithm ('Qr' option), and to simulate
    -    roundoff errors ('Rn' option).
    -
    -    Random number generators differ between systems.  Most systems provide
    -    rand() but the period varies.  The period of rand() is not critical
    -    since qhull does not normally use random numbers.
    -
    -    The default generator is Park & Miller's minimal standard random
    -    number generator [CACM 31:1195 '88].  It is included with Qhull.
    -
    -    If qh_RANDOMmax is wrong, qhull will report a warning and Geomview
    -    output will likely be invisible.
    -*/
    -#define qh_RANDOMtype 5   /* *** change to the desired number *** */
    -
    -#if (qh_RANDOMtype == 1)
    -#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, random()/MAX */
    -#define qh_RANDOMint random()
    -#define qh_RANDOMseed_(seed) srandom(seed);
    -
    -#elif (qh_RANDOMtype == 2)
    -#ifdef RAND_MAX
    -#define qh_RANDOMmax ((realT)RAND_MAX)
    -#else
    -#define qh_RANDOMmax ((realT)32767)   /* 15 bits (System 5) */
    -#endif
    -#define qh_RANDOMint  rand()
    -#define qh_RANDOMseed_(seed) srand((unsigned)seed);
    -
    -#elif (qh_RANDOMtype == 3)
    -#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, Sun */
    -#define qh_RANDOMint  rand()
    -#define qh_RANDOMseed_(seed) srand((unsigned)seed);
    -
    -#elif (qh_RANDOMtype == 4)
    -#define qh_RANDOMmax ((realT)0x7fffffffUL)  /* 31 bits, lrand38()/MAX */
    -#define qh_RANDOMint lrand48()
    -#define qh_RANDOMseed_(seed) srand48(seed);
    -
    -#elif (qh_RANDOMtype == 5)
    -#define qh_RANDOMmax ((realT)2147483646UL)  /* 31 bits, qh_rand/MAX */
    -#define qh_RANDOMint qh_rand()
    -#define qh_RANDOMseed_(seed) qh_srand(seed);
    -/* unlike rand(), never returns 0 */
    -
    -#else
    -#error: unknown random option
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_ORIENTclock
    -    0 for inward pointing normals by Geomview convention
    -*/
    -#define qh_ORIENTclock 0
    -
    -
    -/*============================================================*/
    -/*============= joggle constants =============================*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -qh_JOGGLEdefault
    -default qh.JOGGLEmax is qh.DISTround * qh_JOGGLEdefault
    -
    -notes:
    -rbox s r 100 | qhull QJ1e-15 QR0 generates 90% faults at distround 7e-16
    -rbox s r 100 | qhull QJ1e-14 QR0 generates 70% faults
    -rbox s r 100 | qhull QJ1e-13 QR0 generates 35% faults
    -rbox s r 100 | qhull QJ1e-12 QR0 generates 8% faults
    -rbox s r 100 | qhull QJ1e-11 QR0 generates 1% faults
    -rbox s r 100 | qhull QJ1e-10 QR0 generates 0% faults
    -rbox 1000 W0 | qhull QJ1e-12 QR0 generates 86% faults
    -rbox 1000 W0 | qhull QJ1e-11 QR0 generates 20% faults
    -rbox 1000 W0 | qhull QJ1e-10 QR0 generates 2% faults
    -the later have about 20 points per facet, each of which may interfere
    -
    -pick a value large enough to avoid retries on most inputs
    -*/
    -#define qh_JOGGLEdefault 30000.0
    -
    -/*----------------------------------
    -
    -qh_JOGGLEincrease
    -factor to increase qh.JOGGLEmax on qh_JOGGLEretry or qh_JOGGLEagain
    -*/
    -#define qh_JOGGLEincrease 10.0
    -
    -/*----------------------------------
    -
    -qh_JOGGLEretry
    -if ZZretry = qh_JOGGLEretry, increase qh.JOGGLEmax
    -
    -notes:
    -try twice at the original value in case of bad luck the first time
    -*/
    -#define qh_JOGGLEretry 2
    -
    -/*----------------------------------
    -
    -qh_JOGGLEagain
    -every following qh_JOGGLEagain, increase qh.JOGGLEmax
    -
    -notes:
    -1 is OK since it's already failed qh_JOGGLEretry times
    -*/
    -#define qh_JOGGLEagain 1
    -
    -/*----------------------------------
    -
    -qh_JOGGLEmaxincrease
    -maximum qh.JOGGLEmax due to qh_JOGGLEincrease
    -relative to qh.MAXwidth
    -
    -notes:
    -qh.joggleinput will retry at this value until qh_JOGGLEmaxretry
    -*/
    -#define qh_JOGGLEmaxincrease 1e-2
    -
    -/*----------------------------------
    -
    -qh_JOGGLEmaxretry
    -stop after qh_JOGGLEmaxretry attempts
    -*/
    -#define qh_JOGGLEmaxretry 100
    -
    -/*============================================================*/
    -/*============= performance related constants ================*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -  qh_HASHfactor
    -    total hash slots / used hash slots.  Must be at least 1.1.
    -
    -  notes:
    -    =2 for at worst 50% occupancy for qh hash_table and normally 25% occupancy
    -*/
    -#define qh_HASHfactor 2
    -
    -/*----------------------------------
    -
    -  qh_VERIFYdirect
    -    with 'Tv' verify all points against all facets if op count is smaller
    -
    -  notes:
    -    if greater, calls qh_check_bestdist() instead
    -*/
    -#define qh_VERIFYdirect 1000000
    -
    -/*----------------------------------
    -
    -  qh_INITIALsearch
    -     if qh_INITIALmax, search points up to this dimension
    -*/
    -#define qh_INITIALsearch 6
    -
    -/*----------------------------------
    -
    -  qh_INITIALmax
    -    if dim >= qh_INITIALmax, use min/max coordinate points for initial simplex
    -
    -  notes:
    -    from points with non-zero determinants
    -    use option 'Qs' to override (much slower)
    -*/
    -#define qh_INITIALmax 8
    -
    -/*============================================================*/
    -/*============= memory constants =============================*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -  qh_MEMalign
    -    memory alignment for qh_meminitbuffers() in global.c
    -
    -  notes:
    -    to avoid bus errors, memory allocation must consider alignment requirements.
    -    malloc() automatically takes care of alignment.   Since mem.c manages
    -    its own memory, we need to explicitly specify alignment in
    -    qh_meminitbuffers().
    -
    -    A safe choice is sizeof(double).  sizeof(float) may be used if doubles
    -    do not occur in data structures and pointers are the same size.  Be careful
    -    of machines (e.g., DEC Alpha) with large pointers.
    -
    -    If using gcc, best alignment is
    -              #define qh_MEMalign fmax_(__alignof__(realT),__alignof__(void *))
    -*/
    -#define qh_MEMalign ((int)(fmax_(sizeof(realT), sizeof(void *))))
    -
    -/*----------------------------------
    -
    -  qh_MEMbufsize
    -    size of additional memory buffers
    -
    -  notes:
    -    used for qh_meminitbuffers() in global.c
    -*/
    -#define qh_MEMbufsize 0x10000       /* allocate 64K memory buffers */
    -
    -/*----------------------------------
    -
    -  qh_MEMinitbuf
    -    size of initial memory buffer
    -
    -  notes:
    -    use for qh_meminitbuffers() in global.c
    -*/
    -#define qh_MEMinitbuf 0x20000      /* initially allocate 128K buffer */
    -
    -/*----------------------------------
    -
    -  qh_INFINITE
    -    on output, indicates Voronoi center at infinity
    -*/
    -#define qh_INFINITE  -10.101
    -
    -/*----------------------------------
    -
    -  qh_DEFAULTbox
    -    default box size (Geomview expects 0.5)
    -
    -  qh_DEFAULTbox
    -    default box size for integer coorindate (rbox only)
    -*/
    -#define qh_DEFAULTbox 0.5
    -#define qh_DEFAULTzbox 1e6
    -
    -/*============================================================*/
    -/*============= conditional compilation ======================*/
    -/*============================================================*/
    -
    -/*----------------------------------
    -
    -  __cplusplus
    -    defined by C++ compilers
    -
    -  __MSC_VER
    -    defined by Microsoft Visual C++
    -
    -  __MWERKS__ && __POWERPC__
    -    defined by Metrowerks when compiling for the Power Macintosh
    -
    -  __STDC__
    -    defined for strict ANSI C
    -*/
    -
    -/*----------------------------------
    -
    -  qh_COMPUTEfurthest
    -    compute furthest distance to an outside point instead of storing it with the facet
    -    =1 to compute furthest
    -
    -  notes:
    -    computing furthest saves memory but costs time
    -      about 40% more distance tests for partitioning
    -      removes facet->furthestdist
    -*/
    -#define qh_COMPUTEfurthest 0
    -
    -/*----------------------------------
    -
    -  qh_KEEPstatistics
    -    =0 removes most of statistic gathering and reporting
    -
    -  notes:
    -    if 0, code size is reduced by about 4%.
    -*/
    -#define qh_KEEPstatistics 1
    -
    -/*----------------------------------
    -
    -  qh_MAXoutside
    -    record outer plane for each facet
    -    =1 to record facet->maxoutside
    -
    -  notes:
    -    this takes a realT per facet and slightly slows down qhull
    -    it produces better outer planes for geomview output
    -*/
    -#define qh_MAXoutside 1
    -
    -/*----------------------------------
    -
    -  qh_NOmerge
    -    disables facet merging if defined
    -
    -  notes:
    -    This saves about 10% space.
    -
    -    Unless 'Q0'
    -      qh_NOmerge sets 'QJ' to avoid precision errors
    -
    -    #define qh_NOmerge
    -
    -  see:
    -    qh_NOmem in mem.c
    -
    -    see user.c/user_eg.c for removing io.o
    -*/
    -
    -/*----------------------------------
    -
    -  qh_NOtrace
    -    no tracing if defined
    -
    -  notes:
    -    This saves about 5% space.
    -
    -    #define qh_NOtrace
    -*/
    -
    -/*----------------------------------
    -
    -  qh_QHpointer
    -    access global data with pointer or static structure
    -
    -  qh_QHpointer  = 1     access globals via a pointer to allocated memory
    -                        enables qh_saveqhull() and qh_restoreqhull()
    -                        [2010, gcc] costs about 4% in time and 4% in space
    -                        [2003, msvc] costs about 8% in time and 2% in space
    -
    -                = 0     qh_qh and qh_qhstat are static data structures
    -                        only one instance of qhull() can be active at a time
    -                        default value
    -
    -  qh_QHpointer_dllimport and qh_dllimport define qh_qh as __declspec(dllimport) [libqhull.h]
    -  It is required for msvc-2005.  It is not needed for gcc.
    -
    -  notes:
    -    all global variables for qhull are in qh, qhmem, and qhstat
    -    qh is defined in libqhull.h
    -    qhmem is defined in mem.h
    -    qhstat is defined in stat.h
    -    C++ build defines qh_QHpointer [libqhullp.pro, libqhullcpp.pro]
    -
    -  see:
    -    user_eg.c for an example
    -*/
    -#ifdef qh_QHpointer
    -#if qh_dllimport
    -#error QH6207 Qhull error: Use qh_QHpointer_dllimport instead of qh_dllimport with qh_QHpointer
    -#endif
    -#else
    -#define qh_QHpointer 0
    -#if qh_QHpointer_dllimport
    -#error QH6234 Qhull error: Use qh_dllimport instead of qh_QHpointer_dllimport when qh_QHpointer is not defined
    -#endif
    -#endif
    -#if 0  /* sample code */
    -    qhT *oldqhA, *oldqhB;
    -
    -    exitcode= qh_new_qhull(dim, numpoints, points, ismalloc,
    -                      flags, outfile, errfile);
    -    /* use results from first call to qh_new_qhull */
    -    oldqhA= qh_save_qhull();
    -    exitcode= qh_new_qhull(dimB, numpointsB, pointsB, ismalloc,
    -                      flags, outfile, errfile);
    -    /* use results from second call to qh_new_qhull */
    -    oldqhB= qh_save_qhull();
    -    qh_restore_qhull(&oldqhA);
    -    /* use results from first call to qh_new_qhull */
    -    qh_freeqhull(qh_ALL);  /* frees all memory used by first call */
    -    qh_restore_qhull(&oldqhB);
    -    /* use results from second call to qh_new_qhull */
    -    qh_freeqhull(!qh_ALL); /* frees long memory used by second call */
    -    qh_memfreeshort(&curlong, &totlong);  /* frees short memory and memory allocator */
    -#endif
    -
    -/*----------------------------------
    -
    -  qh_QUICKhelp
    -    =1 to use abbreviated help messages, e.g., for degenerate inputs
    -*/
    -#define qh_QUICKhelp    0
    -
    -/*============================================================*/
    -/*============= -merge constants- ============================*/
    -/*============================================================*/
    -/*
    -   These constants effect facet merging.  You probably will not need
    -   to modify them.  They effect the performance of facet merging.
    -*/
    -
    -/*----------------------------------
    -
    -  qh_DIMmergeVertex
    -    max dimension for vertex merging (it is not effective in high-d)
    -*/
    -#define qh_DIMmergeVertex 6
    -
    -/*----------------------------------
    -
    -  qh_DIMreduceBuild
    -     max dimension for vertex reduction during build (slow in high-d)
    -*/
    -#define qh_DIMreduceBuild 5
    -
    -/*----------------------------------
    -
    -  qh_BESTcentrum
    -     if > 2*dim+n vertices, qh_findbestneighbor() tests centrums (faster)
    -     else, qh_findbestneighbor() tests all vertices (much better merges)
    -
    -  qh_BESTcentrum2
    -     if qh_BESTcentrum2 * DIM3 + BESTcentrum < #vertices tests centrums
    -*/
    -#define qh_BESTcentrum 20
    -#define qh_BESTcentrum2 2
    -
    -/*----------------------------------
    -
    -  qh_BESTnonconvex
    -    if > dim+n neighbors, qh_findbestneighbor() tests nonconvex ridges.
    -
    -  notes:
    -    It is needed because qh_findbestneighbor is slow for large facets
    -*/
    -#define qh_BESTnonconvex 15
    -
    -/*----------------------------------
    -
    -  qh_MAXnewmerges
    -    if >n newmerges, qh_merge_nonconvex() calls qh_reducevertices_centrums.
    -
    -  notes:
    -    It is needed because postmerge can merge many facets at once
    -*/
    -#define qh_MAXnewmerges 2
    -
    -/*----------------------------------
    -
    -  qh_MAXnewcentrum
    -    if <= dim+n vertices (n approximates the number of merges),
    -      reset the centrum in qh_updatetested() and qh_mergecycle_facets()
    -
    -  notes:
    -    needed to reduce cost and because centrums may move too much if
    -    many vertices in high-d
    -*/
    -#define qh_MAXnewcentrum 5
    -
    -/*----------------------------------
    -
    -  qh_COPLANARratio
    -    for 3-d+ merging, qh.MINvisible is n*premerge_centrum
    -
    -  notes:
    -    for non-merging, it's DISTround
    -*/
    -#define qh_COPLANARratio 3
    -
    -/*----------------------------------
    -
    -  qh_DISToutside
    -    When is a point clearly outside of a facet?
    -    Stops search in qh_findbestnew or qh_partitionall
    -    qh_findbest uses qh.MINoutside since since it is only called if no merges.
    -
    -  notes:
    -    'Qf' always searches for best facet
    -    if !qh.MERGING, same as qh.MINoutside.
    -    if qh_USEfindbestnew, increase value since neighboring facets may be ill-behaved
    -      [Note: Zdelvertextot occurs normally with interior points]
    -            RBOX 1000 s Z1 G1e-13 t1001188774 | QHULL Tv
    -    When there is a sharp edge, need to move points to a
    -    clearly good facet; otherwise may be lost in another partitioning.
    -    if too big then O(n^2) behavior for partitioning in cone
    -    if very small then important points not processed
    -    Needed in qh_partitionall for
    -      RBOX 1000 s Z1 G1e-13 t1001032651 | QHULL Tv
    -    Needed in qh_findbestnew for many instances of
    -      RBOX 1000 s Z1 G1e-13 t | QHULL Tv
    -
    -  See:
    -    qh_DISToutside -- when is a point clearly outside of a facet
    -    qh_SEARCHdist -- when is facet coplanar with the best facet?
    -    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    -*/
    -#define qh_DISToutside ((qh_USEfindbestnew ? 2 : 1) * \
    -     fmax_((qh MERGING ? 2 : 1)*qh MINoutside, qh max_outside))
    -
    -/*----------------------------------
    -
    -  qh_RATIOnearinside
    -    ratio of qh.NEARinside to qh.ONEmerge for retaining inside points for
    -    qh_check_maxout().
    -
    -  notes:
    -    This is overkill since do not know the correct value.
    -    It effects whether 'Qc' reports all coplanar points
    -    Not used for 'd' since non-extreme points are coplanar
    -*/
    -#define qh_RATIOnearinside 5
    -
    -/*----------------------------------
    -
    -  qh_SEARCHdist
    -    When is a facet coplanar with the best facet?
    -    qh_findbesthorizon: all coplanar facets of the best facet need to be searched.
    -
    -  See:
    -    qh_DISToutside -- when is a point clearly outside of a facet
    -    qh_SEARCHdist -- when is facet coplanar with the best facet?
    -    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    -*/
    -#define qh_SEARCHdist ((qh_USEfindbestnew ? 2 : 1) * \
    -      (qh max_outside + 2 * qh DISTround + fmax_( qh MINvisible, qh MAXcoplanar)));
    -
    -/*----------------------------------
    -
    -  qh_USEfindbestnew
    -     Always use qh_findbestnew for qh_partitionpoint, otherwise use
    -     qh_findbestnew if merged new facet or sharpnewfacets.
    -
    -  See:
    -    qh_DISToutside -- when is a point clearly outside of a facet
    -    qh_SEARCHdist -- when is facet coplanar with the best facet?
    -    qh_USEfindbestnew -- when to use qh_findbestnew for qh_partitionpoint()
    -*/
    -#define qh_USEfindbestnew (zzval_(Ztotmerge) > 50)
    -
    -/*----------------------------------
    -
    -  qh_WIDEcoplanar
    -    n*MAXcoplanar or n*MINvisible for a WIDEfacet
    -
    -    if vertex is further than qh.WIDEfacet from the hyperplane
    -    then its ridges are not counted in computing the area, and
    -    the facet's centrum is frozen.
    -
    -  notes:
    -   qh.WIDEfacet= max(qh.MAXoutside,qh_WIDEcoplanar*qh.MAXcoplanar,
    -      qh_WIDEcoplanar * qh.MINvisible);
    -*/
    -#define qh_WIDEcoplanar 6
    -
    -/*----------------------------------
    -
    -  qh_MAXnarrow
    -    max. cosine in initial hull that sets qh.NARROWhull
    -
    -  notes:
    -    If qh.NARROWhull, the initial partition does not make
    -    coplanar points.  If narrow, a coplanar point can be
    -    coplanar to two facets of opposite orientations and
    -    distant from the exact convex hull.
    -
    -    Conservative estimate.  Don't actually see problems until it is -1.0
    -*/
    -#define qh_MAXnarrow -0.99999999
    -
    -/*----------------------------------
    -
    -  qh_WARNnarrow
    -    max. cosine in initial hull to warn about qh.NARROWhull
    -
    -  notes:
    -    this is a conservative estimate.
    -    Don't actually see problems until it is -1.0.  See qh-impre.htm
    -*/
    -#define qh_WARNnarrow -0.999999999999999
    -
    -/*----------------------------------
    -
    -  qh_ZEROdelaunay
    -    a zero Delaunay facet occurs for input sites coplanar with their convex hull
    -    the last normal coefficient of a zero Delaunay facet is within
    -        qh_ZEROdelaunay * qh.ANGLEround of 0
    -
    -  notes:
    -    qh_ZEROdelaunay does not allow for joggled input ('QJ').
    -
    -    You can avoid zero Delaunay facets by surrounding the input with a box.
    -
    -    Use option 'PDk:-n' to explicitly define zero Delaunay facets
    -      k= dimension of input sites (e.g., 3 for 3-d Delaunay triangulation)
    -      n= the cutoff for zero Delaunay facets (e.g., 'PD3:-1e-12')
    -*/
    -#define qh_ZEROdelaunay 2
    -
    -#endif /* qh_DEFuser */
    diff --git a/extern/qhull/usermem.c b/extern/qhull/usermem.c
    deleted file mode 100644
    index 722c0b42f871..000000000000
    --- a/extern/qhull/usermem.c
    +++ /dev/null
    @@ -1,62 +0,0 @@
    -/*
      ---------------------------------
    -
    -   usermem.c
    -   qh_exit(), qh_free(), and qh_malloc()
    -
    -   See README.txt.
    -
    -   If you redefine one of these functions you must redefine all of them.
    -   If you recompile and load this file, then usermem.o will not be loaded
    -   from qhull.a or qhull.lib
    -
    -   See libqhull.h for data structures, macros, and user-callable functions.
    -   See user.c for qhull-related, redefinable functions
    -   see user.h for user-definable constants
    -   See userprintf.c for qh_fprintf and userprintf_rbox,c for qh_fprintf_rbox
    -
    -   Please report any errors that you fix to qhull@qhull.org
    -*/
    -
    -#include "libqhull.h"
    -
    -#include 
    -
    -/*---------------------------------
    -
    -  qh_exit( exitcode )
    -    exit program
    -
    -  notes:
    -    same as exit()
    -*/
    -void qh_exit(int exitcode) {
    -    exit(exitcode);
    -} /* exit */
    -
    -/*---------------------------------
    -
    -qh_free( mem )
    -free memory
    -
    -notes:
    -same as free()
    -*/
    -void qh_free(void *mem) {
    -    free(mem);
    -} /* free */
    -
    -/*---------------------------------
    -
    -    qh_malloc( mem )
    -      allocate memory
    -
    -    notes:
    -      same as malloc()
    -*/
    -void *qh_malloc(size_t size) {
    -    return malloc(size);
    -} /* malloc */
    diff --git a/extern/qhull/userprintf.c b/extern/qhull/userprintf.c
    deleted file mode 100644
    index d6c245b217b4..000000000000
    --- a/extern/qhull/userprintf.c
    +++ /dev/null
    @@ -1,62 +0,0 @@
    -/*
      ---------------------------------
    -
    -   userprintf.c
    -   qh_fprintf()
    -
    -   see README.txt  see COPYING.txt for copyright information.
    -
    -   If you recompile and load this file, then userprintf.o will not be loaded
    -   from qhull.a or qhull.lib
    -
    -   See libqhull.h for data structures, macros, and user-callable functions.
    -   See user.c for qhull-related, redefinable functions
    -   see user.h for user-definable constants
    -   See usermem.c for qh_exit(), qh_free(), and qh_malloc()
    -   see Qhull.cpp and RboxPoints.cpp for examples.
    -
    -   Please report any errors that you fix to qhull@qhull.org
    -*/
    -
    -#include "libqhull.h"
    -
    -#include 
    -#include 
    -#include 
    -
    -/*---------------------------------
    -
    -   qh_fprintf(fp, msgcode, format, list of args )
    -     print arguments to *fp according to format
    -     Use qh_fprintf_rbox() for rboxlib.c
    -
    -   notes:
    -     same as fprintf()
    -     fgets() is not trapped like fprintf()
    -     exit qh_fprintf via qh_errexit()
    -*/
    -
    -void qh_fprintf(FILE *fp, int msgcode, const char *fmt, ... ) {
    -    va_list args;
    -
    -    if (!fp) {
    -        fprintf(stderr, "QH6232 Qhull internal error (userprintf.c): fp is 0.  Wrong qh_fprintf called.\n");
    -        qh_errexit(6232, NULL, NULL);
    -    }
    -    va_start(args, fmt);
    -#if qh_QHpointer
    -    if (qh_qh && qh ANNOTATEoutput) {
    -#else
    -    if (qh ANNOTATEoutput) {
    -#endif
    -      fprintf(fp, "[QH%.4d]", msgcode);
    -    }else if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR ) {
    -      fprintf(fp, "QH%.4d ", msgcode);
    -    }
    -    vfprintf(fp, fmt, args);
    -    va_end(args);
    -
    -    /* Place debugging traps here. Use with option 'Tn' */
    -
    -} /* qh_fprintf */
    diff --git a/extern/qhull/userprintf_rbox.c b/extern/qhull/userprintf_rbox.c
    deleted file mode 100644
    index 0e1a12ac3f34..000000000000
    --- a/extern/qhull/userprintf_rbox.c
    +++ /dev/null
    @@ -1,52 +0,0 @@
    -/*
      ---------------------------------
    -
    -   userprintf_rbox.c
    -   qh_fprintf_rbox()
    -
    -   see README.txt  see COPYING.txt for copyright information.
    -
    -   If you recompile and load this file, then userprintf_rbox.o will not be loaded
    -   from qhull.a or qhull.lib
    -
    -   See libqhull.h for data structures, macros, and user-callable functions.
    -   See user.c for qhull-related, redefinable functions
    -   see user.h for user-definable constants
    -   See usermem.c for qh_exit(), qh_free(), and qh_malloc()
    -   see Qhull.cpp and RboxPoints.cpp for examples.
    -
    -   Please report any errors that you fix to qhull@qhull.org
    -*/
    -
    -#include "libqhull.h"
    -
    -#include 
    -#include 
    -#include 
    -
    -/*---------------------------------
    -
    -   qh_fprintf_rbox(fp, msgcode, format, list of args )
    -     print arguments to *fp according to format
    -     Use qh_fprintf_rbox() for rboxlib.c
    -
    -   notes:
    -     same as fprintf()
    -     fgets() is not trapped like fprintf()
    -     exit qh_fprintf_rbox via qh_errexit_rbox()
    -*/
    -
    -void qh_fprintf_rbox(FILE *fp, int msgcode, const char *fmt, ... ) {
    -    va_list args;
    -
    -    if (!fp) {
    -        fprintf(stderr, "QH6231 Qhull internal error (userprintf.c): fp is 0.  Wrong qh_fprintf_rbox called.\n");
    -        qh_errexit_rbox(6231);
    -    }
    -    if (msgcode >= MSG_ERROR && msgcode < MSG_STDERR)
    -      fprintf(fp, "QH%.4d ", msgcode);
    -    va_start(args, fmt);
    -    vfprintf(fp, fmt, args);
    -    va_end(args);
    -} /* qh_fprintf_rbox */
    diff --git a/extern/ttconv/global_defines.h b/extern/ttconv/global_defines.h
    deleted file mode 100644
    index 04dd41e02ac1..000000000000
    --- a/extern/ttconv/global_defines.h
    +++ /dev/null
    @@ -1,41 +0,0 @@
    -/* -*- mode: c; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -/*
    -** ~ppr/src/include/global_defines.h
    -** Copyright 1995, Trinity College Computing Center.
    -** Written by David Chappell.
    -**
    -** Permission to use, copy, modify, and distribute this software and its
    -** documentation for any purpose and without fee is hereby granted, provided
    -** that the above copyright notice appear in all copies and that both that
    -** copyright notice and this permission notice appear in supporting
    -** documentation.  This software and documentation are provided "as is" without
    -** express or implied warranty.
    -**
    -** The PPR project was begun 28 December 1992.
    -**
    -** There are many things in this file you may want to change.  This file
    -** should be the first include file.  It is the header file for the whole
    -** project.
    -**
    -** This file was last modified 22 December 1995.
    -*/
    -
    -/*
    -** TRUE and FALSE
    -**  The code makes liberal use of these macros.
    -*/
    -#if !defined(FALSE)
    -#define FALSE 0
    -#endif
    -#if !defined(TRUE)
    -#define TRUE !FALSE
    -#endif
    -
    -/* end of file */
    diff --git a/extern/ttconv/pprdrv.h b/extern/ttconv/pprdrv.h
    deleted file mode 100644
    index 39e81fee7f0c..000000000000
    --- a/extern/ttconv/pprdrv.h
    +++ /dev/null
    @@ -1,113 +0,0 @@
    -/* -*- mode: c++; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -/*
    -** ~ppr/src/include/pprdrv.h
    -** Copyright 1995, Trinity College Computing Center.
    -** Written by David Chappell.
    -**
    -** Permission to use, copy, modify, and distribute this software and its
    -** documentation for any purpose and without fee is hereby granted, provided
    -** that the above copyright notice appear in all copies and that both that
    -** copyright notice and this permission notice appear in supporting
    -** documentation.  This software is provided "as is" without express or
    -** implied warranty.
    -**
    -** This file last revised 5 December 1995.
    -*/
    -
    -#include 
    -#include 
    -
    -/*
    - * Encapsulates all of the output to write to an arbitrary output
    - * function.  This both removes the hardcoding of output to go to stdout
    - * and makes output thread-safe.  Michael Droettboom [06-07-07]
    - */
    -class TTStreamWriter
    -{
    - private:
    -    // Private copy and assignment
    -    TTStreamWriter& operator=(const TTStreamWriter& other);
    -    TTStreamWriter(const TTStreamWriter& other);
    -
    - public:
    -    TTStreamWriter() { }
    -    virtual ~TTStreamWriter() { }
    -
    -    virtual void write(const char*) = 0;
    -
    -    virtual void printf(const char* format, ...);
    -    virtual void put_char(int val);
    -    virtual void puts(const char* a);
    -    virtual void putline(const char* a);
    -};
    -
    -class TTDictionaryCallback
    -{
    -private:
    -    // Private copy and assignment
    -    TTDictionaryCallback& operator=(const TTStreamWriter& other);
    -    TTDictionaryCallback(const TTStreamWriter& other);
    -
    -public:
    -    TTDictionaryCallback() { }
    -    virtual ~TTDictionaryCallback() { }
    -
    -    virtual void add_pair(const char* key, const char* value) = 0;
    -};
    -
    -void replace_newlines_with_spaces(char* a);
    -
    -/*
    - * A simple class for all ttconv exceptions.
    - */
    -class TTException
    -{
    -    const char* message;
    -    TTException& operator=(const TTStreamWriter& other);
    -    TTException(const TTStreamWriter& other);
    -
    -public:
    -    TTException(const char* message_) : message(message_) { }
    -    const char* getMessage()
    -    {
    -        return message;
    -    }
    -};
    -
    -/*
    -** No debug code will be included if this
    -** is not defined:
    -*/
    -/* #define DEBUG 1 */
    -
    -/*
    -** Uncomment the defines for the debugging
    -** code you want to have included.
    -*/
    -#ifdef DEBUG
    -#define DEBUG_TRUETYPE          /* truetype fonts, conversion to Postscript */
    -#endif
    -
    -/* Do not change anything below this line. */
    -
    -enum font_type_enum
    -{
    -    PS_TYPE_3  = 3,
    -    PS_TYPE_42 = 42,
    -    PS_TYPE_42_3_HYBRID = 43,
    -    PDF_TYPE_3 = -3
    -};
    -
    -/* routines in pprdrv_tt.c */
    -void insert_ttfont(const char *filename, TTStreamWriter& stream, font_type_enum target_type, std::vector& glyph_ids);
    -
    -void get_pdf_charprocs(const char *filename, std::vector& glyph_ids, TTDictionaryCallback& dict);
    -
    -/* end of file */
    diff --git a/extern/ttconv/pprdrv_tt.cpp b/extern/ttconv/pprdrv_tt.cpp
    deleted file mode 100644
    index 7677e8210505..000000000000
    --- a/extern/ttconv/pprdrv_tt.cpp
    +++ /dev/null
    @@ -1,1468 +0,0 @@
    -/* -*- mode: c++; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -/*
    -** ~ppr/src/pprdrv/pprdrv_tt.c
    -** Copyright 1995, Trinity College Computing Center.
    -** Written by David Chappell.
    -**
    -** Permission to use, copy, modify, and distribute this software and its
    -** documentation for any purpose and without fee is hereby granted, provided
    -** that the above copyright notice appear in all copies and that both that
    -** copyright notice and this permission notice appear in supporting
    -** documentation.  This software is provided "as is" without express or
    -** implied warranty.
    -**
    -** TrueType font support.  These functions allow PPR to generate
    -** PostScript fonts from Microsoft compatible TrueType font files.
    -**
    -** Last revised 19 December 1995.
    -*/
    -
    -#include "global_defines.h"
    -#include 
    -#include 
    -#include 
    -#include "pprdrv.h"
    -#include "truetype.h"
    -#include 
    -#ifdef _POSIX_C_SOURCE
    -#    undef _POSIX_C_SOURCE
    -#endif
    -#ifdef _XOPEN_SOURCE
    -#    undef _XOPEN_SOURCE
    -#endif
    -#include 
    -
    -/*==========================================================================
    -** Convert the indicated Truetype font file to a type 42 or type 3
    -** PostScript font and insert it in the output stream.
    -**
    -** All the routines from here to the end of file file are involved
    -** in this process.
    -==========================================================================*/
    -
    -/*---------------------------------------
    -** Endian conversion routines.
    -** These routines take a BYTE pointer
    -** and return a value formed by reading
    -** bytes starting at that point.
    -**
    -** These routines read the big-endian
    -** values which are used in TrueType
    -** font files.
    ----------------------------------------*/
    -
    -/*
    -** Get an Unsigned 32 bit number.
    -*/
    -ULONG getULONG(BYTE *p)
    -{
    -    int x;
    -    ULONG val=0;
    -
    -    for (x=0; x<4; x++)
    -    {
    -        val *= 0x100;
    -        val += p[x];
    -    }
    -
    -    return val;
    -} /* end of ftohULONG() */
    -
    -/*
    -** Get an unsigned 16 bit number.
    -*/
    -USHORT getUSHORT(BYTE *p)
    -{
    -    int x;
    -    USHORT val=0;
    -
    -    for (x=0; x<2; x++)
    -    {
    -        val *= 0x100;
    -        val += p[x];
    -    }
    -
    -    return val;
    -} /* end of getUSHORT() */
    -
    -/*
    -** Get a 32 bit fixed point (16.16) number.
    -** A special structure is used to return the value.
    -*/
    -Fixed getFixed(BYTE *s)
    -{
    -    Fixed val={0,0};
    -
    -    val.whole = ((s[0] * 256) + s[1]);
    -    val.fraction = ((s[2] * 256) + s[3]);
    -
    -    return val;
    -} /* end of getFixed() */
    -
    -/*-----------------------------------------------------------------------
    -** Load a TrueType font table into memory and return a pointer to it.
    -** The font's "file" and "offset_table" fields must be set before this
    -** routine is called.
    -**
    -** This first argument is a TrueType font structure, the second
    -** argument is the name of the table to retrieve.  A table name
    -** is always 4 characters, though the last characters may be
    -** padding spaces.
    ------------------------------------------------------------------------*/
    -BYTE *GetTable(struct TTFONT *font, const char *name)
    -{
    -    BYTE *ptr;
    -    ULONG x;
    -
    -#ifdef DEBUG_TRUETYPE
    -    debug("GetTable(file,font,\"%s\")",name);
    -#endif
    -
    -    /* We must search the table directory. */
    -    ptr = font->offset_table + 12;
    -    x=0;
    -    while (TRUE)
    -    {
    -        if ( strncmp((const char*)ptr,name,4) == 0 )
    -        {
    -            ULONG offset,length;
    -            BYTE *table;
    -
    -            offset = getULONG( ptr + 8 );
    -            length = getULONG( ptr + 12 );
    -            table = (BYTE*)calloc( sizeof(BYTE), length );
    -
    -            try
    -            {
    -#ifdef DEBUG_TRUETYPE
    -                debug("Loading table \"%s\" from offset %d, %d bytes",name,offset,length);
    -#endif
    -
    -                if ( fseek( font->file, (long)offset, SEEK_SET ) )
    -                {
    -                    throw TTException("TrueType font may be corrupt (reason 3)");
    -                }
    -
    -                if ( fread(table,sizeof(BYTE),length,font->file) != (sizeof(BYTE) * length))
    -                {
    -                    throw TTException("TrueType font may be corrupt (reason 4)");
    -                }
    -            }
    -            catch (TTException& )
    -            {
    -                free(table);
    -                throw;
    -            }
    -            return table;
    -        }
    -
    -        x++;
    -        ptr += 16;
    -        if (x == font->numTables)
    -        {
    -            throw TTException("TrueType font is missing table");
    -        }
    -    }
    -
    -} /* end of GetTable() */
    -
    -static void utf16be_to_ascii(char *dst, char *src, size_t length) {
    -    ++src;
    -    for (; *src != 0 && length; dst++, src += 2, --length) {
    -        *dst = *src;
    -    }
    -}
    -
    -/*--------------------------------------------------------------------
    -** Load the 'name' table, get information from it,
    -** and store that information in the font structure.
    -**
    -** The 'name' table contains information such as the name of
    -** the font, and it's PostScript name.
    ---------------------------------------------------------------------*/
    -void Read_name(struct TTFONT *font)
    -{
    -    BYTE *table_ptr,*ptr2;
    -    int numrecords;                     /* Number of strings in this table */
    -    BYTE *strings;                      /* pointer to start of string storage */
    -    int x;
    -    int platform;                       /* Current platform id */
    -    int nameid;                         /* name id, */
    -    int offset,length;                  /* offset and length of string. */
    -
    -#ifdef DEBUG_TRUETYPE
    -    debug("Read_name()");
    -#endif
    -
    -    table_ptr = NULL;
    -
    -    /* Set default values to avoid future references to undefined
    -     * pointers. Allocate each of PostName, FullName, FamilyName,
    -     * Version, and Style separately so they can be freed safely. */
    -    for (char **ptr = &(font->PostName); ptr != NULL; )
    -    {
    -        *ptr = (char*) calloc(sizeof(char), strlen("unknown")+1);
    -        strcpy(*ptr, "unknown");
    -        if (ptr == &(font->PostName)) ptr = &(font->FullName);
    -        else if (ptr == &(font->FullName)) ptr = &(font->FamilyName);
    -        else if (ptr == &(font->FamilyName)) ptr = &(font->Version);
    -        else if (ptr == &(font->Version)) ptr = &(font->Style);
    -        else ptr = NULL;
    -    }
    -    font->Copyright = font->Trademark = (char*)NULL;
    -
    -    table_ptr = GetTable(font, "name");         /* pointer to table */
    -    try
    -    {
    -        numrecords = getUSHORT( table_ptr + 2 );  /* number of names */
    -        strings = table_ptr + getUSHORT( table_ptr + 4 ); /* start of string storage */
    -
    -        ptr2 = table_ptr + 6;
    -        for (x=0; x < numrecords; x++,ptr2+=12)
    -        {
    -            platform = getUSHORT(ptr2);
    -            nameid = getUSHORT(ptr2+6);
    -            length = getUSHORT(ptr2+8);
    -            offset = getUSHORT(ptr2+10);
    -
    -#ifdef DEBUG_TRUETYPE
    -            debug("platform %d, encoding %d, language 0x%x, name %d, offset %d, length %d",
    -                  platform,encoding,language,nameid,offset,length);
    -#endif
    -
    -            /* Copyright notice */
    -            if ( platform == 1 && nameid == 0 )
    -            {
    -                font->Copyright = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->Copyright,(const char*)strings+offset,length);
    -                font->Copyright[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->Copyright);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->Copyright=\"%s\"",font->Copyright);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* Font Family name */
    -            if ( platform == 1 && nameid == 1 )
    -            {
    -                free(font->FamilyName);
    -                font->FamilyName = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->FamilyName,(const char*)strings+offset,length);
    -                font->FamilyName[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->FamilyName);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->FamilyName=\"%s\"",font->FamilyName);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* Font Family name */
    -            if ( platform == 1 && nameid == 2 )
    -            {
    -                free(font->Style);
    -                font->Style = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->Style,(const char*)strings+offset,length);
    -                font->Style[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->Style);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->Style=\"%s\"",font->Style);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* Full Font name */
    -            if ( platform == 1 && nameid == 4 )
    -            {
    -                free(font->FullName);
    -                font->FullName = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->FullName,(const char*)strings+offset,length);
    -                font->FullName[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->FullName);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->FullName=\"%s\"",font->FullName);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* Version string */
    -            if ( platform == 1 && nameid == 5 )
    -            {
    -                free(font->Version);
    -                font->Version = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->Version,(const char*)strings+offset,length);
    -                font->Version[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->Version);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->Version=\"%s\"",font->Version);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* PostScript name */
    -            if ( platform == 1 && nameid == 6 )
    -            {
    -                free(font->PostName);
    -                font->PostName = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->PostName,(const char*)strings+offset,length);
    -                font->PostName[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->PostName);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->PostName=\"%s\"",font->PostName);
    -#endif
    -                continue;
    -            }
    -
    -            /* Microsoft-format PostScript name */
    -            if ( platform == 3 && nameid == 6 )
    -            {
    -                free(font->PostName);
    -                font->PostName = (char*)calloc(sizeof(char),length+1);
    -                utf16be_to_ascii(font->PostName, (char *)strings+offset, length);
    -                font->PostName[length/2]=(char)NULL;
    -                replace_newlines_with_spaces(font->PostName);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->PostName=\"%s\"",font->PostName);
    -#endif
    -                continue;
    -            }
    -
    -
    -            /* Trademark string */
    -            if ( platform == 1 && nameid == 7 )
    -            {
    -                font->Trademark = (char*)calloc(sizeof(char),length+1);
    -                strncpy(font->Trademark,(const char*)strings+offset,length);
    -                font->Trademark[length]=(char)NULL;
    -                replace_newlines_with_spaces(font->Trademark);
    -
    -#ifdef DEBUG_TRUETYPE
    -                debug("font->Trademark=\"%s\"",font->Trademark);
    -#endif
    -                continue;
    -            }
    -        }
    -    }
    -    catch (TTException& )
    -    {
    -        free(table_ptr);
    -        throw;
    -    }
    -
    -    free(table_ptr);
    -} /* end of Read_name() */
    -
    -/*---------------------------------------------------------------------
    -** Write the header for a PostScript font.
    ----------------------------------------------------------------------*/
    -void ttfont_header(TTStreamWriter& stream, struct TTFONT *font)
    -{
    -    int VMMin;
    -    int VMMax;
    -
    -    /*
    -    ** To show that it is a TrueType font in PostScript format,
    -    ** we will begin the file with a specific string.
    -    ** This string also indicates the version of the TrueType
    -    ** specification on which the font is based and the
    -    ** font manufacturer's revision number for the font.
    -    */
    -    if ( font->target_type == PS_TYPE_42 ||
    -            font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.printf("%%!PS-TrueTypeFont-%d.%d-%d.%d\n",
    -                      font->TTVersion.whole, font->TTVersion.fraction,
    -                      font->MfrRevision.whole, font->MfrRevision.fraction);
    -    }
    -
    -    /* If it is not a Type 42 font, we will use a different format. */
    -    else
    -    {
    -        stream.putline("%!PS-Adobe-3.0 Resource-Font");
    -    }       /* See RBIIp 641 */
    -
    -    /* We will make the title the name of the font. */
    -    stream.printf("%%%%Title: %s\n",font->FullName);
    -
    -    /* If there is a Copyright notice, put it here too. */
    -    if ( font->Copyright != (char*)NULL )
    -    {
    -        stream.printf("%%%%Copyright: %s\n",font->Copyright);
    -    }
    -
    -    /* We created this file. */
    -    if ( font->target_type == PS_TYPE_42 )
    -    {
    -        stream.putline("%%Creator: Converted from TrueType to type 42 by PPR");
    -    }
    -    else if (font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.putline("%%Creator: Converted from TypeType to type 42/type 3 hybrid by PPR");
    -    }
    -    else
    -    {
    -        stream.putline("%%Creator: Converted from TrueType to type 3 by PPR");
    -    }
    -
    -    /* If VM usage information is available, print it. */
    -    if ( font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        VMMin = (int)getULONG( font->post_table + 16 );
    -        VMMax = (int)getULONG( font->post_table + 20 );
    -        if ( VMMin > 0 && VMMax > 0 )
    -            stream.printf("%%%%VMUsage: %d %d\n",VMMin,VMMax);
    -    }
    -
    -    /* Start the dictionary which will eventually */
    -    /* become the font. */
    -    if (font->target_type == PS_TYPE_42)
    -    {
    -        stream.putline("15 dict begin");
    -    }
    -    else
    -    {
    -        stream.putline("25 dict begin");
    -
    -        /* Type 3 fonts will need some subroutines here. */
    -        stream.putline("/_d{bind def}bind def");
    -        stream.putline("/_m{moveto}_d");
    -        stream.putline("/_l{lineto}_d");
    -        stream.putline("/_cl{closepath eofill}_d");
    -        stream.putline("/_c{curveto}_d");
    -        stream.putline("/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d");
    -        stream.putline("/_e{exec}_d");
    -    }
    -
    -    stream.printf("/FontName /%s def\n",font->PostName);
    -    stream.putline("/PaintType 0 def");
    -
    -    if (font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.putline("/FontMatrix[1 0 0 1 0 0]def");
    -    }
    -    else
    -    {
    -        stream.putline("/FontMatrix[.001 0 0 .001 0 0]def");
    -    }
    -
    -    stream.printf("/FontBBox[%d %d %d %d]def\n",font->llx-1,font->lly-1,font->urx,font->ury);
    -    if (font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.printf("/FontType 42 def\n", font->target_type );
    -    }
    -    else
    -    {
    -        stream.printf("/FontType 3 def\n", font->target_type );
    -    }
    -} /* end of ttfont_header() */
    -
    -/*-------------------------------------------------------------
    -** Define the encoding array for this font.
    -** Since we don't really want to deal with converting all of
    -** the possible font encodings in the wild to a standard PS
    -** one, we just explicitly create one for each font.
    --------------------------------------------------------------*/
    -void ttfont_encoding(TTStreamWriter& stream, struct TTFONT *font, std::vector& glyph_ids, font_type_enum target_type)
    -{
    -    if (target_type == PS_TYPE_3 || target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.printf("/Encoding [ ");
    -
    -        for (std::vector::const_iterator i = glyph_ids.begin();
    -                i != glyph_ids.end(); ++i)
    -        {
    -            const char* name = ttfont_CharStrings_getname(font, *i);
    -            stream.printf("/%s ", name);
    -        }
    -
    -        stream.printf("] def\n");
    -    }
    -    else
    -    {
    -        stream.putline("/Encoding StandardEncoding def");
    -    }
    -} /* end of ttfont_encoding() */
    -
    -/*-----------------------------------------------------------
    -** Create the optional "FontInfo" sub-dictionary.
    ------------------------------------------------------------*/
    -void ttfont_FontInfo(TTStreamWriter& stream, struct TTFONT *font)
    -{
    -    Fixed ItalicAngle;
    -
    -    /* We create a sub dictionary named "FontInfo" where we */
    -    /* store information which though it is not used by the */
    -    /* interpreter, is useful to some programs which will */
    -    /* be printing with the font. */
    -    stream.putline("/FontInfo 10 dict dup begin");
    -
    -    /* These names come from the TrueType font's "name" table. */
    -    stream.printf("/FamilyName (%s) def\n",font->FamilyName);
    -    stream.printf("/FullName (%s) def\n",font->FullName);
    -
    -    if ( font->Copyright != (char*)NULL || font->Trademark != (char*)NULL )
    -    {
    -        stream.printf("/Notice (%s",
    -                      font->Copyright != (char*)NULL ? font->Copyright : "");
    -        stream.printf("%s%s) def\n",
    -                      font->Trademark != (char*)NULL ? " " : "",
    -                      font->Trademark != (char*)NULL ? font->Trademark : "");
    -    }
    -
    -    /* This information is not quite correct. */
    -    stream.printf("/Weight (%s) def\n",font->Style);
    -
    -    /* Some fonts have this as "version". */
    -    stream.printf("/Version (%s) def\n",font->Version);
    -
    -    /* Some information from the "post" table. */
    -    ItalicAngle = getFixed( font->post_table + 4 );
    -    stream.printf("/ItalicAngle %d.%d def\n",ItalicAngle.whole,ItalicAngle.fraction);
    -    stream.printf("/isFixedPitch %s def\n", getULONG( font->post_table + 12 ) ? "true" : "false" );
    -    stream.printf("/UnderlinePosition %d def\n", (int)getFWord( font->post_table + 8 ) );
    -    stream.printf("/UnderlineThickness %d def\n", (int)getFWord( font->post_table + 10 ) );
    -    stream.putline("end readonly def");
    -} /* end of ttfont_FontInfo() */
    -
    -/*-------------------------------------------------------------------
    -** sfnts routines
    -** These routines generate the PostScript "sfnts" array which
    -** contains one or more strings which contain a reduced version
    -** of the TrueType font.
    -**
    -** A number of functions are required to accomplish this rather
    -** complicated task.
    --------------------------------------------------------------------*/
    -int string_len;
    -int line_len;
    -int in_string;
    -
    -/*
    -** This is called once at the start.
    -*/
    -void sfnts_start(TTStreamWriter& stream)
    -{
    -    stream.puts("/sfnts[<");
    -    in_string=TRUE;
    -    string_len=0;
    -    line_len=8;
    -} /* end of sfnts_start() */
    -
    -/*
    -** Write a BYTE as a hexadecimal value as part of the sfnts array.
    -*/
    -void sfnts_pputBYTE(TTStreamWriter& stream, BYTE n)
    -{
    -    static const char hexdigits[]="0123456789ABCDEF";
    -
    -    if (!in_string)
    -    {
    -        stream.put_char('<');
    -        string_len=0;
    -        line_len++;
    -        in_string=TRUE;
    -    }
    -
    -    stream.put_char( hexdigits[ n / 16 ] );
    -    stream.put_char( hexdigits[ n % 16 ] );
    -    string_len++;
    -    line_len+=2;
    -
    -    if (line_len > 70)
    -    {
    -        stream.put_char('\n');
    -        line_len=0;
    -    }
    -
    -} /* end of sfnts_pputBYTE() */
    -
    -/*
    -** Write a USHORT as a hexadecimal value as part of the sfnts array.
    -*/
    -void sfnts_pputUSHORT(TTStreamWriter& stream, USHORT n)
    -{
    -    sfnts_pputBYTE(stream, n / 256);
    -    sfnts_pputBYTE(stream, n % 256);
    -} /* end of sfnts_pputUSHORT() */
    -
    -/*
    -** Write a ULONG as part of the sfnts array.
    -*/
    -void sfnts_pputULONG(TTStreamWriter& stream, ULONG n)
    -{
    -    int x1,x2,x3;
    -
    -    x1 = n % 256;
    -    n /= 256;
    -    x2 = n % 256;
    -    n /= 256;
    -    x3 = n % 256;
    -    n /= 256;
    -
    -    sfnts_pputBYTE(stream, n);
    -    sfnts_pputBYTE(stream, x3);
    -    sfnts_pputBYTE(stream, x2);
    -    sfnts_pputBYTE(stream, x1);
    -} /* end of sfnts_pputULONG() */
    -
    -/*
    -** This is called whenever it is
    -** necessary to end a string in the sfnts array.
    -**
    -** (The array must be broken into strings which are
    -** no longer than 64K characters.)
    -*/
    -void sfnts_end_string(TTStreamWriter& stream)
    -{
    -    if (in_string)
    -    {
    -        string_len=0;           /* fool sfnts_pputBYTE() */
    -
    -#ifdef DEBUG_TRUETYPE_INLINE
    -        puts("\n% dummy byte:\n");
    -#endif
    -
    -        sfnts_pputBYTE(stream, 0);      /* extra byte for pre-2013 compatibility */
    -        stream.put_char('>');
    -        line_len++;
    -    }
    -    in_string=FALSE;
    -} /* end of sfnts_end_string() */
    -
    -/*
    -** This is called at the start of each new table.
    -** The argement is the length in bytes of the table
    -** which will follow.  If the new table will not fit
    -** in the current string, a new one is started.
    -*/
    -void sfnts_new_table(TTStreamWriter& stream, ULONG length)
    -{
    -    if ( (string_len + length) > 65528 )
    -        sfnts_end_string(stream);
    -} /* end of sfnts_new_table() */
    -
    -/*
    -** We may have to break up the 'glyf' table.  That is the reason
    -** why we provide this special routine to copy it into the sfnts
    -** array.
    -*/
    -void sfnts_glyf_table(TTStreamWriter& stream, struct TTFONT *font, ULONG oldoffset, ULONG correct_total_length)
    -{
    -    ULONG off;
    -    ULONG length;
    -    int c;
    -    ULONG total=0;              /* running total of bytes written to table */
    -    int x;
    -    bool loca_is_local=false;
    -
    -#ifdef DEBUG_TRUETYPE
    -    debug("sfnts_glyf_table(font,%d)", (int)correct_total_length);
    -#endif
    -
    -    if (font->loca_table == NULL)
    -    {
    -        font->loca_table = GetTable(font,"loca");
    -        loca_is_local = true;
    -    }
    -
    -    /* Seek to proper position in the file. */
    -    fseek( font->file, oldoffset, SEEK_SET );
    -
    -    /* Copy the glyphs one by one */
    -    for (x=0; x < font->numGlyphs; x++)
    -    {
    -        /* Read the glyph offset from the index-to-location table. */
    -        if (font->indexToLocFormat == 0)
    -        {
    -            off = getUSHORT( font->loca_table + (x * 2) );
    -            off *= 2;
    -            length = getUSHORT( font->loca_table + ((x+1) * 2) );
    -            length *= 2;
    -            length -= off;
    -        }
    -        else
    -        {
    -            off = getULONG( font->loca_table + (x * 4) );
    -            length = getULONG( font->loca_table + ((x+1) * 4) );
    -            length -= off;
    -        }
    -
    -#ifdef DEBUG_TRUETYPE
    -        debug("glyph length=%d",(int)length);
    -#endif
    -
    -        /* Start new string if necessary. */
    -        sfnts_new_table( stream, (int)length );
    -
    -        /*
    -        ** Make sure the glyph is padded out to a
    -        ** two byte boundary.
    -        */
    -        if ( length % 2 ) {
    -            throw TTException("TrueType font contains a 'glyf' table without 2 byte padding");
    -        }
    -
    -        /* Copy the bytes of the glyph. */
    -        while ( length-- )
    -        {
    -            if ( (c = fgetc(font->file)) == EOF ) {
    -                throw TTException("TrueType font may be corrupt (reason 6)");
    -            }
    -
    -            sfnts_pputBYTE(stream, c);
    -            total++;            /* add to running total */
    -        }
    -
    -    }
    -
    -    if (loca_is_local)
    -    {
    -        free(font->loca_table);
    -        font->loca_table = NULL;
    -    }
    -
    -    /* Pad out to full length from table directory */
    -    while ( total < correct_total_length )
    -    {
    -        sfnts_pputBYTE(stream, 0);
    -        total++;
    -    }
    -
    -} /* end of sfnts_glyf_table() */
    -
    -/*
    -** Here is the routine which ties it all together.
    -**
    -** Create the array called "sfnts" which
    -** holds the actual TrueType data.
    -*/
    -void ttfont_sfnts(TTStreamWriter& stream, struct TTFONT *font)
    -{
    -    static const char *table_names[] =  /* The names of all tables */
    -    {
    -        /* which it is worth while */
    -        "cvt ",                         /* to include in a Type 42 */
    -        "fpgm",                         /* PostScript font. */
    -        "glyf",
    -        "head",
    -        "hhea",
    -        "hmtx",
    -        "loca",
    -        "maxp",
    -        "prep"
    -    } ;
    -
    -    struct                      /* The location of each of */
    -    {
    -        ULONG oldoffset;        /* the above tables. */
    -        ULONG newoffset;
    -        ULONG length;
    -        ULONG checksum;
    -    } tables[9];
    -
    -    BYTE *ptr;                  /* A pointer into the origional table directory. */
    -    ULONG x,y;                  /* General use loop countes. */
    -    int c;                      /* Input character. */
    -    int diff;
    -    ULONG nextoffset;
    -    int count;                  /* How many `important' tables did we find? */
    -
    -    ptr = font->offset_table + 12;
    -    nextoffset=0;
    -    count=0;
    -
    -    /*
    -    ** Find the tables we want and store there vital
    -    ** statistics in tables[].
    -    */
    -    for (x=0; x < 9; x++ )
    -    {
    -        do
    -        {
    -            diff = strncmp( (char*)ptr, table_names[x], 4 );
    -
    -            if ( diff > 0 )             /* If we are past it. */
    -            {
    -                tables[x].length = 0;
    -                diff = 0;
    -            }
    -            else if ( diff < 0 )        /* If we haven't hit it yet. */
    -            {
    -                ptr += 16;
    -            }
    -            else if ( diff == 0 )       /* Here it is! */
    -            {
    -                tables[x].newoffset = nextoffset;
    -                tables[x].checksum = getULONG( ptr + 4 );
    -                tables[x].oldoffset = getULONG( ptr + 8 );
    -                tables[x].length = getULONG( ptr + 12 );
    -                nextoffset += ( ((tables[x].length + 3) / 4) * 4 );
    -                count++;
    -                ptr += 16;
    -            }
    -        }
    -        while (diff != 0);
    -
    -    } /* end of for loop which passes over the table directory */
    -
    -    /* Begin the sfnts array. */
    -    sfnts_start(stream);
    -
    -    /* Generate the offset table header */
    -    /* Start by copying the TrueType version number. */
    -    ptr = font->offset_table;
    -    for (x=0; x < 4; x++)
    -    {
    -        sfnts_pputBYTE( stream,  *(ptr++) );
    -    }
    -
    -    /* Now, generate those silly numTables numbers. */
    -    sfnts_pputUSHORT(stream, count);            /* number of tables */
    -    if ( count == 9 )
    -    {
    -        sfnts_pputUSHORT(stream, 7);          /* searchRange */
    -        sfnts_pputUSHORT(stream, 3);          /* entrySelector */
    -        sfnts_pputUSHORT(stream, 81);         /* rangeShift */
    -    }
    -#ifdef DEBUG_TRUETYPE
    -    else
    -    {
    -        debug("only %d tables selected",count);
    -    }
    -#endif
    -
    -    /* Now, emmit the table directory. */
    -    for (x=0; x < 9; x++)
    -    {
    -        if ( tables[x].length == 0 )    /* Skip missing tables */
    -        {
    -            continue;
    -        }
    -
    -        /* Name */
    -        sfnts_pputBYTE( stream, table_names[x][0] );
    -        sfnts_pputBYTE( stream, table_names[x][1] );
    -        sfnts_pputBYTE( stream, table_names[x][2] );
    -        sfnts_pputBYTE( stream, table_names[x][3] );
    -
    -        /* Checksum */
    -        sfnts_pputULONG( stream, tables[x].checksum );
    -
    -        /* Offset */
    -        sfnts_pputULONG( stream, tables[x].newoffset + 12 + (count * 16) );
    -
    -        /* Length */
    -        sfnts_pputULONG( stream, tables[x].length );
    -    }
    -
    -    /* Now, send the tables */
    -    for (x=0; x < 9; x++)
    -    {
    -        if ( tables[x].length == 0 )    /* skip tables that aren't there */
    -        {
    -            continue;
    -        }
    -
    -#ifdef DEBUG_TRUETYPE
    -        debug("emmiting table '%s'",table_names[x]);
    -#endif
    -
    -        /* 'glyf' table gets special treatment */
    -        if ( strcmp(table_names[x],"glyf")==0 )
    -        {
    -            sfnts_glyf_table(stream,font,tables[x].oldoffset,tables[x].length);
    -        }
    -        else                    /* Other tables may not exceed */
    -        {
    -            /* 65535 bytes in length. */
    -            if ( tables[x].length > 65535 )
    -            {
    -                throw TTException("TrueType font has a table which is too long");
    -            }
    -
    -            /* Start new string if necessary. */
    -            sfnts_new_table(stream, tables[x].length);
    -
    -            /* Seek to proper position in the file. */
    -            fseek( font->file, tables[x].oldoffset, SEEK_SET );
    -
    -            /* Copy the bytes of the table. */
    -            for ( y=0; y < tables[x].length; y++ )
    -            {
    -                if ( (c = fgetc(font->file)) == EOF )
    -                {
    -                    throw TTException("TrueType font may be corrupt (reason 7)");
    -                }
    -
    -                sfnts_pputBYTE(stream, c);
    -            }
    -        }
    -
    -        /* Padd it out to a four byte boundary. */
    -        y=tables[x].length;
    -        while ( (y % 4) != 0 )
    -        {
    -            sfnts_pputBYTE(stream, 0);
    -            y++;
    -#ifdef DEBUG_TRUETYPE_INLINE
    -            puts("\n% pad byte:\n");
    -#endif
    -        }
    -
    -    } /* End of loop for all tables */
    -
    -    /* Close the array. */
    -    sfnts_end_string(stream);
    -    stream.putline("]def");
    -} /* end of ttfont_sfnts() */
    -
    -/*--------------------------------------------------------------
    -** Create the CharStrings dictionary which will translate
    -** PostScript character names to TrueType font character
    -** indexes.
    -**
    -** If we are creating a type 3 instead of a type 42 font,
    -** this array will instead convert PostScript character names
    -** to executable proceedures.
    ---------------------------------------------------------------*/
    -const char *Apple_CharStrings[]=
    -{
    -    ".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign",
    -    "dollar","percent","ampersand","quotesingle","parenleft","parenright",
    -    "asterisk","plus", "comma","hyphen","period","slash","zero","one","two",
    -    "three","four","five","six","seven","eight","nine","colon","semicolon",
    -    "less","equal","greater","question","at","A","B","C","D","E","F","G","H","I",
    -    "J","K", "L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
    -    "bracketleft","backslash","bracketright","asciicircum","underscore","grave",
    -    "a","b","c","d","e","f","g","h","i","j","k", "l","m","n","o","p","q","r","s",
    -    "t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde",
    -    "Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis",
    -    "aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla",
    -    "eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex",
    -    "idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde",
    -    "uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent",
    -    "sterling","section","bullet","paragraph","germandbls","registered",
    -    "copyright","trademark","acute","dieresis","notequal","AE","Oslash",
    -    "infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff",
    -    "summation","product","pi","integral","ordfeminine","ordmasculine","Omega",
    -    "ae","oslash","questiondown","exclamdown","logicalnot","radical","florin",
    -    "approxequal","Delta","guillemotleft","guillemotright","ellipsis",
    -    "nobreakspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash",
    -    "quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge",
    -    "ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright",
    -    "fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase",
    -    "perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave",
    -    "Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple",
    -    "Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde",
    -    "macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron",
    -    "Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth",
    -    "Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior",
    -    "twosuperior","threesuperior","onehalf","onequarter","threequarters","franc",
    -    "Gbreve","gbreve","Idot","Scedilla","scedilla","Cacute","cacute","Ccaron",
    -    "ccaron","dmacron","markingspace","capslock","shift","propeller","enter",
    -    "markingtabrtol","markingtabltor","control","markingdeleteltor",
    -    "markingdeletertol","option","escape","parbreakltor","parbreakrtol",
    -    "newpage","checkmark","linebreakltor","linebreakrtol","markingnobreakspace",
    -    "diamond","appleoutline"
    -};
    -
    -/*
    -** This routine is called by the one below.
    -** It is also called from pprdrv_tt2.c
    -*/
    -const char *ttfont_CharStrings_getname(struct TTFONT *font, int charindex)
    -{
    -    int GlyphIndex;
    -    static char temp[80];
    -    char *ptr;
    -    ULONG len;
    -
    -    Fixed post_format;
    -
    -    /* The 'post' table format number. */
    -    post_format = getFixed( font->post_table );
    -
    -    if ( post_format.whole != 2 || post_format.fraction != 0 )
    -    {
    -        /* We don't have a glyph name table, so generate a name.
    -           This generated name must match exactly the name that is
    -           generated by FT2Font in get_glyph_name */
    -        PyOS_snprintf(temp, 80, "uni%08x", charindex);
    -        return temp;
    -    }
    -
    -    GlyphIndex = (int)getUSHORT( font->post_table + 34 + (charindex * 2) );
    -
    -    if ( GlyphIndex <= 257 )            /* If a standard Apple name, */
    -    {
    -        return Apple_CharStrings[GlyphIndex];
    -    }
    -    else                                /* Otherwise, use one */
    -    {
    -        /* of the pascal strings. */
    -        GlyphIndex -= 258;
    -
    -        /* Set pointer to start of Pascal strings. */
    -        ptr = (char*)(font->post_table + 34 + (font->numGlyphs * 2));
    -
    -        len = (ULONG)*(ptr++);  /* Step thru the strings */
    -        while (GlyphIndex--)            /* until we get to the one */
    -        {
    -            /* that we want. */
    -            ptr += len;
    -            len = (ULONG)*(ptr++);
    -        }
    -
    -        if ( len >= sizeof(temp) )
    -        {
    -            throw TTException("TrueType font file contains a very long PostScript name");
    -        }
    -
    -        strncpy(temp,ptr,len);  /* Copy the pascal string into */
    -        temp[len]=(char)NULL;   /* a buffer and make it ASCIIz. */
    -
    -        return temp;
    -    }
    -} /* end of ttfont_CharStrings_getname() */
    -
    -/*
    -** This is the central routine of this section.
    -*/
    -void ttfont_CharStrings(TTStreamWriter& stream, struct TTFONT *font, std::vector& glyph_ids)
    -{
    -    Fixed post_format;
    -
    -    /* The 'post' table format number. */
    -    post_format = getFixed( font->post_table );
    -
    -    /* Emmit the start of the PostScript code to define the dictionary. */
    -    stream.printf("/CharStrings %d dict dup begin\n", glyph_ids.size());
    -
    -    /* Emmit one key-value pair for each glyph. */
    -    for (std::vector::const_iterator i = glyph_ids.begin();
    -            i != glyph_ids.end(); ++i)
    -    {
    -        if ((font->target_type == PS_TYPE_42 ||
    -             font->target_type == PS_TYPE_42_3_HYBRID)
    -            && *i < 256) /* type 42 */
    -        {
    -            stream.printf("/%s %d def\n",ttfont_CharStrings_getname(font, *i), *i);
    -        }
    -        else                            /* type 3 */
    -        {
    -            stream.printf("/%s{",ttfont_CharStrings_getname(font, *i));
    -
    -            tt_type3_charproc(stream, font, *i);
    -
    -            stream.putline("}_d");      /* "} bind def" */
    -        }
    -    }
    -
    -    stream.putline("end readonly def");
    -} /* end of ttfont_CharStrings() */
    -
    -/*----------------------------------------------------------------
    -** Emmit the code to finish up the dictionary and turn
    -** it into a font.
    -----------------------------------------------------------------*/
    -void ttfont_trailer(TTStreamWriter& stream, struct TTFONT *font)
    -{
    -    /* If we are generating a type 3 font, we need to provide */
    -    /* a BuildGlyph and BuildChar proceedures. */
    -    if (font->target_type == PS_TYPE_3 ||
    -        font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.put_char('\n');
    -
    -        stream.putline("/BuildGlyph");
    -        stream.putline(" {exch begin");         /* start font dictionary */
    -        stream.putline(" CharStrings exch");
    -        stream.putline(" 2 copy known not{pop /.notdef}if");
    -        stream.putline(" true 3 1 roll get exec");
    -        stream.putline(" end}_d");
    -
    -        stream.put_char('\n');
    -
    -        /* This proceedure is for compatiblity with */
    -        /* level 1 interpreters. */
    -        stream.putline("/BuildChar {");
    -        stream.putline(" 1 index /Encoding get exch get");
    -        stream.putline(" 1 index /BuildGlyph get exec");
    -        stream.putline("}_d");
    -
    -        stream.put_char('\n');
    -    }
    -
    -    /* If we are generating a type 42 font, we need to check to see */
    -    /* if this PostScript interpreter understands type 42 fonts.  If */
    -    /* it doesn't, we will hope that the Apple TrueType rasterizer */
    -    /* has been loaded and we will adjust the font accordingly. */
    -    /* I found out how to do this by examining a TrueType font */
    -    /* generated by a Macintosh.  That is where the TrueType interpreter */
    -    /* setup instructions and part of BuildGlyph came from. */
    -    if (font->target_type == PS_TYPE_42 ||
    -        font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.put_char('\n');
    -
    -        /* If we have no "resourcestatus" command, or FontType 42 */
    -        /* is unknown, leave "true" on the stack. */
    -        stream.putline("systemdict/resourcestatus known");
    -        stream.putline(" {42 /FontType resourcestatus");
    -        stream.putline("   {pop pop false}{true}ifelse}");
    -        stream.putline(" {true}ifelse");
    -
    -        /* If true, execute code to produce an error message if */
    -        /* we can't find Apple's TrueDict in VM. */
    -        stream.putline("{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse");
    -
    -        /* Since we are expected to use Apple's TrueDict TrueType */
    -        /* reasterizer, change the font type to 3. */
    -        stream.putline("/FontType 3 def");
    -
    -        /* Define a string to hold the state of the Apple */
    -        /* TrueType interpreter. */
    -        stream.putline(" /TrueState 271 string def");
    -
    -        /* It looks like we get information about the resolution */
    -        /* of the printer and store it in the TrueState string. */
    -        stream.putline(" TrueDict begin sfnts save");
    -        stream.putline(" 72 0 matrix defaultmatrix dtransform dup");
    -        stream.putline(" mul exch dup mul add sqrt cvi 0 72 matrix");
    -        stream.putline(" defaultmatrix dtransform dup mul exch dup");
    -        stream.putline(" mul add sqrt cvi 3 -1 roll restore");
    -        stream.putline(" TrueState initer end");
    -
    -        /* This BuildGlyph procedure will look the name up in the */
    -        /* CharStrings array, and then check to see if what it gets */
    -        /* is a procedure.  If it is, it executes it, otherwise, it */
    -        /* lets the TrueType rasterizer loose on it. */
    -
    -        /* When this proceedure is executed the stack contains */
    -        /* the font dictionary and the character name.  We */
    -        /* exchange arguments and move the dictionary to the */
    -        /* dictionary stack. */
    -        stream.putline(" /BuildGlyph{exch begin");
    -        /* stack: charname */
    -
    -        /* Put two copies of CharStrings on the stack and consume */
    -        /* one testing to see if the charname is defined in it, */
    -        /* leave the answer on the stack. */
    -        stream.putline("  CharStrings dup 2 index known");
    -        /* stack: charname CharStrings bool */
    -
    -        /* Exchange the CharStrings dictionary and the charname, */
    -        /* but if the answer was false, replace the character name */
    -        /* with ".notdef". */
    -        stream.putline("    {exch}{exch pop /.notdef}ifelse");
    -        /* stack: CharStrings charname */
    -
    -        /* Get the value from the CharStrings dictionary and see */
    -        /* if it is executable. */
    -        stream.putline("  get dup xcheck");
    -        /* stack: CharStrings_entry */
    -
    -        /* If is a proceedure.  Execute according to RBIIp 277-278. */
    -        stream.putline("    {currentdict systemdict begin begin exec end end}");
    -
    -        /* Is a TrueType character index, let the rasterizer at it. */
    -        stream.putline("    {TrueDict begin /bander load cvlit exch TrueState render end}");
    -
    -        stream.putline("    ifelse");
    -
    -        /* Pop the font's dictionary off the stack. */
    -        stream.putline(" end}bind def");
    -
    -        /* This is the level 1 compatibility BuildChar procedure. */
    -        /* See RBIIp 281. */
    -        stream.putline(" /BuildChar{");
    -        stream.putline("  1 index /Encoding get exch get");
    -        stream.putline("  1 index /BuildGlyph get exec");
    -        stream.putline(" }bind def");
    -
    -        /* Here we close the condition which is true */
    -        /* if the printer has no built-in TrueType */
    -        /* rasterizer. */
    -        stream.putline("}if");
    -        stream.put_char('\n');
    -    } /* end of if Type 42 not understood. */
    -
    -    stream.putline("FontName currentdict end definefont pop");
    -    /* stream.putline("%%EOF"); */
    -} /* end of ttfont_trailer() */
    -
    -/*------------------------------------------------------------------
    -** This is the externally callable routine which inserts the font.
    -------------------------------------------------------------------*/
    -
    -void read_font(const char *filename, font_type_enum target_type, std::vector& glyph_ids, TTFONT& font)
    -{
    -    BYTE *ptr;
    -
    -    /* Decide what type of PostScript font we will be generating. */
    -    font.target_type = target_type;
    -
    -    if (font.target_type == PS_TYPE_42)
    -    {
    -        bool has_low = false;
    -        bool has_high = false;
    -
    -        for (std::vector::const_iterator i = glyph_ids.begin();
    -                i != glyph_ids.end(); ++i)
    -        {
    -            if (*i > 255)
    -            {
    -                has_high = true;
    -                if (has_low) break;
    -            }
    -            else
    -            {
    -                has_low = true;
    -                if (has_high) break;
    -            }
    -        }
    -
    -        if (has_high && has_low)
    -        {
    -            font.target_type = PS_TYPE_42_3_HYBRID;
    -        }
    -        else if (has_high && !has_low)
    -        {
    -            font.target_type = PS_TYPE_3;
    -        }
    -    }
    -
    -    /* Save the file name for error messages. */
    -    font.filename=filename;
    -
    -    /* Open the font file */
    -    if ( (font.file = fopen(filename,"rb")) == (FILE*)NULL )
    -    {
    -        throw TTException("Failed to open TrueType font");
    -    }
    -
    -    /* Allocate space for the unvarying part of the offset table. */
    -    assert(font.offset_table == NULL);
    -    font.offset_table = (BYTE*)calloc( 12, sizeof(BYTE) );
    -
    -    /* Read the first part of the offset table. */
    -    if ( fread( font.offset_table, sizeof(BYTE), 12, font.file ) != 12 )
    -    {
    -        throw TTException("TrueType font may be corrupt (reason 1)");
    -    }
    -
    -    /* Determine how many directory entries there are. */
    -    font.numTables = getUSHORT( font.offset_table + 4 );
    -#ifdef DEBUG_TRUETYPE
    -    debug("numTables=%d",(int)font.numTables);
    -#endif
    -
    -    /* Expand the memory block to hold the whole thing. */
    -    font.offset_table = (BYTE*)realloc( font.offset_table, sizeof(BYTE) * (12 + font.numTables * 16) );
    -
    -    /* Read the rest of the table directory. */
    -    if ( fread( font.offset_table + 12, sizeof(BYTE), (font.numTables*16), font.file ) != (font.numTables*16) )
    -    {
    -        throw TTException("TrueType font may be corrupt (reason 2)");
    -    }
    -
    -    /* Extract information from the "Offset" table. */
    -    font.TTVersion = getFixed( font.offset_table );
    -
    -    /* Load the "head" table and extract information from it. */
    -    ptr = GetTable(&font, "head");
    -    try
    -    {
    -        font.MfrRevision = getFixed( ptr + 4 );           /* font revision number */
    -        font.unitsPerEm = getUSHORT( ptr + 18 );
    -        font.HUPM = font.unitsPerEm / 2;
    -#ifdef DEBUG_TRUETYPE
    -        debug("unitsPerEm=%d",(int)font.unitsPerEm);
    -#endif
    -        font.llx = topost2( getFWord( ptr + 36 ) );               /* bounding box info */
    -        font.lly = topost2( getFWord( ptr + 38 ) );
    -        font.urx = topost2( getFWord( ptr + 40 ) );
    -        font.ury = topost2( getFWord( ptr + 42 ) );
    -        font.indexToLocFormat = getSHORT( ptr + 50 );     /* size of 'loca' data */
    -        if (font.indexToLocFormat != 0 && font.indexToLocFormat != 1)
    -        {
    -            throw TTException("TrueType font is unusable because indexToLocFormat != 0");
    -        }
    -        if ( getSHORT(ptr+52) != 0 )
    -        {
    -            throw TTException("TrueType font is unusable because glyphDataFormat != 0");
    -        }
    -    }
    -    catch (TTException& )
    -    {
    -        free(ptr);
    -        throw;
    -    }
    -    free(ptr);
    -
    -    /* Load information from the "name" table. */
    -    Read_name(&font);
    -
    -    /* We need to have the PostScript table around. */
    -    assert(font.post_table == NULL);
    -    font.post_table = GetTable(&font, "post");
    -    font.numGlyphs = getUSHORT( font.post_table + 32 );
    -
    -    /* If we are generating a Type 3 font, we will need to */
    -    /* have the 'loca' and 'glyf' tables arround while */
    -    /* we are generating the CharStrings. */
    -    if (font.target_type == PS_TYPE_3 || font.target_type == PDF_TYPE_3 ||
    -            font.target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        BYTE *ptr;                      /* We need only one value */
    -        ptr = GetTable(&font, "hhea");
    -        font.numberOfHMetrics = getUSHORT(ptr + 34);
    -        free(ptr);
    -
    -        assert(font.loca_table == NULL);
    -        font.loca_table = GetTable(&font,"loca");
    -        assert(font.glyf_table == NULL);
    -        font.glyf_table = GetTable(&font,"glyf");
    -        assert(font.hmtx_table == NULL);
    -        font.hmtx_table = GetTable(&font,"hmtx");
    -    }
    -
    -    if (glyph_ids.size() == 0)
    -    {
    -        glyph_ids.clear();
    -        glyph_ids.reserve(font.numGlyphs);
    -        for (int x = 0; x < font.numGlyphs; ++x)
    -        {
    -            glyph_ids.push_back(x);
    -        }
    -    }
    -    else if (font.target_type == PS_TYPE_3 ||
    -             font.target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        ttfont_add_glyph_dependencies(&font, glyph_ids);
    -    }
    -
    -} /* end of insert_ttfont() */
    -
    -void insert_ttfont(const char *filename, TTStreamWriter& stream,
    -                   font_type_enum target_type, std::vector& glyph_ids)
    -{
    -    struct TTFONT font;
    -
    -    read_font(filename, target_type, glyph_ids, font);
    -
    -    /* Write the header for the PostScript font. */
    -    ttfont_header(stream, &font);
    -
    -    /* Define the encoding. */
    -    ttfont_encoding(stream, &font, glyph_ids, target_type);
    -
    -    /* Insert FontInfo dictionary. */
    -    ttfont_FontInfo(stream, &font);
    -
    -    /* If we are generating a type 42 font, */
    -    /* emmit the sfnts array. */
    -    if (font.target_type == PS_TYPE_42 ||
    -        font.target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        ttfont_sfnts(stream, &font);
    -    }
    -
    -    /* Emmit the CharStrings array. */
    -    ttfont_CharStrings(stream, &font, glyph_ids);
    -
    -    /* Send the font trailer. */
    -    ttfont_trailer(stream, &font);
    -
    -} /* end of insert_ttfont() */
    -
    -class StringStreamWriter : public TTStreamWriter
    -{
    -    std::ostringstream oss;
    -
    -public:
    -    void write(const char* a)
    -    {
    -        oss << a;
    -    }
    -
    -    std::string str()
    -    {
    -        return oss.str();
    -    }
    -};
    -
    -void get_pdf_charprocs(const char *filename, std::vector& glyph_ids, TTDictionaryCallback& dict)
    -{
    -    struct TTFONT font;
    -
    -    read_font(filename, PDF_TYPE_3, glyph_ids, font);
    -
    -    for (std::vector::const_iterator i = glyph_ids.begin();
    -            i != glyph_ids.end(); ++i)
    -    {
    -        StringStreamWriter writer;
    -        tt_type3_charproc(writer, &font, *i);
    -        const char* name = ttfont_CharStrings_getname(&font, *i);
    -        dict.add_pair(name, writer.str().c_str());
    -    }
    -}
    -
    -TTFONT::TTFONT() :
    -    file(NULL),
    -    PostName(NULL),
    -    FullName(NULL),
    -    FamilyName(NULL),
    -    Style(NULL),
    -    Copyright(NULL),
    -    Version(NULL),
    -    Trademark(NULL),
    -    offset_table(NULL),
    -    post_table(NULL),
    -    loca_table(NULL),
    -    glyf_table(NULL),
    -    hmtx_table(NULL)
    -{
    -
    -}
    -
    -TTFONT::~TTFONT()
    -{
    -    if (file)
    -    {
    -        fclose(file);
    -    }
    -    free(PostName);
    -    free(FullName);
    -    free(FamilyName);
    -    free(Style);
    -    free(Copyright);
    -    free(Version);
    -    free(Trademark);
    -    free(offset_table);
    -    free(post_table);
    -    free(loca_table);
    -    free(glyf_table);
    -    free(hmtx_table);
    -}
    -
    -/* end of file */
    diff --git a/extern/ttconv/pprdrv_tt2.cpp b/extern/ttconv/pprdrv_tt2.cpp
    deleted file mode 100644
    index 2643afa09930..000000000000
    --- a/extern/ttconv/pprdrv_tt2.cpp
    +++ /dev/null
    @@ -1,737 +0,0 @@
    -/* -*- mode: c++; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -/*
    -** ~ppr/src/pprdrv/pprdrv_tt2.c
    -** Copyright 1995, Trinity College Computing Center.
    -** Written by David Chappell.
    -**
    -** Permission to use, copy, modify, and distribute this software and its
    -** documentation for any purpose and without fee is hereby granted, provided
    -** that the above copyright notice appear in all copies and that both that
    -** copyright notice and this permission notice appear in supporting
    -** documentation.  This software is provided "as is" without express or
    -** implied warranty.
    -**
    -** TrueType font support.  These functions allow PPR to generate
    -** PostScript fonts from Microsoft compatible TrueType font files.
    -**
    -** The functions in this file do most of the work to convert a
    -** TrueType font to a type 3 PostScript font.
    -**
    -** Most of the material in this file is derived from a program called
    -** "ttf2ps" which L. S. Ng posted to the usenet news group
    -** "comp.sources.postscript".  The author did not provide a copyright
    -** notice or indicate any restrictions on use.
    -**
    -** Last revised 11 July 1995.
    -*/
    -
    -#include "global_defines.h"
    -#include 
    -#include 
    -#include 
    -#include 
    -#include "pprdrv.h"
    -#include "truetype.h"
    -#include 
    -#include 
    -#include 
    -
    -class GlyphToType3
    -{
    -private:
    -    GlyphToType3& operator=(const GlyphToType3& other);
    -    GlyphToType3(const GlyphToType3& other);
    -
    -    /* The PostScript bounding box. */
    -    int llx,lly,urx,ury;
    -    int advance_width;
    -
    -    /* Variables to hold the character data. */
    -    int *epts_ctr;                      /* array of contour endpoints */
    -    int num_pts, num_ctr;               /* number of points, number of coutours */
    -    FWord *xcoor, *ycoor;               /* arrays of x and y coordinates */
    -    BYTE *tt_flags;                     /* array of TrueType flags */
    -
    -    int stack_depth;            /* A book-keeping variable for keeping track of the depth of the PS stack */
    -
    -    bool pdf_mode;
    -
    -    void load_char(TTFONT* font, BYTE *glyph);
    -    void stack(TTStreamWriter& stream, int new_elem);
    -    void stack_end(TTStreamWriter& stream);
    -    void PSConvert(TTStreamWriter& stream);
    -    void PSCurveto(TTStreamWriter& stream,
    -                   FWord x0, FWord y0,
    -                   FWord x1, FWord y1,
    -                   FWord x2, FWord y2);
    -    void PSMoveto(TTStreamWriter& stream, int x, int y);
    -    void PSLineto(TTStreamWriter& stream, int x, int y);
    -    void do_composite(TTStreamWriter& stream, struct TTFONT *font, BYTE *glyph);
    -
    -public:
    -    GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int charindex, bool embedded = false);
    -    ~GlyphToType3();
    -};
    -
    -// Each point on a TrueType contour is either on the path or off it (a
    -// control point); here's a simple representation for building such
    -// contours. Added by Jouni Seppänen 2012-05-27.
    -enum Flag { ON_PATH, OFF_PATH };
    -struct FlaggedPoint
    -{
    -    enum Flag flag;
    -    FWord x;
    -    FWord y;
    -    FlaggedPoint(Flag flag_, FWord x_, FWord y_): flag(flag_), x(x_), y(y_) {};
    -};
    -
    -double area(FWord *x, FWord *y, int n);
    -#define sqr(x) ((x)*(x))
    -
    -#define NOMOREINCTR -1
    -#define NOMOREOUTCTR -1
    -
    -/*
    -** This routine is used to break the character
    -** procedure up into a number of smaller
    -** procedures.  This is necessary so as not to
    -** overflow the stack on certain level 1 interpreters.
    -**
    -** Prepare to push another item onto the stack,
    -** starting a new proceedure if necessary.
    -**
    -** Not all the stack depth calculations in this routine
    -** are perfectly accurate, but they do the job.
    -*/
    -void GlyphToType3::stack(TTStreamWriter& stream, int new_elem)
    -{
    -    if ( !pdf_mode && num_pts > 25 )                    /* Only do something of we will */
    -    {
    -        /* have a log of points. */
    -        if (stack_depth == 0)
    -        {
    -            stream.put_char('{');
    -            stack_depth=1;
    -        }
    -
    -        stack_depth += new_elem;                /* Account for what we propose to add */
    -
    -        if (stack_depth > 100)
    -        {
    -            stream.puts("}_e{");
    -            stack_depth = 3 + new_elem; /* A rough estimate */
    -        }
    -    }
    -} /* end of stack() */
    -
    -void GlyphToType3::stack_end(TTStreamWriter& stream)                    /* called at end */
    -{
    -    if ( !pdf_mode && stack_depth )
    -    {
    -        stream.puts("}_e");
    -        stack_depth=0;
    -    }
    -} /* end of stack_end() */
    -
    -/*
    -** We call this routine to emmit the PostScript code
    -** for the character we have loaded with load_char().
    -*/
    -void GlyphToType3::PSConvert(TTStreamWriter& stream)
    -{
    -    int j, k;
    -
    -    /* Step thru the contours.
    -     * j = index to xcoor, ycoor, tt_flags (point data)
    -     * k = index to epts_ctr (which points belong to the same contour) */
    -    for(j = k = 0; k < num_ctr; k++)
    -    {
    -        // A TrueType contour consists of on-path and off-path points.
    -        // Two consecutive on-path points are to be joined with a
    -        // line; off-path points between on-path points indicate a
    -        // quadratic spline, where the off-path point is the control
    -        // point. Two consecutive off-path points have an implicit
    -        // on-path point midway between them.
    -        std::list points;
    -
    -        // Represent flags and x/y coordinates as a C++ list
    -        for (; j <= epts_ctr[k]; j++)
    -        {
    -            if (!(tt_flags[j] & 1)) {
    -                points.push_back(FlaggedPoint(OFF_PATH, xcoor[j], ycoor[j]));
    -            } else {
    -                points.push_back(FlaggedPoint(ON_PATH, xcoor[j], ycoor[j]));
    -            }
    -        }
    -
    -        if (points.size() == 0) {
    -            // Don't try to access the last element of an empty list
    -            continue;
    -        }
    -
    -        // For any two consecutive off-path points, insert the implied
    -        // on-path point.
    -        FlaggedPoint prev = points.back();
    -        for (std::list::iterator it = points.begin();
    -             it != points.end();
    -             it++)
    -        {
    -            if (prev.flag == OFF_PATH && it->flag == OFF_PATH)
    -            {
    -                points.insert(it,
    -                              FlaggedPoint(ON_PATH,
    -                                           (prev.x + it->x) / 2,
    -                                           (prev.y + it->y) / 2));
    -            }
    -            prev = *it;
    -        }
    -        // Handle the wrap-around: insert a point either at the beginning
    -        // or at the end that has the same coordinates as the opposite point.
    -        // This also ensures that the initial point is ON_PATH.
    -        if (points.front().flag == OFF_PATH)
    -        {
    -            assert(points.back().flag == ON_PATH);
    -            points.insert(points.begin(), points.back());
    -        }
    -        else
    -        {
    -            assert(points.front().flag == ON_PATH);
    -            points.push_back(points.front());
    -        }
    -
    -        // The first point
    -        stack(stream, 3);
    -        PSMoveto(stream, points.front().x, points.front().y);
    -
    -        // Step through the remaining points
    -        std::list::const_iterator it = points.begin();
    -        for (it++; it != points.end(); /* incremented inside */)
    -        {
    -            const FlaggedPoint& point = *it;
    -            if (point.flag == ON_PATH)
    -            {
    -                stack(stream, 3);
    -                PSLineto(stream, point.x, point.y);
    -                it++;
    -            } else {
    -                std::list::const_iterator prev = it, next = it;
    -                prev--;
    -                next++;
    -                assert(prev->flag == ON_PATH);
    -                assert(next->flag == ON_PATH);
    -                stack(stream, 7);
    -                PSCurveto(stream,
    -                          prev->x, prev->y,
    -                          point.x, point.y,
    -                          next->x, next->y);
    -                it++;
    -                it++;
    -            }
    -        }
    -    }
    -
    -    /* Now, we can fill the whole thing. */
    -    stack(stream, 1);
    -    stream.puts( pdf_mode ? "f" : "_cl" );
    -} /* end of PSConvert() */
    -
    -void GlyphToType3::PSMoveto(TTStreamWriter& stream, int x, int y)
    -{
    -    stream.printf(pdf_mode ? "%d %d m\n" : "%d %d _m\n",
    -                  x, y);
    -}
    -
    -void GlyphToType3::PSLineto(TTStreamWriter& stream, int x, int y)
    -{
    -    stream.printf(pdf_mode ? "%d %d l\n" : "%d %d _l\n",
    -                  x, y);
    -}
    -
    -/*
    -** Emit a PostScript "curveto" command, assuming the current point
    -** is (x0, y0), the control point of a quadratic spline is (x1, y1),
    -** and the endpoint is (x2, y2). Note that this requires a conversion,
    -** since PostScript splines are cubic.
    -*/
    -void GlyphToType3::PSCurveto(TTStreamWriter& stream,
    -                             FWord x0, FWord y0,
    -                             FWord x1, FWord y1,
    -                             FWord x2, FWord y2)
    -{
    -    double sx[3], sy[3], cx[3], cy[3];
    -
    -    sx[0] = x0;
    -    sy[0] = y0;
    -    sx[1] = x1;
    -    sy[1] = y1;
    -    sx[2] = x2;
    -    sy[2] = y2;
    -    cx[0] = (2*sx[1]+sx[0])/3;
    -    cy[0] = (2*sy[1]+sy[0])/3;
    -    cx[1] = (sx[2]+2*sx[1])/3;
    -    cy[1] = (sy[2]+2*sy[1])/3;
    -    cx[2] = sx[2];
    -    cy[2] = sy[2];
    -    stream.printf("%d %d %d %d %d %d %s\n",
    -                  (int)cx[0], (int)cy[0], (int)cx[1], (int)cy[1],
    -                  (int)cx[2], (int)cy[2], pdf_mode ? "c" : "_c");
    -}
    -
    -/*
    -** Deallocate the structures which stored
    -** the data for the last simple glyph.
    -*/
    -GlyphToType3::~GlyphToType3()
    -{
    -    free(tt_flags);            /* The flags array */
    -    free(xcoor);               /* The X coordinates */
    -    free(ycoor);               /* The Y coordinates */
    -    free(epts_ctr);            /* The array of contour endpoints */
    -}
    -
    -/*
    -** Load the simple glyph data pointed to by glyph.
    -** The pointer "glyph" should point 10 bytes into
    -** the glyph data.
    -*/
    -void GlyphToType3::load_char(TTFONT* font, BYTE *glyph)
    -{
    -    int x;
    -    BYTE c, ct;
    -
    -    /* Read the contour endpoints list. */
    -    epts_ctr = (int *)calloc(num_ctr,sizeof(int));
    -    for (x = 0; x < num_ctr; x++)
    -    {
    -        epts_ctr[x] = getUSHORT(glyph);
    -        glyph += 2;
    -    }
    -
    -    /* From the endpoint of the last contour, we can */
    -    /* determine the number of points. */
    -    num_pts = epts_ctr[num_ctr-1]+1;
    -#ifdef DEBUG_TRUETYPE
    -    debug("num_pts=%d",num_pts);
    -    stream.printf("%% num_pts=%d\n",num_pts);
    -#endif
    -
    -    /* Skip the instructions. */
    -    x = getUSHORT(glyph);
    -    glyph += 2;
    -    glyph += x;
    -
    -    /* Allocate space to hold the data. */
    -    tt_flags = (BYTE *)calloc(num_pts,sizeof(BYTE));
    -    xcoor = (FWord *)calloc(num_pts,sizeof(FWord));
    -    ycoor = (FWord *)calloc(num_pts,sizeof(FWord));
    -
    -    /* Read the flags array, uncompressing it as we go. */
    -    /* There is danger of overflow here. */
    -    for (x = 0; x < num_pts; )
    -    {
    -        tt_flags[x++] = c = *(glyph++);
    -
    -        if (c&8)                /* If next byte is repeat count, */
    -        {
    -            ct = *(glyph++);
    -
    -            if ( (x + ct) > num_pts )
    -            {
    -                throw TTException("Error in TT flags");
    -            }
    -
    -            while (ct--)
    -            {
    -                tt_flags[x++] = c;
    -            }
    -        }
    -    }
    -
    -    /* Read the x coordinates */
    -    for (x = 0; x < num_pts; x++)
    -    {
    -        if (tt_flags[x] & 2)            /* one byte value with */
    -        {
    -            /* external sign */
    -            c = *(glyph++);
    -            xcoor[x] = (tt_flags[x] & 0x10) ? c : (-1 * (int)c);
    -        }
    -        else if (tt_flags[x] & 0x10)    /* repeat last */
    -        {
    -            xcoor[x] = 0;
    -        }
    -        else                            /* two byte signed value */
    -        {
    -            xcoor[x] = getFWord(glyph);
    -            glyph+=2;
    -        }
    -    }
    -
    -    /* Read the y coordinates */
    -    for (x = 0; x < num_pts; x++)
    -    {
    -        if (tt_flags[x] & 4)            /* one byte value with */
    -        {
    -            /* external sign */
    -            c = *(glyph++);
    -            ycoor[x] = (tt_flags[x] & 0x20) ? c : (-1 * (int)c);
    -        }
    -        else if (tt_flags[x] & 0x20)    /* repeat last value */
    -        {
    -            ycoor[x] = 0;
    -        }
    -        else                            /* two byte signed value */
    -        {
    -            ycoor[x] = getUSHORT(glyph);
    -            glyph+=2;
    -        }
    -    }
    -
    -    /* Convert delta values to absolute values. */
    -    for (x = 1; x < num_pts; x++)
    -    {
    -        xcoor[x] += xcoor[x-1];
    -        ycoor[x] += ycoor[x-1];
    -    }
    -
    -    for (x=0; x < num_pts; x++)
    -    {
    -        xcoor[x] = topost(xcoor[x]);
    -        ycoor[x] = topost(ycoor[x]);
    -    }
    -
    -} /* end of load_char() */
    -
    -/*
    -** Emmit PostScript code for a composite character.
    -*/
    -void GlyphToType3::do_composite(TTStreamWriter& stream, struct TTFONT *font, BYTE *glyph)
    -{
    -    USHORT flags;
    -    USHORT glyphIndex;
    -    int arg1;
    -    int arg2;
    -
    -    /* Once around this loop for each component. */
    -    do
    -    {
    -        flags = getUSHORT(glyph);       /* read the flags word */
    -        glyph += 2;
    -
    -        glyphIndex = getUSHORT(glyph);  /* read the glyphindex word */
    -        glyph += 2;
    -
    -        if (flags & ARG_1_AND_2_ARE_WORDS)
    -        {
    -            /* The tt spec. seems to say these are signed. */
    -            arg1 = getSHORT(glyph);
    -            glyph += 2;
    -            arg2 = getSHORT(glyph);
    -            glyph += 2;
    -        }
    -        else                    /* The tt spec. does not clearly indicate */
    -        {
    -            /* whether these values are signed or not. */
    -            arg1 = *(signed char *)(glyph++);
    -            arg2 = *(signed char *)(glyph++);
    -        }
    -
    -        if (flags & WE_HAVE_A_SCALE)
    -        {
    -            glyph += 2;
    -        }
    -        else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
    -        {
    -            glyph += 4;
    -        }
    -        else if (flags & WE_HAVE_A_TWO_BY_TWO)
    -        {
    -            glyph += 8;
    -        }
    -        else
    -        {
    -        }
    -
    -        /* Debugging */
    -#ifdef DEBUG_TRUETYPE
    -        stream.printf("%% flags=%d, arg1=%d, arg2=%d\n",
    -                      (int)flags,arg1,arg2);
    -#endif
    -
    -        if (pdf_mode)
    -        {
    -            if ( flags & ARGS_ARE_XY_VALUES )
    -            {
    -                /* We should have been able to use 'Do' to reference the
    -                   subglyph here.  However, that doesn't seem to work with
    -                   xpdf or gs (only acrobat), so instead, this just includes
    -                   the subglyph here inline. */
    -                stream.printf("q 1 0 0 1 %d %d cm\n", topost(arg1), topost(arg2));
    -            }
    -            else
    -            {
    -                stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2);
    -            }
    -            GlyphToType3(stream, font, glyphIndex, true);
    -            if ( flags & ARGS_ARE_XY_VALUES )
    -            {
    -                stream.printf("\nQ\n");
    -            }
    -        }
    -        else
    -        {
    -            /* If we have an (X,Y) shif and it is non-zero, */
    -            /* translate the coordinate system. */
    -            if ( flags & ARGS_ARE_XY_VALUES )
    -            {
    -                if ( arg1 != 0 || arg2 != 0 )
    -                    stream.printf("gsave %d %d translate\n", topost(arg1), topost(arg2) );
    -            }
    -            else
    -            {
    -                stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2);
    -            }
    -
    -            /* Invoke the CharStrings procedure to print the component. */
    -            stream.printf("false CharStrings /%s get exec\n",
    -                          ttfont_CharStrings_getname(font,glyphIndex));
    -
    -            /* If we translated the coordinate system, */
    -            /* put it back the way it was. */
    -            if ( flags & ARGS_ARE_XY_VALUES && (arg1 != 0 || arg2 != 0) )
    -            {
    -                stream.puts("grestore ");
    -            }
    -        }
    -
    -    }
    -    while (flags & MORE_COMPONENTS);
    -
    -} /* end of do_composite() */
    -
    -/*
    -** Return a pointer to a specific glyph's data.
    -*/
    -BYTE *find_glyph_data(struct TTFONT *font, int charindex)
    -{
    -    ULONG off;
    -    ULONG length;
    -
    -    /* Read the glyph offset from the index to location table. */
    -    if (font->indexToLocFormat == 0)
    -    {
    -        off = getUSHORT( font->loca_table + (charindex * 2) );
    -        off *= 2;
    -        length = getUSHORT( font->loca_table + ((charindex+1) * 2) );
    -        length *= 2;
    -        length -= off;
    -    }
    -    else
    -    {
    -        off = getULONG( font->loca_table + (charindex * 4) );
    -        length = getULONG( font->loca_table + ((charindex+1) * 4) );
    -        length -= off;
    -    }
    -
    -    if (length > 0)
    -    {
    -        return font->glyf_table + off;
    -    }
    -    else
    -    {
    -        return (BYTE*)NULL;
    -    }
    -
    -} /* end of find_glyph_data() */
    -
    -GlyphToType3::GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int charindex, bool embedded /* = false */)
    -{
    -    BYTE *glyph;
    -
    -    tt_flags = NULL;
    -    xcoor = NULL;
    -    ycoor = NULL;
    -    epts_ctr = NULL;
    -    stack_depth = 0;
    -    pdf_mode = font->target_type < 0;
    -
    -    /* Get a pointer to the data. */
    -    glyph = find_glyph_data( font, charindex );
    -
    -    /* If the character is blank, it has no bounding box, */
    -    /* otherwise read the bounding box. */
    -    if ( glyph == (BYTE*)NULL )
    -    {
    -        llx=lly=urx=ury=0;      /* A blank char has an all zero BoundingBox */
    -        num_ctr=0;              /* Set this for later if()s */
    -    }
    -    else
    -    {
    -        /* Read the number of contours. */
    -        num_ctr = getSHORT(glyph);
    -
    -        /* Read PostScript bounding box. */
    -        llx = getFWord(glyph + 2);
    -        lly = getFWord(glyph + 4);
    -        urx = getFWord(glyph + 6);
    -        ury = getFWord(glyph + 8);
    -
    -        /* Advance the pointer. */
    -        glyph += 10;
    -    }
    -
    -    /* If it is a simple character, load its data. */
    -    if (num_ctr > 0)
    -    {
    -        load_char(font, glyph);
    -    }
    -    else
    -    {
    -        num_pts=0;
    -    }
    -
    -    /* Consult the horizontal metrics table to determine */
    -    /* the character width. */
    -    if ( charindex < font->numberOfHMetrics )
    -    {
    -        advance_width = getuFWord( font->hmtx_table + (charindex * 4) );
    -    }
    -    else
    -    {
    -        advance_width = getuFWord( font->hmtx_table + ((font->numberOfHMetrics-1) * 4) );
    -    }
    -
    -    /* Execute setcachedevice in order to inform the font machinery */
    -    /* of the character bounding box and advance width. */
    -    stack(stream, 7);
    -    if (pdf_mode)
    -    {
    -        if (!embedded) {
    -            stream.printf("%d 0 %d %d %d %d d1\n",
    -                          topost(advance_width),
    -                          topost(llx), topost(lly), topost(urx), topost(ury) );
    -        }
    -    }
    -    else if (font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.printf("pop gsave .001 .001 scale %d 0 %d %d %d %d setcachedevice\n",
    -                      topost(advance_width),
    -                      topost(llx), topost(lly), topost(urx), topost(ury) );
    -    }
    -    else
    -    {
    -        stream.printf("%d 0 %d %d %d %d _sc\n",
    -                      topost(advance_width),
    -                      topost(llx), topost(lly), topost(urx), topost(ury) );
    -    }
    -
    -    /* If it is a simple glyph, convert it, */
    -    /* otherwise, close the stack business. */
    -    if ( num_ctr > 0 )          /* simple */
    -    {
    -        PSConvert(stream);
    -    }
    -    else if ( num_ctr < 0 )     /* composite */
    -    {
    -        do_composite(stream, font, glyph);
    -    }
    -
    -    if (font->target_type == PS_TYPE_42_3_HYBRID)
    -    {
    -        stream.printf("\ngrestore\n");
    -    }
    -
    -    stack_end(stream);
    -}
    -
    -/*
    -** This is the routine which is called from pprdrv_tt.c.
    -*/
    -void tt_type3_charproc(TTStreamWriter& stream, struct TTFONT *font, int charindex)
    -{
    -    GlyphToType3 glyph(stream, font, charindex);
    -} /* end of tt_type3_charproc() */
    -
    -/*
    -** Some of the given glyph ids may refer to composite glyphs.
    -** This function adds all of the dependencies of those composite
    -** glyphs to the glyph id vector.  Michael Droettboom [06-07-07]
    -*/
    -void ttfont_add_glyph_dependencies(struct TTFONT *font, std::vector& glyph_ids)
    -{
    -    std::sort(glyph_ids.begin(), glyph_ids.end());
    -
    -    std::stack glyph_stack;
    -    for (std::vector::iterator i = glyph_ids.begin();
    -            i != glyph_ids.end(); ++i)
    -    {
    -        glyph_stack.push(*i);
    -    }
    -
    -    while (glyph_stack.size())
    -    {
    -        int gind = glyph_stack.top();
    -        glyph_stack.pop();
    -
    -        BYTE* glyph = find_glyph_data( font, gind );
    -        if (glyph != (BYTE*)NULL)
    -        {
    -
    -            int num_ctr = getSHORT(glyph);
    -            if (num_ctr <= 0)   // This is a composite glyph
    -            {
    -
    -                glyph += 10;
    -                USHORT flags = 0;
    -
    -                do
    -                {
    -                    flags = getUSHORT(glyph);
    -                    glyph += 2;
    -                    gind = (int)getUSHORT(glyph);
    -                    glyph += 2;
    -
    -                    std::vector::iterator insertion =
    -                        std::lower_bound(glyph_ids.begin(), glyph_ids.end(), gind);
    -                    if (insertion == glyph_ids.end() || *insertion != gind)
    -                    {
    -                        glyph_ids.insert(insertion, gind);
    -                        glyph_stack.push(gind);
    -                    }
    -
    -                    if (flags & ARG_1_AND_2_ARE_WORDS)
    -                    {
    -                        glyph += 4;
    -                    }
    -                    else
    -                    {
    -                        glyph += 2;
    -                    }
    -
    -                    if (flags & WE_HAVE_A_SCALE)
    -                    {
    -                        glyph += 2;
    -                    }
    -                    else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
    -                    {
    -                        glyph += 4;
    -                    }
    -                    else if (flags & WE_HAVE_A_TWO_BY_TWO)
    -                    {
    -                        glyph += 8;
    -                    }
    -                }
    -                while (flags & MORE_COMPONENTS);
    -            }
    -        }
    -    }
    -}
    -
    -/* end of file */
    diff --git a/extern/ttconv/truetype.h b/extern/ttconv/truetype.h
    deleted file mode 100644
    index 86be14fe3705..000000000000
    --- a/extern/ttconv/truetype.h
    +++ /dev/null
    @@ -1,129 +0,0 @@
    -/* -*- mode: c; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -#include 
    -
    -/*
    -** ~ppr/src/include/typetype.h
    -**
    -** Permission to use, copy, modify, and distribute this software and its
    -** documentation for any purpose and without fee is hereby granted, provided
    -** that the above copyright notice appear in all copies and that both that
    -** copyright notice and this permission notice appear in supporting
    -** documentation.  This software is provided "as is" without express or
    -** implied warranty.
    -**
    -** This include file is shared by the source files
    -** "pprdrv/pprdrv_tt.c" and "pprdrv/pprdrv_tt2.c".
    -**
    -** Last modified 19 April 1995.
    -*/
    -
    -/* Types used in TrueType font files. */
    -#define BYTE unsigned char
    -#define USHORT unsigned short int
    -#define SHORT short signed int
    -#define ULONG unsigned int
    -#define FIXED long signed int
    -#define FWord short signed int
    -#define uFWord short unsigned int
    -
    -/* This structure stores a 16.16 bit fixed */
    -/* point number. */
    -typedef struct
    -    {
    -    short int whole;
    -    unsigned short int fraction;
    -    } Fixed;
    -
    -/* This structure tells what we have found out about */
    -/* the current font. */
    -struct TTFONT
    -    {
    -    // A quick-and-dirty way to create a minimum level of exception safety
    -    // Added by Michael Droettboom
    -    TTFONT();
    -    ~TTFONT();
    -
    -    const char *filename;               /* Name of TT file */
    -    FILE *file;                         /* the open TT file */
    -    font_type_enum  target_type;        /* 42 or 3 for PS, or -3 for PDF */
    -
    -    ULONG numTables;                    /* number of tables present */
    -    char *PostName;                     /* Font's PostScript name */
    -    char *FullName;                     /* Font's full name */
    -    char *FamilyName;                   /* Font's family name */
    -    char *Style;                        /* Font's style string */
    -    char *Copyright;                    /* Font's copyright string */
    -    char *Version;                      /* Font's version string */
    -    char *Trademark;                    /* Font's trademark string */
    -    int llx,lly,urx,ury;                /* bounding box */
    -
    -    Fixed TTVersion;                    /* Truetype version number from offset table */
    -    Fixed MfrRevision;                  /* Revision number of this font */
    -
    -    BYTE *offset_table;                 /* Offset table in memory */
    -    BYTE *post_table;                   /* 'post' table in memory */
    -
    -    BYTE *loca_table;                   /* 'loca' table in memory */
    -    BYTE *glyf_table;                   /* 'glyf' table in memory */
    -    BYTE *hmtx_table;                   /* 'hmtx' table in memory */
    -
    -    USHORT numberOfHMetrics;
    -    int unitsPerEm;                     /* unitsPerEm converted to int */
    -    int HUPM;                           /* half of above */
    -
    -    int numGlyphs;                      /* from 'post' table */
    -
    -    int indexToLocFormat;               /* short or long offsets */
    -};
    -
    -ULONG getULONG(BYTE *p);
    -USHORT getUSHORT(BYTE *p);
    -Fixed getFixed(BYTE *p);
    -
    -/*
    -** Get an funits word.
    -** since it is 16 bits long, we can
    -** use getUSHORT() to do the real work.
    -*/
    -#define getFWord(x) (FWord)getUSHORT(x)
    -#define getuFWord(x) (uFWord)getUSHORT(x)
    -
    -/*
    -** We can get a SHORT by making USHORT signed.
    -*/
    -#define getSHORT(x) (SHORT)getUSHORT(x)
    -
    -/* This is the one routine in pprdrv_tt.c that is */
    -/* called from pprdrv_tt.c. */
    -const char *ttfont_CharStrings_getname(struct TTFONT *font, int charindex);
    -
    -void tt_type3_charproc(TTStreamWriter& stream, struct TTFONT *font, int charindex);
    -
    -/* Added 06-07-07 Michael Droettboom */
    -void ttfont_add_glyph_dependencies(struct TTFONT *font, std::vector& glypy_ids);
    -
    -/* This routine converts a number in the font's character coordinate */
    -/* system to a number in a 1000 unit character system. */
    -#define topost(x) (int)( ((int)(x) * 1000 + font->HUPM) / font->unitsPerEm )
    -#define topost2(x) (int)( ((int)(x) * 1000 + font.HUPM) / font.unitsPerEm )
    -
    -/* Composite glyph values. */
    -#define ARG_1_AND_2_ARE_WORDS 1
    -#define ARGS_ARE_XY_VALUES 2
    -#define ROUND_XY_TO_GRID 4
    -#define WE_HAVE_A_SCALE 8
    -/* RESERVED 16 */
    -#define MORE_COMPONENTS 32
    -#define WE_HAVE_AN_X_AND_Y_SCALE 64
    -#define WE_HAVE_A_TWO_BY_TWO 128
    -#define WE_HAVE_INSTRUCTIONS 256
    -#define USE_MY_METRICS 512
    -
    -/* end of file */
    diff --git a/extern/ttconv/ttutil.cpp b/extern/ttconv/ttutil.cpp
    deleted file mode 100644
    index 13aa170e0f2f..000000000000
    --- a/extern/ttconv/ttutil.cpp
    +++ /dev/null
    @@ -1,83 +0,0 @@
    -/* -*- mode: c++; c-basic-offset: 4 -*- */
    -
    -/*
    - * Modified for use within matplotlib
    - * 5 July 2007
    - * Michael Droettboom
    - */
    -
    -/* Very simple interface to the ppr TT routines */
    -/* (c) Frank Siegert 1996 */
    -
    -#include "global_defines.h"
    -#include 
    -#include 
    -#include 
    -#include "pprdrv.h"
    -
    -#if DEBUG_TRUETYPE
    -void debug(const char *format, ... )
    -{
    -  va_list arg_list;
    -  va_start(arg_list, format);
    -
    -  printf(format, arg_list);
    -
    -  va_end(arg_list);
    -}
    -#endif
    -
    -#define PRINTF_BUFFER_SIZE 512
    -void TTStreamWriter::printf(const char* format, ...)
    -{
    -  va_list arg_list;
    -  va_start(arg_list, format);
    -  char buffer[PRINTF_BUFFER_SIZE];
    -
    -#if defined(WIN32) || defined(_MSC_VER)
    -  int size = _vsnprintf(buffer, PRINTF_BUFFER_SIZE, format, arg_list);
    -#else
    -  int size = vsnprintf(buffer, PRINTF_BUFFER_SIZE, format, arg_list);
    -#endif
    -  if (size >= PRINTF_BUFFER_SIZE) {
    -    char* buffer2 = (char*)malloc(size);
    -#if defined(WIN32) || defined(_MSC_VER)
    -    _vsnprintf(buffer2, size, format, arg_list);
    -#else
    -    vsnprintf(buffer2, size, format, arg_list);
    -#endif
    -    free(buffer2);
    -  } else {
    -    this->write(buffer);
    -  }
    -
    -  va_end(arg_list);
    -}
    -
    -void TTStreamWriter::put_char(int val)
    -{
    -  char c[2];
    -  c[0] = (char)val;
    -  c[1] = 0;
    -  this->write(c);
    -}
    -
    -void TTStreamWriter::puts(const char *a)
    -{
    -  this->write(a);
    -}
    -
    -void TTStreamWriter::putline(const char *a)
    -{
    -  this->write(a);
    -  this->write("\n");
    -}
    -
    -void replace_newlines_with_spaces(char *a) {
    -  char* i = a;
    -  while (*i != 0) {
    -    if (*i == '\r' || *i == '\n')
    -      *i = ' ';
    -    i++;
    -  }
    -}
    diff --git a/galleries/examples/README.txt b/galleries/examples/README.txt
    new file mode 100644
    index 000000000000..363494ac7e6b
    --- /dev/null
    +++ b/galleries/examples/README.txt
    @@ -0,0 +1,21 @@
    +
    +.. _examples-index:
    +
    +.. _gallery:
    +
    +========
    +Examples
    +========
    +For an overview of the plotting methods we provide, see :ref:`plot_types`
    +
    +This page contains example plots. Click on any image to see the full image
    +and source code.
    +
    +For longer tutorials, see our :ref:`tutorials page `.
    +You can also find :ref:`external resources ` and
    +a :ref:`FAQ ` in our :ref:`user guide `.
    +
    +
    +.. admonition:: Tagging!
    +
    +    You can also browse the example gallery by :ref:`tags `.
    diff --git a/galleries/examples/animation/README.txt b/galleries/examples/animation/README.txt
    new file mode 100644
    index 000000000000..c2177be47df0
    --- /dev/null
    +++ b/galleries/examples/animation/README.txt
    @@ -0,0 +1,6 @@
    +.. _animation_examples:
    +
    +.. _animation-examples-index:
    +
    +Animation
    +=========
    diff --git a/galleries/examples/animation/animate_decay.py b/galleries/examples/animation/animate_decay.py
    new file mode 100644
    index 000000000000..2238af6558c2
    --- /dev/null
    +++ b/galleries/examples/animation/animate_decay.py
    @@ -0,0 +1,59 @@
    +"""
    +=====
    +Decay
    +=====
    +
    +This example showcases:
    +
    +- using a generator to drive an animation,
    +- changing axes limits during an animation.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import itertools
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +
    +def data_gen():
    +    for cnt in itertools.count():
    +        t = cnt / 10
    +        yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)
    +
    +
    +def init():
    +    ax.set_ylim(-1.1, 1.1)
    +    ax.set_xlim(0, 1)
    +    del xdata[:]
    +    del ydata[:]
    +    line.set_data(xdata, ydata)
    +    return line,
    +
    +fig, ax = plt.subplots()
    +line, = ax.plot([], [], lw=2)
    +ax.grid()
    +xdata, ydata = [], []
    +
    +
    +def run(data):
    +    # update the data
    +    t, y = data
    +    xdata.append(t)
    +    ydata.append(y)
    +    xmin, xmax = ax.get_xlim()
    +
    +    if t >= xmax:
    +        ax.set_xlim(xmin, 2*xmax)
    +        ax.figure.canvas.draw()
    +    line.set_data(xdata, ydata)
    +
    +    return line,
    +
    +# Only save last 100 frames, but run forever
    +ani = animation.FuncAnimation(fig, run, data_gen, interval=100, init_func=init,
    +                              save_count=100)
    +plt.show()
    diff --git a/galleries/examples/animation/animated_histogram.py b/galleries/examples/animation/animated_histogram.py
    new file mode 100644
    index 000000000000..ad9f871f5cbc
    --- /dev/null
    +++ b/galleries/examples/animation/animated_histogram.py
    @@ -0,0 +1,59 @@
    +"""
    +==================
    +Animated histogram
    +==================
    +
    +Use histogram's `.BarContainer` to draw a bunch of rectangles for an animated
    +histogram.
    +"""
    +
    +import functools
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +# Setting up a random number generator with a fixed state for reproducibility.
    +rng = np.random.default_rng(seed=19680801)
    +# Fixing bin edges.
    +HIST_BINS = np.linspace(-4, 4, 100)
    +
    +# Histogram our data with numpy.
    +data = rng.standard_normal(1000)
    +n, _ = np.histogram(data, HIST_BINS)
    +
    +# %%
    +# To animate the histogram, we need an ``animate`` function, which generates
    +# a random set of numbers and updates the heights of rectangles. The ``animate``
    +# function updates the `.Rectangle` patches on an instance of `.BarContainer`.
    +
    +
    +def animate(frame_number, bar_container):
    +    # Simulate new data coming in.
    +    data = rng.standard_normal(1000)
    +    n, _ = np.histogram(data, HIST_BINS)
    +    for count, rect in zip(n, bar_container.patches):
    +        rect.set_height(count)
    +
    +    return bar_container.patches
    +
    +# %%
    +# Using :func:`~matplotlib.pyplot.hist` allows us to get an instance of
    +# `.BarContainer`, which is a collection of `.Rectangle` instances.  Since
    +# `.FuncAnimation` will only pass the frame number parameter to the animation
    +# function, we use `functools.partial` to fix the ``bar_container`` parameter.
    +
    +# Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +
    +fig, ax = plt.subplots()
    +_, _, bar_container = ax.hist(data, HIST_BINS, lw=1,
    +                              ec="yellow", fc="green", alpha=0.5)
    +ax.set_ylim(top=55)  # set safe limit to ensure that all data is visible.
    +
    +anim = functools.partial(animate, bar_container=bar_container)
    +ani = animation.FuncAnimation(fig, anim, 50, repeat=False, blit=True)
    +plt.show()
    +
    +# %%
    +# .. tags:: plot-type: histogram, component: animation
    diff --git a/galleries/examples/animation/animation_demo.py b/galleries/examples/animation/animation_demo.py
    new file mode 100644
    index 000000000000..d2de7c43e7b5
    --- /dev/null
    +++ b/galleries/examples/animation/animation_demo.py
    @@ -0,0 +1,30 @@
    +"""
    +================
    +pyplot animation
    +================
    +
    +Generating an animation by calling `~.pyplot.pause` between plotting commands.
    +
    +The method shown here is only suitable for simple, low-performance use.  For
    +more demanding applications, look at the :mod:`.animation` module and the
    +examples that use it.
    +
    +Note that calling `time.sleep` instead of `~.pyplot.pause` would *not* work.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +data = np.random.random((50, 50, 50))
    +
    +fig, ax = plt.subplots()
    +
    +for i, img in enumerate(data):
    +    ax.clear()
    +    ax.imshow(img)
    +    ax.set_title(f"frame {i}")
    +    # Note that using time.sleep does *not* work here!
    +    plt.pause(0.1)
    diff --git a/galleries/examples/animation/bayes_update.py b/galleries/examples/animation/bayes_update.py
    new file mode 100644
    index 000000000000..b2ea27cee275
    --- /dev/null
    +++ b/galleries/examples/animation/bayes_update.py
    @@ -0,0 +1,74 @@
    +"""
    +================
    +The Bayes update
    +================
    +
    +This animation displays the posterior estimate updates as it is refitted when
    +new data arrives.
    +The vertical line represents the theoretical value to which the plotted
    +distribution should converge.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import math
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.animation import FuncAnimation
    +
    +
    +def beta_pdf(x, a, b):
    +    return (x**(a-1) * (1-x)**(b-1) * math.gamma(a + b)
    +            / (math.gamma(a) * math.gamma(b)))
    +
    +
    +class UpdateDist:
    +    def __init__(self, ax, prob=0.5):
    +        self.success = 0
    +        self.prob = prob
    +        self.line, = ax.plot([], [], 'k-')
    +        self.x = np.linspace(0, 1, 200)
    +        self.ax = ax
    +
    +        # Set up plot parameters
    +        self.ax.set_xlim(0, 1)
    +        self.ax.set_ylim(0, 10)
    +        self.ax.grid(True)
    +
    +        # This vertical line represents the theoretical value, to
    +        # which the plotted distribution should converge.
    +        self.ax.axvline(prob, linestyle='--', color='black')
    +
    +    def start(self):
    +        # Used for the *init_func* parameter of FuncAnimation; this is called when
    +        # initializing the animation, and also after resizing the figure.
    +        return self.line,
    +
    +    def __call__(self, i):
    +        # This way the plot can continuously run and we just keep
    +        # watching new realizations of the process
    +        if i == 0:
    +            self.success = 0
    +            self.line.set_data([], [])
    +            return self.line,
    +
    +        # Choose success based on exceed a threshold with a uniform pick
    +        if np.random.rand() < self.prob:
    +            self.success += 1
    +        y = beta_pdf(self.x, self.success + 1, (i - self.success) + 1)
    +        self.line.set_data(self.x, y)
    +        return self.line,
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig, ax = plt.subplots()
    +ud = UpdateDist(ax, prob=0.7)
    +anim = FuncAnimation(fig, ud, init_func=ud.start, frames=100, interval=100, blit=True)
    +plt.show()
    +
    +# %%
    +# .. tags:: component: animation, plot-type: line
    diff --git a/galleries/examples/animation/double_pendulum.py b/galleries/examples/animation/double_pendulum.py
    new file mode 100644
    index 000000000000..7a42a6d989ba
    --- /dev/null
    +++ b/galleries/examples/animation/double_pendulum.py
    @@ -0,0 +1,110 @@
    +"""
    +===========================
    +The double pendulum problem
    +===========================
    +
    +This animation illustrates the double pendulum problem.
    +
    +Double pendulum formula translated from the C code at
    +http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +from numpy import cos, sin
    +
    +import matplotlib.animation as animation
    +
    +G = 9.8  # acceleration due to gravity, in m/s^2
    +L1 = 1.0  # length of pendulum 1 in m
    +L2 = 1.0  # length of pendulum 2 in m
    +L = L1 + L2  # maximal length of the combined pendulum
    +M1 = 1.0  # mass of pendulum 1 in kg
    +M2 = 1.0  # mass of pendulum 2 in kg
    +t_stop = 2.5  # how many seconds to simulate
    +history_len = 500  # how many trajectory points to display
    +
    +
    +def derivs(t, state):
    +    dydx = np.zeros_like(state)
    +
    +    dydx[0] = state[1]
    +
    +    delta = state[2] - state[0]
    +    den1 = (M1+M2) * L1 - M2 * L1 * cos(delta) * cos(delta)
    +    dydx[1] = ((M2 * L1 * state[1] * state[1] * sin(delta) * cos(delta)
    +                + M2 * G * sin(state[2]) * cos(delta)
    +                + M2 * L2 * state[3] * state[3] * sin(delta)
    +                - (M1+M2) * G * sin(state[0]))
    +               / den1)
    +
    +    dydx[2] = state[3]
    +
    +    den2 = (L2/L1) * den1
    +    dydx[3] = ((- M2 * L2 * state[3] * state[3] * sin(delta) * cos(delta)
    +                + (M1+M2) * G * sin(state[0]) * cos(delta)
    +                - (M1+M2) * L1 * state[1] * state[1] * sin(delta)
    +                - (M1+M2) * G * sin(state[2]))
    +               / den2)
    +
    +    return dydx
    +
    +# create a time array from 0..t_stop sampled at 0.02 second steps
    +dt = 0.01
    +t = np.arange(0, t_stop, dt)
    +
    +# th1 and th2 are the initial angles (degrees)
    +# w10 and w20 are the initial angular velocities (degrees per second)
    +th1 = 120.0
    +w1 = 0.0
    +th2 = -10.0
    +w2 = 0.0
    +
    +# initial state
    +state = np.radians([th1, w1, th2, w2])
    +
    +# integrate the ODE using Euler's method
    +y = np.empty((len(t), 4))
    +y[0] = state
    +for i in range(1, len(t)):
    +    y[i] = y[i - 1] + derivs(t[i - 1], y[i - 1]) * dt
    +
    +# A more accurate estimate could be obtained e.g. using scipy:
    +#
    +#   y = scipy.integrate.solve_ivp(derivs, t[[0, -1]], state, t_eval=t).y.T
    +
    +x1 = L1*sin(y[:, 0])
    +y1 = -L1*cos(y[:, 0])
    +
    +x2 = L2*sin(y[:, 2]) + x1
    +y2 = -L2*cos(y[:, 2]) + y1
    +
    +fig = plt.figure(figsize=(5, 4))
    +ax = fig.add_subplot(autoscale_on=False, xlim=(-L, L), ylim=(-L, 1.))
    +ax.set_aspect('equal')
    +ax.grid()
    +
    +line, = ax.plot([], [], 'o-', lw=2)
    +trace, = ax.plot([], [], '.-', lw=1, ms=2)
    +time_template = 'time = %.1fs'
    +time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
    +
    +
    +def animate(i):
    +    thisx = [0, x1[i], x2[i]]
    +    thisy = [0, y1[i], y2[i]]
    +
    +    history_x = x2[:i]
    +    history_y = y2[:i]
    +
    +    line.set_data(thisx, thisy)
    +    trace.set_data(history_x, history_y)
    +    time_text.set_text(time_template % (i*dt))
    +    return line, trace, time_text
    +
    +
    +ani = animation.FuncAnimation(
    +    fig, animate, len(y), interval=dt*1000, blit=True)
    +plt.show()
    diff --git a/galleries/examples/animation/dynamic_image.py b/galleries/examples/animation/dynamic_image.py
    new file mode 100644
    index 000000000000..908a7517865d
    --- /dev/null
    +++ b/galleries/examples/animation/dynamic_image.py
    @@ -0,0 +1,51 @@
    +"""
    +=================================================
    +Animated image using a precomputed list of images
    +=================================================
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +fig, ax = plt.subplots()
    +
    +
    +def f(x, y):
    +    return np.sin(x) + np.cos(y)
    +
    +x = np.linspace(0, 2 * np.pi, 120)
    +y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
    +
    +# ims is a list of lists, each row is a list of artists to draw in the
    +# current frame; here we are just animating one artist, the image, in
    +# each frame
    +ims = []
    +for i in range(60):
    +    x += np.pi / 15
    +    y += np.pi / 30
    +    im = ax.imshow(f(x, y), animated=True)
    +    if i == 0:
    +        ax.imshow(f(x, y))  # show an initial one first
    +    ims.append([im])
    +
    +ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
    +                                repeat_delay=1000)
    +
    +# To save the animation, use e.g.
    +#
    +# ani.save("movie.mp4")
    +#
    +# or
    +#
    +# writer = animation.FFMpegWriter(
    +#     fps=15, metadata=dict(artist='Me'), bitrate=1800)
    +# ani.save("movie.mp4", writer=writer)
    +
    +plt.show()
    +
    +# %%
    +# .. tags:: component: animation
    diff --git a/galleries/examples/animation/frame_grabbing_sgskip.py b/galleries/examples/animation/frame_grabbing_sgskip.py
    new file mode 100644
    index 000000000000..dcc2ca01afd9
    --- /dev/null
    +++ b/galleries/examples/animation/frame_grabbing_sgskip.py
    @@ -0,0 +1,41 @@
    +"""
    +==============
    +Frame grabbing
    +==============
    +
    +Use a MovieWriter directly to grab individual frames and write them to a
    +file.  This avoids any event loop integration, and thus works even with the Agg
    +backend.  This is not recommended for use in an interactive setting.
    +"""
    +
    +import numpy as np
    +
    +import matplotlib
    +
    +matplotlib.use("Agg")
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.animation import FFMpegWriter
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +metadata = dict(title='Movie Test', artist='Matplotlib',
    +                comment='Movie support!')
    +writer = FFMpegWriter(fps=15, metadata=metadata)
    +
    +fig = plt.figure()
    +l, = plt.plot([], [], 'k-o')
    +
    +plt.xlim(-5, 5)
    +plt.ylim(-5, 5)
    +
    +x0, y0 = 0, 0
    +
    +with writer.saving(fig, "writer_test.mp4", 100):
    +    for i in range(100):
    +        x0 += 0.1 * np.random.randn()
    +        y0 += 0.1 * np.random.randn()
    +        l.set_data([x0], [y0])
    +        writer.grab_frame()
    diff --git a/galleries/examples/animation/multiple_axes.py b/galleries/examples/animation/multiple_axes.py
    new file mode 100644
    index 000000000000..ce6b811b9e3e
    --- /dev/null
    +++ b/galleries/examples/animation/multiple_axes.py
    @@ -0,0 +1,84 @@
    +"""
    +=======================
    +Multiple Axes animation
    +=======================
    +
    +This example showcases:
    +
    +- how animation across multiple subplots works,
    +- using a figure artist in the animation.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +from matplotlib.patches import ConnectionPatch
    +
    +fig, (axl, axr) = plt.subplots(
    +    ncols=2,
    +    sharey=True,
    +    figsize=(6, 2),
    +    gridspec_kw=dict(width_ratios=[1, 3], wspace=0),
    +)
    +axl.set_aspect(1)
    +axr.set_box_aspect(1 / 3)
    +axr.yaxis.set_visible(False)
    +axr.xaxis.set_ticks([0, np.pi, 2 * np.pi], ["0", r"$\pi$", r"$2\pi$"])
    +
    +# draw circle with initial point in left Axes
    +x = np.linspace(0, 2 * np.pi, 50)
    +axl.plot(np.cos(x), np.sin(x), "k", lw=0.3)
    +point, = axl.plot(0, 0, "o")
    +
    +# draw full curve to set view limits in right Axes
    +sine, = axr.plot(x, np.sin(x))
    +
    +# draw connecting line between both graphs
    +con = ConnectionPatch(
    +    (1, 0),
    +    (0, 0),
    +    "data",
    +    "data",
    +    axesA=axl,
    +    axesB=axr,
    +    color="C0",
    +    ls="dotted",
    +)
    +fig.add_artist(con)
    +
    +
    +def animate(i):
    +    x = np.linspace(0, i, int(i * 25 / np.pi))
    +    sine.set_data(x, np.sin(x))
    +    x, y = np.cos(i), np.sin(i)
    +    point.set_data([x], [y])
    +    con.xy1 = x, y
    +    con.xy2 = i, y
    +    return point, sine, con
    +
    +
    +ani = animation.FuncAnimation(
    +    fig,
    +    animate,
    +    interval=50,
    +    blit=False,  # blitting can't be used with Figure artists
    +    frames=x,
    +    repeat_delay=100,
    +)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches.ConnectionPatch`
    +#    - `matplotlib.animation.FuncAnimation`
    +#
    +# .. tags:: component: axes, component: animation
    diff --git a/galleries/examples/animation/pause_resume.py b/galleries/examples/animation/pause_resume.py
    new file mode 100644
    index 000000000000..e62dd049e11f
    --- /dev/null
    +++ b/galleries/examples/animation/pause_resume.py
    @@ -0,0 +1,59 @@
    +"""
    +=============================
    +Pause and resume an animation
    +=============================
    +
    +This example showcases:
    +
    +- using the Animation.pause() method to pause an animation.
    +- using the Animation.resume() method to resume an animation.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +
    +class PauseAnimation:
    +    def __init__(self):
    +        fig, ax = plt.subplots()
    +        ax.set_title('Click to pause/resume the animation')
    +        x = np.linspace(-0.1, 0.1, 1000)
    +
    +        # Start with a normal distribution
    +        self.n0 = (1.0 / ((4 * np.pi * 2e-4 * 0.1) ** 0.5)
    +                   * np.exp(-x ** 2 / (4 * 2e-4 * 0.1)))
    +        self.p, = ax.plot(x, self.n0)
    +
    +        self.animation = animation.FuncAnimation(
    +            fig, self.update, frames=200, interval=50, blit=True)
    +        self.paused = False
    +
    +        fig.canvas.mpl_connect('button_press_event', self.toggle_pause)
    +
    +    def toggle_pause(self, *args, **kwargs):
    +        if self.paused:
    +            self.animation.resume()
    +        else:
    +            self.animation.pause()
    +        self.paused = not self.paused
    +
    +    def update(self, i):
    +        self.n0 += i / 100 % 5
    +        self.p.set_ydata(self.n0 % 20)
    +        return (self.p,)
    +
    +
    +pa = PauseAnimation()
    +plt.show()
    diff --git a/examples/animation/rain.py b/galleries/examples/animation/rain.py
    similarity index 75%
    rename from examples/animation/rain.py
    rename to galleries/examples/animation/rain.py
    index db7af8ec8798..a87eace6fe07 100644
    --- a/examples/animation/rain.py
    +++ b/galleries/examples/animation/rain.py
    @@ -7,24 +7,31 @@
     of 50 scatter points.
     
     Author: Nicolas P. Rougier
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
     """
    -import numpy as np
    +
     import matplotlib.pyplot as plt
    +import numpy as np
    +
     from matplotlib.animation import FuncAnimation
     
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
     
     # Create new Figure and an Axes which fills it.
     fig = plt.figure(figsize=(7, 7))
    -ax = fig.add_axes([0, 0, 1, 1], frameon=False)
    +ax = fig.add_axes((0, 0, 1, 1), frameon=False)
     ax.set_xlim(0, 1), ax.set_xticks([])
     ax.set_ylim(0, 1), ax.set_yticks([])
     
     # Create rain data
     n_drops = 50
    -rain_drops = np.zeros(n_drops, dtype=[('position', float, 2),
    -                                      ('size',     float, 1),
    -                                      ('growth',   float, 1),
    -                                      ('color',    float, 4)])
    +rain_drops = np.zeros(n_drops, dtype=[('position', float, (2,)),
    +                                      ('size',     float),
    +                                      ('growth',   float),
    +                                      ('color',    float, (4,))])
     
     # Initialize the raindrops in random positions and with
     # random growth rates.
    @@ -60,9 +67,16 @@ def update(frame_number):
         scat.set_edgecolors(rain_drops['color'])
         scat.set_sizes(rain_drops['size'])
         scat.set_offsets(rain_drops['position'])
    +    return [scat]
     
     
    -# Construct the animation, using the update function as the animation
    -# director.
    -animation = FuncAnimation(fig, update, interval=10)
    +# Construct the animation, using the update function as the animation director.
    +animation = FuncAnimation(fig, update, interval=10, save_count=100, blit=True)
     plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: animation
    +#    plot-type: scatter
    +#    purpose: fun
    diff --git a/galleries/examples/animation/random_walk.py b/galleries/examples/animation/random_walk.py
    new file mode 100644
    index 000000000000..e1b3481d0378
    --- /dev/null
    +++ b/galleries/examples/animation/random_walk.py
    @@ -0,0 +1,55 @@
    +"""
    +=======================
    +Animated 3D random walk
    +=======================
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def random_walk(num_steps, max_step=0.05):
    +    """Return a 3D random walk as (num_steps, 3) array."""
    +    start_pos = np.random.random(3)
    +    steps = np.random.uniform(-max_step, max_step, size=(num_steps, 3))
    +    walk = start_pos + np.cumsum(steps, axis=0)
    +    return walk
    +
    +
    +def update_lines(num, walks, lines):
    +    for line, walk in zip(lines, walks):
    +        line.set_data_3d(walk[:num, :].T)
    +    return lines
    +
    +
    +# Data: 40 random walks as (num_steps, 3) arrays
    +num_steps = 30
    +walks = [random_walk(num_steps) for index in range(40)]
    +
    +# Attaching 3D axis to the figure
    +fig = plt.figure()
    +ax = fig.add_subplot(projection="3d")
    +
    +# Create lines initially without data
    +lines = [ax.plot([], [], [])[0] for _ in walks]
    +
    +# Setting the Axes properties
    +ax.set(xlim3d=(0, 1), xlabel='X')
    +ax.set(ylim3d=(0, 1), ylabel='Y')
    +ax.set(zlim3d=(0, 1), zlabel='Z')
    +
    +# Creating the Animation object
    +ani = animation.FuncAnimation(
    +    fig, update_lines, num_steps, fargs=(walks, lines), interval=100)
    +
    +plt.show()
    +
    +# %%
    +# .. tags:: component: animation, plot-type: 3D
    diff --git a/galleries/examples/animation/simple_anim.py b/galleries/examples/animation/simple_anim.py
    new file mode 100644
    index 000000000000..5391ca5b7aed
    --- /dev/null
    +++ b/galleries/examples/animation/simple_anim.py
    @@ -0,0 +1,38 @@
    +"""
    +==================
    +Animated line plot
    +==================
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +fig, ax = plt.subplots()
    +
    +x = np.arange(0, 2*np.pi, 0.01)
    +line, = ax.plot(x, np.sin(x))
    +
    +
    +def animate(i):
    +    line.set_ydata(np.sin(x + i / 50))  # update the data.
    +    return line,
    +
    +
    +ani = animation.FuncAnimation(
    +    fig, animate, interval=20, blit=True, save_count=50)
    +
    +# To save the animation, use e.g.
    +#
    +# ani.save("movie.mp4")
    +#
    +# or
    +#
    +# writer = animation.FFMpegWriter(
    +#     fps=15, metadata=dict(artist='Me'), bitrate=1800)
    +# ani.save("movie.mp4", writer=writer)
    +
    +plt.show()
    diff --git a/galleries/examples/animation/simple_scatter.py b/galleries/examples/animation/simple_scatter.py
    new file mode 100644
    index 000000000000..5afef75f6074
    --- /dev/null
    +++ b/galleries/examples/animation/simple_scatter.py
    @@ -0,0 +1,41 @@
    +"""
    +=============================
    +Animated scatter saved as GIF
    +=============================
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +
    +fig, ax = plt.subplots()
    +ax.set_xlim(0, 10)
    +
    +scat = ax.scatter(1, 0)
    +x = np.linspace(0, 10)
    +
    +
    +def animate(i):
    +    scat.set_offsets((x[i], 0))
    +    return (scat,)
    +
    +
    +ani = animation.FuncAnimation(fig, animate, repeat=True, frames=len(x) - 1, interval=50)
    +
    +# To save the animation using Pillow as a gif
    +# writer = animation.PillowWriter(fps=15,
    +#                                 metadata=dict(artist='Me'),
    +#                                 bitrate=1800)
    +# ani.save('scatter.gif', writer=writer)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags::
    +#    component: animation,
    +#    plot-type: scatter,
    +#    purpose: reference,
    +#    level: intermediate
    diff --git a/galleries/examples/animation/strip_chart.py b/galleries/examples/animation/strip_chart.py
    new file mode 100644
    index 000000000000..0e533a255f1c
    --- /dev/null
    +++ b/galleries/examples/animation/strip_chart.py
    @@ -0,0 +1,71 @@
    +"""
    +============
    +Oscilloscope
    +============
    +
    +Emulates an oscilloscope.
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.animation as animation
    +from matplotlib.lines import Line2D
    +
    +
    +class Scope:
    +    def __init__(self, ax, maxt=2, dt=0.02):
    +        self.ax = ax
    +        self.dt = dt
    +        self.maxt = maxt
    +        self.tdata = [0]
    +        self.ydata = [0]
    +        self.line = Line2D(self.tdata, self.ydata)
    +        self.ax.add_line(self.line)
    +        self.ax.set_ylim(-.1, 1.1)
    +        self.ax.set_xlim(0, self.maxt)
    +
    +    def update(self, y):
    +        lastt = self.tdata[-1]
    +        if lastt >= self.tdata[0] + self.maxt:  # reset the arrays
    +            self.tdata = [self.tdata[-1]]
    +            self.ydata = [self.ydata[-1]]
    +            self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt)
    +            self.ax.figure.canvas.draw()
    +
    +        # This slightly more complex calculation avoids floating-point issues
    +        # from just repeatedly adding `self.dt` to the previous value.
    +        t = self.tdata[0] + len(self.tdata) * self.dt
    +
    +        self.tdata.append(t)
    +        self.ydata.append(y)
    +        self.line.set_data(self.tdata, self.ydata)
    +        return self.line,
    +
    +
    +def emitter(p=0.1):
    +    """Return a random value in [0, 1) with probability p, else 0."""
    +    while True:
    +        v = np.random.rand()
    +        if v > p:
    +            yield 0.
    +        else:
    +            yield np.random.rand()
    +
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801 // 10)
    +
    +
    +fig, ax = plt.subplots()
    +scope = Scope(ax)
    +
    +# pass a generator in "emitter" to produce data for the update func
    +ani = animation.FuncAnimation(fig, scope.update, emitter, interval=50,
    +                              blit=True, save_count=100)
    +
    +plt.show()
    +
    +# ..tags:: animation, plot-type: line
    diff --git a/examples/animation/unchained.py b/galleries/examples/animation/unchained.py
    similarity index 78%
    rename from examples/animation/unchained.py
    rename to galleries/examples/animation/unchained.py
    index c7af48961362..40764798d425 100644
    --- a/examples/animation/unchained.py
    +++ b/galleries/examples/animation/unchained.py
    @@ -1,27 +1,35 @@
     """
    -========================
    -MATPLOTLIB **UNCHAINED**
    -========================
    +====================
    +Matplotlib unchained
    +====================
     
    -Comparative path demonstration of frequency from a fake signal of a pulsar.
    -(mostly known because of the cover for Joy Division's Unknown Pleasures)
    +Comparative path demonstration of frequency from a fake signal of a pulsar
    +(mostly known because of the cover for Joy Division's Unknown Pleasures).
     
     Author: Nicolas P. Rougier
    +
    +Output generated via `matplotlib.animation.Animation.to_jshtml`.
     """
    -import numpy as np
    +
     import matplotlib.pyplot as plt
    +import numpy as np
    +
     import matplotlib.animation as animation
     
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
     # Create new Figure with black background
     fig = plt.figure(figsize=(8, 8), facecolor='black')
     
     # Add a subplot with no frame
    -ax = plt.subplot(111, frameon=False)
    +ax = plt.subplot(frameon=False)
     
     # Generate random data
     data = np.random.uniform(0, 1, (64, 75))
     X = np.linspace(-1, 1, data.shape[-1])
    -G = 1.5 * np.exp(-4 * X * X)
    +G = 1.5 * np.exp(-4 * X ** 2)
     
     # Generate line plots
     lines = []
    @@ -63,7 +71,13 @@ def update(*args):
         # Return modified artists
         return lines
     
    -# Construct the animation, using the update function as the animation
    -# director.
    -anim = animation.FuncAnimation(fig, update, interval=10)
    +# Construct the animation, using the update function as the animation director.
    +anim = animation.FuncAnimation(fig, update, interval=10, save_count=100)
     plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: animation
    +#    plot-type: line
    +#    purpose: fun
    diff --git a/galleries/examples/axes_grid1/README.txt b/galleries/examples/axes_grid1/README.txt
    new file mode 100644
    index 000000000000..5fdbf13b9e44
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/README.txt
    @@ -0,0 +1,6 @@
    +.. _axes_grid_examples:
    +
    +.. _axes_grid1-examples-index:
    +
    +Module - axes_grid1
    +===================
    diff --git a/galleries/examples/axes_grid1/demo_anchored_direction_arrows.py b/galleries/examples/axes_grid1/demo_anchored_direction_arrows.py
    new file mode 100644
    index 000000000000..f7d8fbc83868
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_anchored_direction_arrows.py
    @@ -0,0 +1,82 @@
    +"""
    +========================
    +Anchored Direction Arrow
    +========================
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.font_manager as fm
    +from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDirectionArrows
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, ax = plt.subplots()
    +ax.imshow(np.random.random((10, 10)))
    +
    +# Simple example
    +simple_arrow = AnchoredDirectionArrows(ax.transAxes, 'X', 'Y')
    +ax.add_artist(simple_arrow)
    +
    +# High contrast arrow
    +high_contrast_part_1 = AnchoredDirectionArrows(
    +                            ax.transAxes,
    +                            '111', r'11$\overline{2}$',
    +                            loc='upper right',
    +                            arrow_props={'ec': 'w', 'fc': 'none', 'alpha': 1,
    +                                         'lw': 2}
    +                            )
    +ax.add_artist(high_contrast_part_1)
    +
    +high_contrast_part_2 = AnchoredDirectionArrows(
    +                            ax.transAxes,
    +                            '111', r'11$\overline{2}$',
    +                            loc='upper right',
    +                            arrow_props={'ec': 'none', 'fc': 'k'},
    +                            text_props={'ec': 'w', 'fc': 'k', 'lw': 0.4}
    +                            )
    +ax.add_artist(high_contrast_part_2)
    +
    +# Rotated arrow
    +fontprops = fm.FontProperties(family='serif')
    +
    +rotated_arrow = AnchoredDirectionArrows(
    +                    ax.transAxes,
    +                    '30', '120',
    +                    loc='center',
    +                    color='w',
    +                    angle=30,
    +                    fontproperties=fontprops
    +                    )
    +ax.add_artist(rotated_arrow)
    +
    +# Altering arrow directions
    +a1 = AnchoredDirectionArrows(
    +        ax.transAxes, 'A', 'B', loc='lower center',
    +        length=-0.15,
    +        sep_x=0.03, sep_y=0.03,
    +        color='r'
    +    )
    +ax.add_artist(a1)
    +
    +a2 = AnchoredDirectionArrows(
    +        ax.transAxes, 'A', ' B', loc='lower left',
    +        aspect_ratio=-1,
    +        sep_x=0.01, sep_y=-0.02,
    +        color='orange'
    +        )
    +ax.add_artist(a2)
    +
    +
    +a3 = AnchoredDirectionArrows(
    +        ax.transAxes, ' A', 'B', loc='lower right',
    +        length=-0.15,
    +        aspect_ratio=-1,
    +        sep_y=-0.1, sep_x=0.04,
    +        color='cyan'
    +        )
    +ax.add_artist(a3)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_axes_divider.py b/galleries/examples/axes_grid1/demo_axes_divider.py
    new file mode 100644
    index 000000000000..42be8aacd8be
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_axes_divider.py
    @@ -0,0 +1,109 @@
    +"""
    +============
    +Axes divider
    +============
    +
    +Axes divider to calculate location of Axes and
    +create a divider for them using existing Axes instances.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib import cbook
    +
    +
    +def get_demo_image():
    +    z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")  # 15x15 array
    +    return z, (-3, 4, -4, 3)
    +
    +
    +def demo_simple_image(ax):
    +    Z, extent = get_demo_image()
    +
    +    im = ax.imshow(Z, extent=extent)
    +    cb = plt.colorbar(im)
    +    cb.ax.yaxis.set_tick_params(labelright=False)
    +
    +
    +def demo_locatable_axes_hard(fig):
    +    from mpl_toolkits.axes_grid1 import Size, SubplotDivider
    +
    +    divider = SubplotDivider(fig, 2, 2, 2, aspect=True)
    +
    +    # Axes for image
    +    ax = fig.add_subplot(axes_locator=divider.new_locator(nx=0, ny=0))
    +    # Axes for colorbar
    +    ax_cb = fig.add_subplot(axes_locator=divider.new_locator(nx=2, ny=0))
    +
    +    divider.set_horizontal([
    +        Size.AxesX(ax),  # main Axes
    +        Size.Fixed(0.05),  # padding, 0.1 inch
    +        Size.Fixed(0.2),  # colorbar, 0.3 inch
    +    ])
    +    divider.set_vertical([Size.AxesY(ax)])
    +
    +    Z, extent = get_demo_image()
    +
    +    im = ax.imshow(Z, extent=extent)
    +    plt.colorbar(im, cax=ax_cb)
    +    ax_cb.yaxis.set_tick_params(labelright=False)
    +
    +
    +def demo_locatable_axes_easy(ax):
    +    from mpl_toolkits.axes_grid1 import make_axes_locatable
    +
    +    divider = make_axes_locatable(ax)
    +
    +    ax_cb = divider.append_axes("right", size="5%", pad=0.05)
    +    fig = ax.get_figure()
    +    fig.add_axes(ax_cb)
    +
    +    Z, extent = get_demo_image()
    +    im = ax.imshow(Z, extent=extent)
    +
    +    plt.colorbar(im, cax=ax_cb)
    +    ax_cb.yaxis.tick_right()
    +    ax_cb.yaxis.set_tick_params(labelright=False)
    +
    +
    +def demo_images_side_by_side(ax):
    +    from mpl_toolkits.axes_grid1 import make_axes_locatable
    +
    +    divider = make_axes_locatable(ax)
    +
    +    Z, extent = get_demo_image()
    +    ax2 = divider.append_axes("right", size="100%", pad=0.05)
    +    fig1 = ax.get_figure()
    +    fig1.add_axes(ax2)
    +
    +    ax.imshow(Z, extent=extent)
    +    ax2.imshow(Z, extent=extent)
    +    ax2.yaxis.set_tick_params(labelleft=False)
    +
    +
    +def demo():
    +    fig = plt.figure(figsize=(6, 6))
    +
    +    # PLOT 1
    +    # simple image & colorbar
    +    ax = fig.add_subplot(2, 2, 1)
    +    demo_simple_image(ax)
    +
    +    # PLOT 2
    +    # image and colorbar with draw-time positioning -- a hard way
    +    demo_locatable_axes_hard(fig)
    +
    +    # PLOT 3
    +    # image and colorbar with draw-time positioning -- an easy way
    +    ax = fig.add_subplot(2, 2, 3)
    +    demo_locatable_axes_easy(ax)
    +
    +    # PLOT 4
    +    # two images side by side with fixed padding.
    +    ax = fig.add_subplot(2, 2, 4)
    +    demo_images_side_by_side(ax)
    +
    +    plt.show()
    +
    +
    +demo()
    diff --git a/galleries/examples/axes_grid1/demo_axes_grid.py b/galleries/examples/axes_grid1/demo_axes_grid.py
    new file mode 100644
    index 000000000000..d29ca6a05859
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_axes_grid.py
    @@ -0,0 +1,72 @@
    +"""
    +==============
    +Demo Axes Grid
    +==============
    +
    +Grid of 2x2 images with a single colorbar or with one colorbar per Axes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1 import ImageGrid
    +
    +fig = plt.figure(figsize=(10.5, 2.5))
    +Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")  # 15x15 array
    +extent = (-3, 4, -4, 3)
    +
    +
    +# A grid of 2x2 images with 0.05 inch pad between images and only the
    +# lower-left Axes is labeled.
    +grid = ImageGrid(
    +    fig, 141,  # similar to fig.add_subplot(141).
    +     nrows_ncols=(2, 2), axes_pad=0.05, label_mode="1")
    +for ax in grid:
    +    ax.imshow(Z, extent=extent)
    +# This only affects Axes in first column and second row as share_all=False.
    +grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2])
    +
    +
    +# A grid of 2x2 images with a single colorbar.
    +grid = ImageGrid(
    +    fig, 142,  # similar to fig.add_subplot(142).
    +    nrows_ncols=(2, 2), axes_pad=0.0, label_mode="L", share_all=True,
    +    cbar_location="top", cbar_mode="single")
    +for ax in grid:
    +    im = ax.imshow(Z, extent=extent)
    +grid.cbar_axes[0].colorbar(im)
    +for cax in grid.cbar_axes:
    +    cax.tick_params(labeltop=False)
    +# This affects all Axes as share_all = True.
    +grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2])
    +
    +
    +# A grid of 2x2 images. Each image has its own colorbar.
    +grid = ImageGrid(
    +    fig, 143,  # similar to fig.add_subplot(143).
    +    nrows_ncols=(2, 2), axes_pad=0.1, label_mode="1", share_all=True,
    +    cbar_location="top", cbar_mode="each", cbar_size="7%", cbar_pad="2%")
    +for ax, cax in zip(grid, grid.cbar_axes):
    +    im = ax.imshow(Z, extent=extent)
    +    cax.colorbar(im)
    +    cax.tick_params(labeltop=False)
    +# This affects all Axes as share_all = True.
    +grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2])
    +
    +
    +# A grid of 2x2 images. Each image has its own colorbar.
    +grid = ImageGrid(
    +    fig, 144,  # similar to fig.add_subplot(144).
    +    nrows_ncols=(2, 2), axes_pad=(0.45, 0.15), label_mode="1", share_all=True,
    +    cbar_location="right", cbar_mode="each", cbar_size="7%", cbar_pad="2%")
    +# Use a different colorbar range every time
    +limits = ((0, 1), (-2, 2), (-1.7, 1.4), (-1.5, 1))
    +for ax, cax, vlim in zip(grid, grid.cbar_axes, limits):
    +    im = ax.imshow(Z, extent=extent, vmin=vlim[0], vmax=vlim[1])
    +    cb = cax.colorbar(im)
    +    cb.set_ticks((vlim[0], vlim[1]))
    +# This affects all Axes as share_all = True.
    +grid.axes_llc.set(xticks=[-2, 0, 2], yticks=[-2, 0, 2])
    +
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_axes_grid2.py b/galleries/examples/axes_grid1/demo_axes_grid2.py
    new file mode 100644
    index 000000000000..458e83b6d68f
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_axes_grid2.py
    @@ -0,0 +1,69 @@
    +"""
    +==========
    +Axes Grid2
    +==========
    +
    +Grid of images with shared xaxis and yaxis.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1 import ImageGrid
    +
    +
    +def add_inner_title(ax, title, loc, **kwargs):
    +    from matplotlib.offsetbox import AnchoredText
    +    from matplotlib.patheffects import withStroke
    +    prop = dict(path_effects=[withStroke(foreground='w', linewidth=3)],
    +                size=plt.rcParams['legend.fontsize'])
    +    at = AnchoredText(title, loc=loc, prop=prop,
    +                      pad=0., borderpad=0.5,
    +                      frameon=False, **kwargs)
    +    ax.add_artist(at)
    +    return at
    +
    +
    +fig = plt.figure(figsize=(6, 6))
    +
    +# Prepare images
    +Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")
    +extent = (-3, 4, -4, 3)
    +ZS = [Z[i::3, :] for i in range(3)]
    +extent = extent[0], extent[1]/3., extent[2], extent[3]
    +
    +# *** Demo 1: colorbar at each Axes ***
    +grid = ImageGrid(
    +    # 211 = at the position of fig.add_subplot(211)
    +    fig, 211, nrows_ncols=(1, 3), axes_pad=0.05, label_mode="1", share_all=True,
    +    cbar_location="top", cbar_mode="each", cbar_size="7%", cbar_pad="1%")
    +grid[0].set(xticks=[-2, 0], yticks=[-2, 0, 2])
    +
    +for i, (ax, z) in enumerate(zip(grid, ZS)):
    +    im = ax.imshow(z, origin="lower", extent=extent)
    +    cb = ax.cax.colorbar(im)
    +    # Changing the colorbar ticks
    +    if i in [1, 2]:
    +        cb.set_ticks([-1, 0, 1])
    +
    +for ax, im_title in zip(grid, ["Image 1", "Image 2", "Image 3"]):
    +    add_inner_title(ax, im_title, loc='lower left')
    +
    +# *** Demo 2: shared colorbar ***
    +grid2 = ImageGrid(
    +    fig, 212, nrows_ncols=(1, 3), axes_pad=0.05, label_mode="1", share_all=True,
    +    cbar_location="right", cbar_mode="single", cbar_size="10%", cbar_pad=0.05)
    +grid2[0].set(xlabel="X", ylabel="Y", xticks=[-2, 0], yticks=[-2, 0, 2])
    +
    +clim = (np.min(ZS), np.max(ZS))
    +for ax, z in zip(grid2, ZS):
    +    im = ax.imshow(z, clim=clim, origin="lower", extent=extent)
    +
    +# With cbar_mode="single", cax attribute of all Axes are identical.
    +ax.cax.colorbar(im)
    +
    +for ax, im_title in zip(grid2, ["(a)", "(b)", "(c)"]):
    +    add_inner_title(ax, im_title, loc='upper left')
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_axes_hbox_divider.py b/galleries/examples/axes_grid1/demo_axes_hbox_divider.py
    new file mode 100644
    index 000000000000..5188bdf66d6f
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_axes_hbox_divider.py
    @@ -0,0 +1,54 @@
    +"""
    +================================
    +HBoxDivider and VBoxDivider demo
    +================================
    +
    +Using an `.HBoxDivider` to arrange subplots.
    +
    +Note that both Axes' location are adjusted so that they have
    +equal heights while maintaining their aspect ratios.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axes_grid1.axes_divider import HBoxDivider, VBoxDivider
    +import mpl_toolkits.axes_grid1.axes_size as Size
    +
    +arr1 = np.arange(20).reshape((4, 5))
    +arr2 = np.arange(20).reshape((5, 4))
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +ax1.imshow(arr1)
    +ax2.imshow(arr2)
    +
    +pad = 0.5  # pad in inches
    +divider = HBoxDivider(
    +    fig, 111,
    +    horizontal=[Size.AxesX(ax1), Size.Fixed(pad), Size.AxesX(ax2)],
    +    vertical=[Size.AxesY(ax1), Size.Scaled(1), Size.AxesY(ax2)])
    +ax1.set_axes_locator(divider.new_locator(0))
    +ax2.set_axes_locator(divider.new_locator(2))
    +
    +plt.show()
    +
    +# %%
    +# Using a `.VBoxDivider` to arrange subplots.
    +#
    +# Note that both Axes' location are adjusted so that they have
    +# equal widths while maintaining their aspect ratios.
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1)
    +ax1.imshow(arr1)
    +ax2.imshow(arr2)
    +
    +divider = VBoxDivider(
    +    fig, 111,
    +    horizontal=[Size.AxesX(ax1), Size.Scaled(1), Size.AxesX(ax2)],
    +    vertical=[Size.AxesY(ax1), Size.Fixed(pad), Size.AxesY(ax2)])
    +
    +ax1.set_axes_locator(divider.new_locator(0))
    +ax2.set_axes_locator(divider.new_locator(2))
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_axes_rgb.py b/galleries/examples/axes_grid1/demo_axes_rgb.py
    new file mode 100644
    index 000000000000..2cdb41fc323b
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_axes_rgb.py
    @@ -0,0 +1,70 @@
    +"""
    +===============================
    +Show RGB channels using RGBAxes
    +===============================
    +
    +`~.axes_grid1.axes_rgb.RGBAxes` creates a layout of 4 Axes for displaying RGB
    +channels: one large Axes for the RGB image and 3 smaller Axes for the R, G, B
    +channels.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1.axes_rgb import RGBAxes, make_rgb_axes
    +
    +
    +def get_rgb():
    +    Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")
    +    Z[Z < 0] = 0.
    +    Z = Z / Z.max()
    +
    +    R = Z[:13, :13]
    +    G = Z[2:, 2:]
    +    B = Z[:13, 2:]
    +
    +    return R, G, B
    +
    +
    +def make_cube(r, g, b):
    +    ny, nx = r.shape
    +    R = np.zeros((ny, nx, 3))
    +    R[:, :, 0] = r
    +    G = np.zeros_like(R)
    +    G[:, :, 1] = g
    +    B = np.zeros_like(R)
    +    B[:, :, 2] = b
    +
    +    RGB = R + G + B
    +
    +    return R, G, B, RGB
    +
    +
    +def demo_rgb1():
    +    fig = plt.figure()
    +    ax = RGBAxes(fig, [0.1, 0.1, 0.8, 0.8], pad=0.0)
    +    r, g, b = get_rgb()
    +    ax.imshow_rgb(r, g, b)
    +
    +
    +def demo_rgb2():
    +    fig, ax = plt.subplots()
    +    ax_r, ax_g, ax_b = make_rgb_axes(ax, pad=0.02)
    +
    +    r, g, b = get_rgb()
    +    im_r, im_g, im_b, im_rgb = make_cube(r, g, b)
    +    ax.imshow(im_rgb)
    +    ax_r.imshow(im_r)
    +    ax_g.imshow(im_g)
    +    ax_b.imshow(im_b)
    +
    +    for ax in fig.axes:
    +        ax.tick_params(direction='in', color='w')
    +        ax.spines[:].set_color("w")
    +
    +
    +demo_rgb1()
    +demo_rgb2()
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py b/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py
    new file mode 100644
    index 000000000000..a04fac2c3ebe
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_colorbar_with_axes_divider.py
    @@ -0,0 +1,43 @@
    +"""
    +.. _demo-colorbar-with-axes-divider:
    +
    +=========================
    +Colorbar with AxesDivider
    +=========================
    +
    +The `.axes_divider.make_axes_locatable` function takes an existing Axes, adds
    +it to a new `.AxesDivider` and returns the `.AxesDivider`.  The `.append_axes`
    +method of the `.AxesDivider` can then be used to create a new Axes on a given
    +side ("top", "right", "bottom", or "left") of the original Axes. This example
    +uses `.append_axes` to add colorbars next to Axes.
    +
    +Users should consider simply passing the main Axes to the *ax* keyword argument of
    +`~.Figure.colorbar` instead of creating a locatable Axes manually like this.
    +See :ref:`colorbar_placement`.
    +
    +.. redirect-from:: /gallery/axes_grid1/simple_colorbar
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +fig.subplots_adjust(wspace=0.5)
    +
    +im1 = ax1.imshow([[1, 2], [3, 4]])
    +ax1_divider = make_axes_locatable(ax1)
    +# Add an Axes to the right of the main Axes.
    +cax1 = ax1_divider.append_axes("right", size="7%", pad="2%")
    +cb1 = fig.colorbar(im1, cax=cax1)
    +
    +im2 = ax2.imshow([[1, 2], [3, 4]])
    +ax2_divider = make_axes_locatable(ax2)
    +# Add an Axes above the main Axes.
    +cax2 = ax2_divider.append_axes("top", size="7%", pad="2%")
    +cb2 = fig.colorbar(im2, cax=cax2, orientation="horizontal")
    +# Change tick position to top (with the default tick position "bottom", ticks
    +# overlap the image).
    +cax2.xaxis.set_ticks_position("top")
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py b/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py
    new file mode 100644
    index 000000000000..352c8527910e
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_colorbar_with_inset_locator.py
    @@ -0,0 +1,50 @@
    +"""
    +.. _demo-colorbar-with-inset-locator:
    +
    +=========================================================================
    +Control the position and size of a colorbar with inset_locator.inset_axes
    +=========================================================================
    +
    +This example shows how to control the position, height, and width of colorbars
    +using `.inset_locator.inset_axes`.
    +
    +`.inset_locator.inset_axes` placement is controlled as for legends: either
    +by providing a *loc* option ("upper right", "best", ...), or by providing a
    +locator with respect to the parent bbox.  Parameters such as *bbox_to_anchor*
    +and *borderpad* likewise work in the same way, and are also demonstrated here.
    +
    +Users should consider using `.Axes.inset_axes` instead (see
    +:ref:`colorbar_placement`).
    +
    +.. redirect-from:: /gallery/axes_grid1/demo_colorbar_of_inset_axes
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import inset_locator
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[6, 3])
    +
    +im1 = ax1.imshow([[1, 2], [2, 3]])
    +axins1 = inset_locator.inset_axes(
    +    ax1,
    +    width="50%",  # width: 50% of parent_bbox width
    +    height="5%",  # height: 5%
    +    loc="upper right",
    +)
    +axins1.xaxis.set_ticks_position("bottom")
    +fig.colorbar(im1, cax=axins1, orientation="horizontal", ticks=[1, 2, 3])
    +
    +im = ax2.imshow([[1, 2], [2, 3]])
    +axins = inset_locator.inset_axes(
    +    ax2,
    +    width="5%",  # width: 5% of parent_bbox width
    +    height="50%",  # height: 50%
    +    loc="lower left",
    +    bbox_to_anchor=(1.05, 0., 1, 1),
    +    bbox_transform=ax2.transAxes,
    +    borderpad=0,
    +)
    +fig.colorbar(im, cax=axins, ticks=[1, 2, 3])
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_edge_colorbar.py b/galleries/examples/axes_grid1/demo_edge_colorbar.py
    new file mode 100644
    index 000000000000..bde482977991
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_edge_colorbar.py
    @@ -0,0 +1,86 @@
    +"""
    +===============================
    +Per-row or per-column colorbars
    +===============================
    +
    +This example shows how to use one common colorbar for each row or column
    +of an image grid.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1 import AxesGrid
    +
    +
    +def get_demo_image():
    +    z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")  # 15x15 array
    +    return z, (-3, 4, -4, 3)
    +
    +
    +def demo_bottom_cbar(fig):
    +    """
    +    A grid of 2x2 images with a colorbar for each column.
    +    """
    +    grid = AxesGrid(fig, 121,  # similar to subplot(121)
    +                    nrows_ncols=(2, 2),
    +                    axes_pad=0.10,
    +                    share_all=True,
    +                    label_mode="1",
    +                    cbar_location="bottom",
    +                    cbar_mode="edge",
    +                    cbar_pad=0.25,
    +                    cbar_size="15%",
    +                    direction="column"
    +                    )
    +
    +    Z, extent = get_demo_image()
    +    cmaps = ["autumn", "summer"]
    +    for i in range(4):
    +        im = grid[i].imshow(Z, extent=extent, cmap=cmaps[i//2])
    +        if i % 2:
    +            grid.cbar_axes[i//2].colorbar(im)
    +
    +    for cax in grid.cbar_axes:
    +        cax.axis[cax.orientation].set_label("Bar")
    +
    +    # This affects all Axes as share_all = True.
    +    grid.axes_llc.set_xticks([-2, 0, 2])
    +    grid.axes_llc.set_yticks([-2, 0, 2])
    +
    +
    +def demo_right_cbar(fig):
    +    """
    +    A grid of 2x2 images. Each row has its own colorbar.
    +    """
    +    grid = AxesGrid(fig, 122,  # similar to subplot(122)
    +                    nrows_ncols=(2, 2),
    +                    axes_pad=0.10,
    +                    label_mode="1",
    +                    share_all=True,
    +                    cbar_location="right",
    +                    cbar_mode="edge",
    +                    cbar_size="7%",
    +                    cbar_pad="2%",
    +                    )
    +    Z, extent = get_demo_image()
    +    cmaps = ["spring", "winter"]
    +    for i in range(4):
    +        im = grid[i].imshow(Z, extent=extent, cmap=cmaps[i//2])
    +        if i % 2:
    +            grid.cbar_axes[i//2].colorbar(im)
    +
    +    for cax in grid.cbar_axes:
    +        cax.axis[cax.orientation].set_label('Foo')
    +
    +    # This affects all Axes because we set share_all = True.
    +    grid.axes_llc.set_xticks([-2, 0, 2])
    +    grid.axes_llc.set_yticks([-2, 0, 2])
    +
    +
    +fig = plt.figure()
    +
    +demo_bottom_cbar(fig)
    +demo_right_cbar(fig)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_fixed_size_axes.py b/galleries/examples/axes_grid1/demo_fixed_size_axes.py
    new file mode 100644
    index 000000000000..a8f8685a115c
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_fixed_size_axes.py
    @@ -0,0 +1,50 @@
    +"""
    +===============================
    +Axes with a fixed physical size
    +===============================
    +
    +Note that this can be accomplished with the main library for
    +Axes on Figures that do not change size: :ref:`fixed_size_axes`
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import Divider, Size
    +
    +# %%
    +
    +
    +fig = plt.figure(figsize=(6, 6))
    +
    +# The first items are for padding and the second items are for the Axes.
    +# sizes are in inch.
    +h = [Size.Fixed(1.0), Size.Fixed(4.5)]
    +v = [Size.Fixed(0.7), Size.Fixed(5.)]
    +
    +divider = Divider(fig, (0, 0, 1, 1), h, v, aspect=False)
    +# The width and height of the rectangle are ignored.
    +
    +ax = fig.add_axes(divider.get_position(),
    +                  axes_locator=divider.new_locator(nx=1, ny=1))
    +
    +ax.plot([1, 2, 3])
    +
    +# %%
    +
    +
    +fig = plt.figure(figsize=(6, 6))
    +
    +# The first & third items are for padding and the second items are for the
    +# Axes. Sizes are in inches.
    +h = [Size.Fixed(1.0), Size.Scaled(1.), Size.Fixed(.2)]
    +v = [Size.Fixed(0.7), Size.Scaled(1.), Size.Fixed(.5)]
    +
    +divider = Divider(fig, (0, 0, 1, 1), h, v, aspect=False)
    +# The width and height of the rectangle are ignored.
    +
    +ax = fig.add_axes(divider.get_position(),
    +                  axes_locator=divider.new_locator(nx=1, ny=1))
    +
    +ax.plot([1, 2, 3])
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/demo_imagegrid_aspect.py b/galleries/examples/axes_grid1/demo_imagegrid_aspect.py
    new file mode 100644
    index 000000000000..55268c41c9b1
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/demo_imagegrid_aspect.py
    @@ -0,0 +1,23 @@
    +"""
    +=========================================
    +ImageGrid cells with a fixed aspect ratio
    +=========================================
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import ImageGrid
    +
    +fig = plt.figure()
    +
    +grid1 = ImageGrid(fig, 121, (2, 2), axes_pad=0.1,
    +                  aspect=True, share_all=True)
    +for i in [0, 1]:
    +    grid1[i].set_aspect(2)
    +
    +grid2 = ImageGrid(fig, 122, (2, 2), axes_pad=0.1,
    +                  aspect=True, share_all=True)
    +for i in [1, 3]:
    +    grid2[i].set_aspect(2)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/inset_locator_demo.py b/galleries/examples/axes_grid1/inset_locator_demo.py
    new file mode 100644
    index 000000000000..e4b310ac6c73
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/inset_locator_demo.py
    @@ -0,0 +1,145 @@
    +"""
    +==================
    +Inset locator demo
    +==================
    +
    +"""
    +
    +# %%
    +# The `.inset_locator`'s `~.inset_locator.inset_axes` allows
    +# easily placing insets in the corners of the Axes by specifying a width and
    +# height and optionally a location (loc) that accepts locations as codes,
    +# similar to `~matplotlib.axes.Axes.legend`.
    +# By default, the inset is offset by some points from the axes,
    +# controlled via the *borderpad* parameter.
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1.inset_locator import inset_axes
    +
    +fig, (ax, ax2) = plt.subplots(1, 2, figsize=[5.5, 2.8])
    +
    +# Create inset of width 1.3 inches and height 0.9 inches
    +# at the default upper right location.
    +axins = inset_axes(ax, width=1.3, height=0.9)
    +
    +# Create inset of width 30% and height 40% of the parent Axes' bounding box
    +# at the lower left corner.
    +axins2 = inset_axes(ax, width="30%", height="40%", loc="lower left")
    +
    +# Create inset of mixed specifications in the second subplot;
    +# width is 30% of parent Axes' bounding box and
    +# height is 1 inch at the upper left corner.
    +axins3 = inset_axes(ax2, width="30%", height=1., loc="upper left")
    +
    +# Create an inset in the lower right corner with borderpad=1, i.e.
    +# 10 points padding (as 10pt is the default fontsize) to the parent Axes.
    +axins4 = inset_axes(ax2, width="20%", height="20%", loc="lower right", borderpad=1)
    +
    +# Turn ticklabels of insets off
    +for axi in [axins, axins2, axins3, axins4]:
    +    axi.tick_params(labelleft=False, labelbottom=False)
    +
    +plt.show()
    +
    +
    +# %%
    +# The parameters *bbox_to_anchor* and *bbox_transform* can be used for a more
    +# fine-grained control over the inset position and size or even to position
    +# the inset at completely arbitrary positions.
    +# The *bbox_to_anchor* sets the bounding box in coordinates according to the
    +# *bbox_transform*.
    +#
    +
    +fig = plt.figure(figsize=[5.5, 2.8])
    +ax = fig.add_subplot(121)
    +
    +# We use the Axes transform as bbox_transform. Therefore, the bounding box
    +# needs to be specified in axes coordinates ((0, 0) is the lower left corner
    +# of the Axes, (1, 1) is the upper right corner).
    +# The bounding box (.2, .4, .6, .5) starts at (.2, .4) and ranges to (.8, .9)
    +# in those coordinates.
    +# Inside this bounding box an inset of half the bounding box' width and
    +# three quarters of the bounding box' height is created. The lower left corner
    +# of the inset is aligned to the lower left corner of the bounding box.
    +# The inset is then offset by the default 0.5 in units of the font size.
    +
    +axins = inset_axes(ax, width="50%", height="75%",
    +                   bbox_to_anchor=(.2, .4, .6, .5),
    +                   bbox_transform=ax.transAxes, loc="lower left")
    +
    +# For visualization purposes we mark the bounding box by a rectangle
    +ax.add_patch(plt.Rectangle((.2, .4), .6, .5, ls="--", ec="c", fc="none",
    +                           transform=ax.transAxes))
    +
    +# We set the axis limits to something other than the default, in order to not
    +# distract from the fact that axes coordinates are used here.
    +ax.set(xlim=(0, 10), ylim=(0, 10))
    +
    +
    +# Note how the two following insets are created at the same positions, one by
    +# use of the default parent Axes' bbox and the other via a bbox in Axes
    +# coordinates and the respective transform.
    +ax2 = fig.add_subplot(222)
    +axins2 = inset_axes(ax2, width="30%", height="50%")
    +
    +ax3 = fig.add_subplot(224)
    +axins3 = inset_axes(ax3, width="100%", height="100%",
    +                    bbox_to_anchor=(.7, .5, .3, .5),
    +                    bbox_transform=ax3.transAxes)
    +
    +# For visualization purposes we mark the bounding box by a rectangle
    +ax2.add_patch(plt.Rectangle((0, 0), 1, 1, ls="--", lw=2, ec="c", fc="none"))
    +ax3.add_patch(plt.Rectangle((.7, .5), .3, .5, ls="--", lw=2,
    +                            ec="c", fc="none"))
    +
    +# Turn ticklabels off
    +for axi in [axins2, axins3, ax2, ax3]:
    +    axi.tick_params(labelleft=False, labelbottom=False)
    +
    +plt.show()
    +
    +
    +# %%
    +# In the above the Axes transform together with 4-tuple bounding boxes has been
    +# used as it mostly is useful to specify an inset relative to the Axes it is
    +# an inset to. However, other use cases are equally possible. The following
    +# example examines some of those.
    +#
    +
    +fig = plt.figure(figsize=[5.5, 2.8])
    +ax = fig.add_subplot(131)
    +
    +# Create an inset outside the Axes
    +axins = inset_axes(ax, width="100%", height="100%",
    +                   bbox_to_anchor=(1.05, .6, .5, .4),
    +                   bbox_transform=ax.transAxes, loc="upper left", borderpad=0)
    +axins.tick_params(left=False, right=True, labelleft=False, labelright=True)
    +
    +# Create an inset with a 2-tuple bounding box. Note that this creates a
    +# bbox without extent. This hence only makes sense when specifying
    +# width and height in absolute units (inches).
    +axins2 = inset_axes(ax, width=0.5, height=0.4,
    +                    bbox_to_anchor=(0.33, 0.25),
    +                    bbox_transform=ax.transAxes, loc="lower left", borderpad=0)
    +
    +
    +ax2 = fig.add_subplot(133)
    +ax2.set_xscale("log")
    +ax2.set(xlim=(1e-6, 1e6), ylim=(-2, 6))
    +
    +# Create inset in data coordinates using ax.transData as transform
    +axins3 = inset_axes(ax2, width="100%", height="100%",
    +                    bbox_to_anchor=(1e-2, 2, 1e3, 3),
    +                    bbox_transform=ax2.transData, loc="upper left", borderpad=0)
    +
    +# Create an inset horizontally centered in figure coordinates and vertically
    +# bound to line up with the Axes.
    +from matplotlib.transforms import blended_transform_factory  # noqa
    +
    +transform = blended_transform_factory(fig.transFigure, ax2.transAxes)
    +axins4 = inset_axes(ax2, width="16%", height="34%",
    +                    bbox_to_anchor=(0, 0, 1, 1),
    +                    bbox_transform=transform, loc="lower center", borderpad=0)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/inset_locator_demo2.py b/galleries/examples/axes_grid1/inset_locator_demo2.py
    new file mode 100644
    index 000000000000..1bbbdd39b886
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/inset_locator_demo2.py
    @@ -0,0 +1,73 @@
    +"""
    +====================
    +Inset locator demo 2
    +====================
    +
    +This demo shows how to create a zoomed inset via `.zoomed_inset_axes`.
    +In the first subplot an `.AnchoredSizeBar` shows the zoom effect.
    +In the second subplot a connection to the region of interest is
    +created via `.mark_inset`.
    +
    +A version of the second subplot, not using the toolkit, is available in
    +:doc:`/gallery/subplots_axes_and_figures/zoom_inset_axes`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
    +from mpl_toolkits.axes_grid1.inset_locator import mark_inset, zoomed_inset_axes
    +
    +fig, (ax, ax2) = plt.subplots(ncols=2, figsize=[6, 3])
    +
    +
    +# First subplot, showing an inset with a size bar.
    +ax.set_aspect(1)
    +
    +axins = zoomed_inset_axes(ax, zoom=0.5, loc='upper right')
    +# fix the number of ticks on the inset Axes
    +axins.yaxis.get_major_locator().set_params(nbins=7)
    +axins.xaxis.get_major_locator().set_params(nbins=7)
    +axins.tick_params(labelleft=False, labelbottom=False)
    +
    +
    +def add_sizebar(ax, size):
    +    asb = AnchoredSizeBar(ax.transData,
    +                          size,
    +                          str(size),
    +                          loc="lower center",
    +                          pad=0.1, borderpad=0.5, sep=5,
    +                          frameon=False)
    +    ax.add_artist(asb)
    +
    +add_sizebar(ax, 0.5)
    +add_sizebar(axins, 0.5)
    +
    +
    +# Second subplot, showing an image with an inset zoom and a marked inset
    +Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")  # 15x15 array
    +extent = (-3, 4, -4, 3)
    +Z2 = np.zeros((150, 150))
    +ny, nx = Z.shape
    +Z2[30:30+ny, 30:30+nx] = Z
    +
    +ax2.imshow(Z2, extent=extent, origin="lower")
    +
    +axins2 = zoomed_inset_axes(ax2, zoom=6, loc="upper right")
    +axins2.imshow(Z2, extent=extent, origin="lower")
    +
    +# subregion of the original image
    +x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
    +axins2.set_xlim(x1, x2)
    +axins2.set_ylim(y1, y2)
    +# fix the number of ticks on the inset Axes
    +axins2.yaxis.get_major_locator().set_params(nbins=7)
    +axins2.xaxis.get_major_locator().set_params(nbins=7)
    +axins2.tick_params(labelleft=False, labelbottom=False)
    +
    +# draw a bbox of the region of the inset Axes in the parent Axes and
    +# connecting lines between the bbox and the inset Axes area
    +mark_inset(ax2, axins2, loc1=2, loc2=4, fc="none", ec="0.5")
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py b/galleries/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py
    new file mode 100644
    index 000000000000..7e914ff76b6b
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/make_room_for_ylabel_using_axesgrid.py
    @@ -0,0 +1,55 @@
    +"""
    +====================================
    +Make room for ylabel using axes_grid
    +====================================
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import make_axes_locatable
    +from mpl_toolkits.axes_grid1.axes_divider import make_axes_area_auto_adjustable
    +
    +fig = plt.figure()
    +ax = fig.add_axes((0, 0, 1, 1))
    +
    +ax.set_yticks([0.5], labels=["very long label"])
    +
    +make_axes_area_auto_adjustable(ax)
    +
    +# %%
    +
    +fig = plt.figure()
    +ax1 = fig.add_axes((0, 0, 1, 0.5))
    +ax2 = fig.add_axes((0, 0.5, 1, 0.5))
    +
    +ax1.set_yticks([0.5], labels=["very long label"])
    +ax1.set_ylabel("Y label")
    +
    +ax2.set_title("Title")
    +
    +make_axes_area_auto_adjustable(ax1, pad=0.1, use_axes=[ax1, ax2])
    +make_axes_area_auto_adjustable(ax2, pad=0.1, use_axes=[ax1, ax2])
    +
    +# %%
    +
    +fig = plt.figure()
    +ax1 = fig.add_axes((0, 0, 1, 1))
    +divider = make_axes_locatable(ax1)
    +
    +ax2 = divider.append_axes("right", "100%", pad=0.3, sharey=ax1)
    +ax2.tick_params(labelleft=False)
    +fig.add_axes(ax2)
    +
    +divider.add_auto_adjustable_area(use_axes=[ax1], pad=0.1,
    +                                 adjust_dirs=["left"])
    +divider.add_auto_adjustable_area(use_axes=[ax2], pad=0.1,
    +                                 adjust_dirs=["right"])
    +divider.add_auto_adjustable_area(use_axes=[ax1, ax2], pad=0.1,
    +                                 adjust_dirs=["top", "bottom"])
    +
    +ax1.set_yticks([0.5], labels=["very long label"])
    +
    +ax2.set_title("Title")
    +ax2.set_xlabel("X - Label")
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/parasite_simple.py b/galleries/examples/axes_grid1/parasite_simple.py
    new file mode 100644
    index 000000000000..a0c4d68051a9
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/parasite_simple.py
    @@ -0,0 +1,26 @@
    +"""
    +===============
    +Parasite Simple
    +===============
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import host_subplot
    +
    +host = host_subplot(111)
    +par = host.twinx()
    +
    +host.set_xlabel("Distance")
    +host.set_ylabel("Density")
    +par.set_ylabel("Temperature")
    +
    +p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
    +p2, = par.plot([0, 1, 2], [0, 3, 2], label="Temperature")
    +
    +host.legend(labelcolor="linecolor")
    +
    +host.yaxis.label.set_color(p1.get_color())
    +par.yaxis.label.set_color(p2.get_color())
    +
    +plt.show()
    diff --git a/examples/axes_grid1/parasite_simple2.py b/galleries/examples/axes_grid1/parasite_simple2.py
    similarity index 85%
    rename from examples/axes_grid1/parasite_simple2.py
    rename to galleries/examples/axes_grid1/parasite_simple2.py
    index 1ab516f0156d..3f587e08e9c4 100644
    --- a/examples/axes_grid1/parasite_simple2.py
    +++ b/galleries/examples/axes_grid1/parasite_simple2.py
    @@ -1,6 +1,13 @@
    -import matplotlib.transforms as mtransforms
    +"""
    +================
    +Parasite Simple2
    +================
    +
    +"""
     import matplotlib.pyplot as plt
    -from mpl_toolkits.axes_grid1.parasite_axes import SubplotHost
    +
    +import matplotlib.transforms as mtransforms
    +from mpl_toolkits.axes_grid1.parasite_axes import HostAxes
     
     obs = [["01_S1", 3.88, 0.14, 1970, 63],
            ["01_S4", 5.6, 0.82, 1622, 150],
    @@ -10,16 +17,13 @@
     
     fig = plt.figure()
     
    -ax_kms = SubplotHost(fig, 1, 1, 1, aspect=1.)
    +ax_kms = fig.add_subplot(axes_class=HostAxes, aspect=1)
     
     # angular proper motion("/yr) to linear velocity(km/s) at distance=2.3kpc
     pm_to_kms = 1./206265.*2300*3.085e18/3.15e7/1.e5
     
     aux_trans = mtransforms.Affine2D().scale(pm_to_kms, 1.)
     ax_pm = ax_kms.twin(aux_trans)
    -ax_pm.set_viewlim_mode("transform")
    -
    -fig.add_subplot(ax_kms)
     
     for n, ds, dse, w, we in obs:
         time = ((2007 + (10. + 4/30.)/12) - 1988.5)
    @@ -38,5 +42,4 @@
     ax_kms.set_ylim(950, 3100)
     # xlim and ylim of ax_pms will be automatically adjusted.
     
    -plt.draw()
     plt.show()
    diff --git a/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py b/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py
    new file mode 100644
    index 000000000000..3f9bc4305b3f
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/scatter_hist_locatable_axes.py
    @@ -0,0 +1,77 @@
    +"""
    +====================================================
    +Align histogram to scatter plot using locatable Axes
    +====================================================
    +
    +Show the marginal distributions of a scatter plot as histograms at the sides of
    +the plot.
    +
    +For a nice alignment of the main Axes with the marginals, the Axes positions
    +are defined by a ``Divider``, produced via `.make_axes_locatable`.  Note that
    +the ``Divider`` API allows setting Axes sizes and pads in inches, which is its
    +main feature.
    +
    +If one wants to set Axes sizes and pads relative to the main Figure, see the
    +:doc:`/gallery/lines_bars_and_markers/scatter_hist` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axes_grid1 import make_axes_locatable
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# the random data
    +x = np.random.randn(1000)
    +y = np.random.randn(1000)
    +
    +
    +fig, ax = plt.subplots(figsize=(5.5, 5.5))
    +
    +# the scatter plot:
    +ax.scatter(x, y)
    +
    +# Set aspect of the main Axes.
    +ax.set_aspect(1.)
    +
    +# create new Axes on the right and on the top of the current Axes
    +divider = make_axes_locatable(ax)
    +# below height and pad are in inches
    +ax_histx = divider.append_axes("top", 1.2, pad=0.1, sharex=ax)
    +ax_histy = divider.append_axes("right", 1.2, pad=0.1, sharey=ax)
    +
    +# make some labels invisible
    +ax_histx.xaxis.set_tick_params(labelbottom=False)
    +ax_histy.yaxis.set_tick_params(labelleft=False)
    +
    +# now determine nice limits by hand:
    +binwidth = 0.25
    +xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
    +lim = (int(xymax/binwidth) + 1)*binwidth
    +
    +bins = np.arange(-lim, lim + binwidth, binwidth)
    +ax_histx.hist(x, bins=bins)
    +ax_histy.hist(y, bins=bins, orientation='horizontal')
    +
    +# the xaxis of ax_histx and yaxis of ax_histy are shared with ax,
    +# thus there is no need to manually adjust the xlim and ylim of these
    +# axis.
    +
    +ax_histx.set_yticks([0, 50, 100])
    +ax_histy.set_xticks([0, 50, 100])
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `mpl_toolkits.axes_grid1.axes_divider.make_axes_locatable`
    +#    - `matplotlib.axes.Axes.set_aspect`
    +#    - `matplotlib.axes.Axes.scatter`
    +#    - `matplotlib.axes.Axes.hist`
    diff --git a/galleries/examples/axes_grid1/simple_anchored_artists.py b/galleries/examples/axes_grid1/simple_anchored_artists.py
    new file mode 100644
    index 000000000000..848c48c16c49
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_anchored_artists.py
    @@ -0,0 +1,71 @@
    +"""
    +=======================
    +Simple Anchored Artists
    +=======================
    +
    +This example illustrates the use of the anchored helper classes found in
    +:mod:`matplotlib.offsetbox` and in :mod:`mpl_toolkits.axes_grid1`.
    +An implementation of a similar figure, but without use of the toolkit,
    +can be found in :doc:`/gallery/misc/anchored_artists`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def draw_text(ax):
    +    """
    +    Draw two text-boxes, anchored by different corners to the upper-left
    +    corner of the figure.
    +    """
    +    from matplotlib.offsetbox import AnchoredText
    +    at = AnchoredText("Figure 1a",
    +                      loc='upper left', prop=dict(size=8), frameon=True,
    +                      )
    +    at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
    +    ax.add_artist(at)
    +
    +    at2 = AnchoredText("Figure 1(b)",
    +                       loc='lower left', prop=dict(size=8), frameon=True,
    +                       bbox_to_anchor=(0., 1.),
    +                       bbox_transform=ax.transAxes
    +                       )
    +    at2.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
    +    ax.add_artist(at2)
    +
    +
    +def draw_circle(ax):
    +    """
    +    Draw a circle in axis coordinates
    +    """
    +    from matplotlib.patches import Circle
    +    from mpl_toolkits.axes_grid1.anchored_artists import AnchoredDrawingArea
    +    ada = AnchoredDrawingArea(20, 20, 0, 0,
    +                              loc='upper right', pad=0., frameon=False)
    +    p = Circle((10, 10), 10)
    +    ada.da.add_artist(p)
    +    ax.add_artist(ada)
    +
    +
    +def draw_sizebar(ax):
    +    """
    +    Draw a horizontal bar with length of 0.1 in data coordinates,
    +    with a fixed label underneath.
    +    """
    +    from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
    +    asb = AnchoredSizeBar(ax.transData,
    +                          0.1,
    +                          r"1$^{\prime}$",
    +                          loc='lower center',
    +                          pad=0.1, borderpad=0.5, sep=5,
    +                          frameon=False)
    +    ax.add_artist(asb)
    +
    +
    +fig, ax = plt.subplots()
    +ax.set_aspect(1.)
    +
    +draw_text(ax)
    +draw_circle(ax)
    +draw_sizebar(ax)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/simple_axes_divider1.py b/galleries/examples/axes_grid1/simple_axes_divider1.py
    new file mode 100644
    index 000000000000..414672dc3596
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_axes_divider1.py
    @@ -0,0 +1,69 @@
    +"""
    +=====================
    +Simple Axes Divider 1
    +=====================
    +
    +See also :ref:`axes_grid`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import Divider, Size
    +
    +
    +def label_axes(ax, text):
    +    """Place a label at the center of an Axes, and remove the axis ticks."""
    +    ax.text(.5, .5, text, transform=ax.transAxes,
    +            horizontalalignment="center", verticalalignment="center")
    +    ax.tick_params(bottom=False, labelbottom=False,
    +                   left=False, labelleft=False)
    +
    +
    +# %%
    +# Fixed Axes sizes; fixed paddings.
    +
    +fig = plt.figure(figsize=(6, 6))
    +fig.suptitle("Fixed axes sizes, fixed paddings")
    +
    +# Sizes are in inches.
    +horiz = [Size.Fixed(1.), Size.Fixed(.5), Size.Fixed(1.5), Size.Fixed(.5)]
    +vert = [Size.Fixed(1.5), Size.Fixed(.5), Size.Fixed(1.)]
    +
    +rect = (0.1, 0.1, 0.8, 0.8)
    +# Divide the Axes rectangle into a grid with sizes specified by horiz * vert.
    +div = Divider(fig, rect, horiz, vert, aspect=False)
    +
    +# The rect parameter will actually be ignored and overridden by axes_locator.
    +ax1 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=0))
    +label_axes(ax1, "nx=0, ny=0")
    +ax2 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=2))
    +label_axes(ax2, "nx=0, ny=2")
    +ax3 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, ny=2))
    +label_axes(ax3, "nx=2, ny=2")
    +ax4 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, nx1=4, ny=0))
    +label_axes(ax4, "nx=2, nx1=4, ny=0")
    +
    +# %%
    +# Axes sizes that scale with the figure size; fixed paddings.
    +
    +fig = plt.figure(figsize=(6, 6))
    +fig.suptitle("Scalable axes sizes, fixed paddings")
    +
    +horiz = [Size.Scaled(1.5), Size.Fixed(.5), Size.Scaled(1.), Size.Scaled(.5)]
    +vert = [Size.Scaled(1.), Size.Fixed(.5), Size.Scaled(1.5)]
    +
    +rect = (0.1, 0.1, 0.8, 0.8)
    +# Divide the Axes rectangle into a grid with sizes specified by horiz * vert.
    +div = Divider(fig, rect, horiz, vert, aspect=False)
    +
    +# The rect parameter will actually be ignored and overridden by axes_locator.
    +ax1 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=0))
    +label_axes(ax1, "nx=0, ny=0")
    +ax2 = fig.add_axes(rect, axes_locator=div.new_locator(nx=0, ny=2))
    +label_axes(ax2, "nx=0, ny=2")
    +ax3 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, ny=2))
    +label_axes(ax3, "nx=2, ny=2")
    +ax4 = fig.add_axes(rect, axes_locator=div.new_locator(nx=2, nx1=4, ny=0))
    +label_axes(ax4, "nx=2, nx1=4, ny=0")
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/simple_axes_divider3.py b/galleries/examples/axes_grid1/simple_axes_divider3.py
    new file mode 100644
    index 000000000000..e2f195bcb753
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_axes_divider3.py
    @@ -0,0 +1,44 @@
    +"""
    +=====================
    +Simple axes divider 3
    +=====================
    +
    +See also :ref:`axes_grid`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axes_grid1 import Divider
    +import mpl_toolkits.axes_grid1.axes_size as Size
    +
    +fig = plt.figure(figsize=(5.5, 4))
    +
    +# the rect parameter will be ignored as we will set axes_locator
    +rect = (0.1, 0.1, 0.8, 0.8)
    +ax = [fig.add_axes(rect, label="%d" % i) for i in range(4)]
    +
    +
    +horiz = [Size.AxesX(ax[0]), Size.Fixed(.5), Size.AxesX(ax[1])]
    +vert = [Size.AxesY(ax[0]), Size.Fixed(.5), Size.AxesY(ax[2])]
    +
    +# divide the Axes rectangle into grid whose size is specified by horiz * vert
    +divider = Divider(fig, rect, horiz, vert, aspect=False)
    +
    +
    +ax[0].set_axes_locator(divider.new_locator(nx=0, ny=0))
    +ax[1].set_axes_locator(divider.new_locator(nx=2, ny=0))
    +ax[2].set_axes_locator(divider.new_locator(nx=0, ny=2))
    +ax[3].set_axes_locator(divider.new_locator(nx=2, ny=2))
    +
    +ax[0].set_xlim(0, 2)
    +ax[1].set_xlim(0, 1)
    +
    +ax[0].set_ylim(0, 1)
    +ax[2].set_ylim(0, 2)
    +
    +divider.set_aspect(1.)
    +
    +for ax1 in ax:
    +    ax1.tick_params(labelbottom=False, labelleft=False)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/simple_axesgrid.py b/galleries/examples/axes_grid1/simple_axesgrid.py
    new file mode 100644
    index 000000000000..bc9ffce692f8
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_axesgrid.py
    @@ -0,0 +1,29 @@
    +"""
    +================
    +Simple ImageGrid
    +================
    +
    +Align multiple images using `~mpl_toolkits.axes_grid1.axes_grid.ImageGrid`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axes_grid1 import ImageGrid
    +
    +im1 = np.arange(100).reshape((10, 10))
    +im2 = im1.T
    +im3 = np.flipud(im1)
    +im4 = np.fliplr(im2)
    +
    +fig = plt.figure(figsize=(4., 4.))
    +grid = ImageGrid(fig, 111,  # similar to subplot(111)
    +                 nrows_ncols=(2, 2),  # creates 2x2 grid of Axes
    +                 axes_pad=0.1,  # pad between Axes in inch.
    +                 )
    +
    +for ax, im in zip(grid, [im1, im2, im3, im4]):
    +    # Iterating over the grid returns the Axes.
    +    ax.imshow(im)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/simple_axesgrid2.py b/galleries/examples/axes_grid1/simple_axesgrid2.py
    new file mode 100644
    index 000000000000..1c9c524da43c
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_axesgrid2.py
    @@ -0,0 +1,31 @@
    +"""
    +==================
    +Simple ImageGrid 2
    +==================
    +
    +Align multiple images of different sizes using
    +`~mpl_toolkits.axes_grid1.axes_grid.ImageGrid`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib import cbook
    +from mpl_toolkits.axes_grid1 import ImageGrid
    +
    +fig = plt.figure(figsize=(5.5, 3.5))
    +grid = ImageGrid(fig, 111,  # similar to subplot(111)
    +                 nrows_ncols=(1, 3),
    +                 axes_pad=0.1,
    +                 label_mode="L",
    +                 )
    +
    +# demo image
    +Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")
    +im1 = Z
    +im2 = Z[:, :10]
    +im3 = Z[:, 10:]
    +vmin, vmax = Z.min(), Z.max()
    +for ax, im in zip(grid, [im1, im2, im3]):
    +    ax.imshow(im, origin="lower", vmin=vmin, vmax=vmax)
    +
    +plt.show()
    diff --git a/galleries/examples/axes_grid1/simple_axisline4.py b/galleries/examples/axes_grid1/simple_axisline4.py
    new file mode 100644
    index 000000000000..bbe6ef1c7970
    --- /dev/null
    +++ b/galleries/examples/axes_grid1/simple_axisline4.py
    @@ -0,0 +1,24 @@
    +"""
    +================
    +Simple Axisline4
    +================
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axes_grid1 import host_subplot
    +
    +ax = host_subplot(111)
    +xx = np.arange(0, 2*np.pi, 0.01)
    +ax.plot(xx, np.sin(xx))
    +
    +ax2 = ax.twin()  # ax2 is responsible for "top" axis and "right" axis
    +ax2.set_xticks([0., .5*np.pi, np.pi, 1.5*np.pi, 2*np.pi],
    +               labels=["$0$", r"$\frac{1}{2}\pi$",
    +                       r"$\pi$", r"$\frac{3}{2}\pi$", r"$2\pi$"])
    +
    +ax2.axis["right"].major_ticklabels.set_visible(False)
    +ax2.axis["top"].major_ticklabels.set_visible(True)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/README.txt b/galleries/examples/axisartist/README.txt
    new file mode 100644
    index 000000000000..c2d1716f8b52
    --- /dev/null
    +++ b/galleries/examples/axisartist/README.txt
    @@ -0,0 +1,6 @@
    +.. _axis_artist_examples:
    +
    +.. _axisartist-examples-index:
    +
    +Module - axisartist
    +===================
    diff --git a/galleries/examples/axisartist/axis_direction.py b/galleries/examples/axisartist/axis_direction.py
    new file mode 100644
    index 000000000000..fbbbc5b93ce4
    --- /dev/null
    +++ b/galleries/examples/axisartist/axis_direction.py
    @@ -0,0 +1,68 @@
    +"""
    +==============
    +Axis Direction
    +==============
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import mpl_toolkits.axisartist as axisartist
    +
    +
    +def setup_axes(fig, pos):
    +    ax = fig.add_subplot(pos, axes_class=axisartist.Axes)
    +
    +    ax.set_ylim(-0.1, 1.5)
    +    ax.set_yticks([0, 1])
    +
    +    ax.axis[:].set_visible(False)
    +
    +    ax.axis["x"] = ax.new_floating_axis(1, 0.5)
    +    ax.axis["x"].set_axisline_style("->", size=1.5)
    +
    +    return ax
    +
    +
    +plt.rcParams.update({
    +    "axes.titlesize": "medium",
    +    "axes.titley": 1.1,
    +})
    +
    +fig = plt.figure(figsize=(10, 4))
    +fig.subplots_adjust(bottom=0.1, top=0.9, left=0.05, right=0.95)
    +
    +ax1 = setup_axes(fig, 251)
    +ax1.axis["x"].set_axis_direction("left")
    +
    +ax2 = setup_axes(fig, 252)
    +ax2.axis["x"].label.set_text("Label")
    +ax2.axis["x"].toggle(ticklabels=False)
    +ax2.axis["x"].set_axislabel_direction("+")
    +ax2.set_title("label direction=$+$")
    +
    +ax3 = setup_axes(fig, 253)
    +ax3.axis["x"].label.set_text("Label")
    +ax3.axis["x"].toggle(ticklabels=False)
    +ax3.axis["x"].set_axislabel_direction("-")
    +ax3.set_title("label direction=$-$")
    +
    +ax4 = setup_axes(fig, 254)
    +ax4.axis["x"].set_ticklabel_direction("+")
    +ax4.set_title("ticklabel direction=$+$")
    +
    +ax5 = setup_axes(fig, 255)
    +ax5.axis["x"].set_ticklabel_direction("-")
    +ax5.set_title("ticklabel direction=$-$")
    +
    +ax7 = setup_axes(fig, 257)
    +ax7.axis["x"].label.set_text("rotation=10")
    +ax7.axis["x"].label.set_rotation(10)
    +ax7.axis["x"].toggle(ticklabels=False)
    +
    +ax8 = setup_axes(fig, 258)
    +ax8.axis["x"].set_axislabel_direction("-")
    +ax8.axis["x"].label.set_text("rotation=10")
    +ax8.axis["x"].label.set_rotation(10)
    +ax8.axis["x"].toggle(ticklabels=False)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_axis_direction.py b/galleries/examples/axisartist/demo_axis_direction.py
    new file mode 100644
    index 000000000000..6bc46fe273a0
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_axis_direction.py
    @@ -0,0 +1,67 @@
    +"""
    +===================
    +axis_direction demo
    +===================
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.projections import PolarAxes
    +from matplotlib.transforms import Affine2D
    +import mpl_toolkits.axisartist as axisartist
    +from mpl_toolkits.axisartist import angle_helper, grid_finder
    +from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
    +
    +
    +def setup_axes(fig, rect):
    +    """Polar projection, but in a rectangular box."""
    +    # see demo_curvelinear_grid.py for details
    +    grid_helper = GridHelperCurveLinear(
    +        Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform(),
    +        extreme_finder=angle_helper.ExtremeFinderCycle(
    +            20, 20,
    +            lon_cycle=360, lat_cycle=None,
    +            lon_minmax=None, lat_minmax=(0, np.inf),
    +        ),
    +        grid_locator1=angle_helper.LocatorDMS(12),
    +        grid_locator2=grid_finder.MaxNLocator(5),
    +        tick_formatter1=angle_helper.FormatterDMS(),
    +    )
    +    ax = fig.add_subplot(
    +        rect, axes_class=axisartist.Axes, grid_helper=grid_helper,
    +        aspect=1, xlim=(-5, 12), ylim=(-5, 10))
    +    ax.axis[:].toggle(ticklabels=False)
    +    ax.grid(color=".9")
    +    return ax
    +
    +
    +def add_floating_axis1(ax):
    +    ax.axis["lat"] = axis = ax.new_floating_axis(0, 30)
    +    axis.label.set_text(r"$\theta = 30^{\circ}$")
    +    axis.label.set_visible(True)
    +    return axis
    +
    +
    +def add_floating_axis2(ax):
    +    ax.axis["lon"] = axis = ax.new_floating_axis(1, 6)
    +    axis.label.set_text(r"$r = 6$")
    +    axis.label.set_visible(True)
    +    return axis
    +
    +
    +fig = plt.figure(figsize=(8, 4), layout="constrained")
    +
    +for i, d in enumerate(["bottom", "left", "top", "right"]):
    +    ax = setup_axes(fig, rect=241+i)
    +    axis = add_floating_axis1(ax)
    +    axis.set_axis_direction(d)
    +    ax.set(title=d)
    +
    +for i, d in enumerate(["bottom", "left", "top", "right"]):
    +    ax = setup_axes(fig, rect=245+i)
    +    axis = add_floating_axis2(ax)
    +    axis.set_axis_direction(d)
    +    ax.set(title=d)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_axisline_style.py b/galleries/examples/axisartist/demo_axisline_style.py
    new file mode 100644
    index 000000000000..3fd1d4d8b767
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_axisline_style.py
    @@ -0,0 +1,36 @@
    +"""
    +================
    +Axis line styles
    +================
    +
    +This example shows some configurations for axis style.
    +
    +Note: The `mpl_toolkits.axisartist` Axes classes may be confusing for new
    +users. If the only aim is to obtain arrow heads at the ends of the axes,
    +rather check out the :doc:`/gallery/spines/centered_spines_with_arrows`
    +example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axisartist.axislines import AxesZero
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(axes_class=AxesZero)
    +
    +for direction in ["xzero", "yzero"]:
    +    # adds arrows at the ends of each axis
    +    ax.axis[direction].set_axisline_style("-|>")
    +
    +    # adds X and Y-axis from the origin
    +    ax.axis[direction].set_visible(True)
    +
    +for direction in ["left", "right", "bottom", "top"]:
    +    # hides borders
    +    ax.axis[direction].set_visible(False)
    +
    +x = np.linspace(-0.5, 1., 100)
    +ax.plot(x, np.sin(x*np.pi))
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_curvelinear_grid.py b/galleries/examples/axisartist/demo_curvelinear_grid.py
    new file mode 100644
    index 000000000000..83fc1ce0ceaa
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_curvelinear_grid.py
    @@ -0,0 +1,110 @@
    +"""
    +=====================
    +Curvilinear grid demo
    +=====================
    +
    +Custom grid and ticklines.
    +
    +This example demonstrates how to use
    +`~.grid_helper_curvelinear.GridHelperCurveLinear` to define custom grids and
    +ticklines by applying a transformation on the grid.  This can be used, as
    +shown on the second plot, to create polar projections in a rectangular box.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.projections import PolarAxes
    +from matplotlib.transforms import Affine2D
    +from mpl_toolkits.axisartist import Axes, HostAxes, angle_helper
    +from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
    +
    +
    +def curvelinear_test1(fig):
    +    """
    +    Grid for custom transform.
    +    """
    +
    +    def tr(x, y): return x, y - x
    +    def inv_tr(x, y): return x, y + x
    +
    +    grid_helper = GridHelperCurveLinear((tr, inv_tr))
    +
    +    ax1 = fig.add_subplot(1, 2, 1, axes_class=Axes, grid_helper=grid_helper)
    +    # ax1 will have ticks and gridlines defined by the given transform (+
    +    # transData of the Axes).  Note that the transform of the Axes itself
    +    # (i.e., transData) is not affected by the given transform.
    +    xx, yy = tr(np.array([3, 6]), np.array([5, 10]))
    +    ax1.plot(xx, yy)
    +
    +    ax1.set_aspect(1)
    +    ax1.set_xlim(0, 10)
    +    ax1.set_ylim(0, 10)
    +
    +    ax1.axis["t"] = ax1.new_floating_axis(0, 3)
    +    ax1.axis["t2"] = ax1.new_floating_axis(1, 7)
    +    ax1.grid(True, zorder=0)
    +
    +
    +def curvelinear_test2(fig):
    +    """
    +    Polar projection, but in a rectangular box.
    +    """
    +
    +    # PolarAxes.PolarTransform takes radian. However, we want our coordinate
    +    # system in degree
    +    tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform()
    +    # Polar projection, which involves cycle, and also has limits in
    +    # its coordinates, needs a special method to find the extremes
    +    # (min, max of the coordinate within the view).
    +    extreme_finder = angle_helper.ExtremeFinderCycle(
    +        nx=20, ny=20,  # Number of sampling points in each direction.
    +        lon_cycle=360, lat_cycle=None,
    +        lon_minmax=None, lat_minmax=(0, np.inf),
    +    )
    +    # Find grid values appropriate for the coordinate (degree, minute, second).
    +    grid_locator1 = angle_helper.LocatorDMS(12)
    +    # Use an appropriate formatter.  Note that the acceptable Locator and
    +    # Formatter classes are a bit different than that of Matplotlib, which
    +    # cannot directly be used here (this may be possible in the future).
    +    tick_formatter1 = angle_helper.FormatterDMS()
    +
    +    grid_helper = GridHelperCurveLinear(
    +        tr, extreme_finder=extreme_finder,
    +        grid_locator1=grid_locator1, tick_formatter1=tick_formatter1)
    +    ax1 = fig.add_subplot(
    +        1, 2, 2, axes_class=HostAxes, grid_helper=grid_helper)
    +
    +    # make ticklabels of right and top axis visible.
    +    ax1.axis["right"].major_ticklabels.set_visible(True)
    +    ax1.axis["top"].major_ticklabels.set_visible(True)
    +    # let right axis shows ticklabels for 1st coordinate (angle)
    +    ax1.axis["right"].get_helper().nth_coord_ticks = 0
    +    # let bottom axis shows ticklabels for 2nd coordinate (radius)
    +    ax1.axis["bottom"].get_helper().nth_coord_ticks = 1
    +
    +    ax1.set_aspect(1)
    +    ax1.set_xlim(-5, 12)
    +    ax1.set_ylim(-5, 10)
    +
    +    ax1.grid(True, zorder=0)
    +
    +    # A parasite Axes with given transform
    +    ax2 = ax1.get_aux_axes(tr)
    +    # note that ax2.transData == tr + ax1.transData
    +    # Anything you draw in ax2 will match the ticks and grids of ax1.
    +    ax2.plot(np.linspace(0, 30, 51), np.linspace(10, 10, 51), linewidth=2)
    +
    +    ax2.pcolor(np.linspace(0, 90, 4), np.linspace(0, 10, 4),
    +               np.arange(9).reshape((3, 3)))
    +    ax2.contour(np.linspace(0, 90, 4), np.linspace(0, 10, 4),
    +                np.arange(16).reshape((4, 4)), colors="k")
    +
    +
    +if __name__ == "__main__":
    +    fig = plt.figure(figsize=(7, 4))
    +
    +    curvelinear_test1(fig)
    +    curvelinear_test2(fig)
    +
    +    plt.show()
    diff --git a/galleries/examples/axisartist/demo_curvelinear_grid2.py b/galleries/examples/axisartist/demo_curvelinear_grid2.py
    new file mode 100644
    index 000000000000..a3cd06ef6706
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_curvelinear_grid2.py
    @@ -0,0 +1,48 @@
    +"""
    +======================
    +Demo CurveLinear Grid2
    +======================
    +
    +Custom grid and ticklines.
    +
    +This example demonstrates how to use GridHelperCurveLinear to define
    +custom grids and ticklines by applying a transformation on the grid.
    +As showcase on the plot, a 5x5 matrix is displayed on the Axes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.axisartist.axislines import Axes
    +from mpl_toolkits.axisartist.grid_finder import ExtremeFinderSimple, MaxNLocator
    +from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
    +
    +
    +def curvelinear_test1(fig):
    +    """Grid for custom transform."""
    +
    +    def tr(x, y):
    +        return np.sign(x)*abs(x)**.5, y
    +
    +    def inv_tr(x, y):
    +        return np.sign(x)*x**2, y
    +
    +    grid_helper = GridHelperCurveLinear(
    +        (tr, inv_tr),
    +        extreme_finder=ExtremeFinderSimple(20, 20),
    +        # better tick density
    +        grid_locator1=MaxNLocator(nbins=6), grid_locator2=MaxNLocator(nbins=6))
    +
    +    ax1 = fig.add_subplot(axes_class=Axes, grid_helper=grid_helper)
    +    # ax1 will have a ticks and gridlines defined by the given
    +    # transform (+ transData of the Axes). Note that the transform of the Axes
    +    # itself (i.e., transData) is not affected by the given transform.
    +
    +    ax1.imshow(np.arange(25).reshape(5, 5),
    +               vmax=50, cmap="gray_r", origin="lower")
    +
    +
    +if __name__ == "__main__":
    +    fig = plt.figure(figsize=(7, 4))
    +    curvelinear_test1(fig)
    +    plt.show()
    diff --git a/galleries/examples/axisartist/demo_floating_axes.py b/galleries/examples/axisartist/demo_floating_axes.py
    new file mode 100644
    index 000000000000..d36b534161c1
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_floating_axes.py
    @@ -0,0 +1,155 @@
    +"""
    +==========================
    +``floating_axes`` features
    +==========================
    +
    +Demonstration of features of the :mod:`.floating_axes` module:
    +
    +* Using `~.axes.Axes.scatter` and `~.axes.Axes.bar` with changing the shape of
    +  the plot.
    +* Using `~.floating_axes.GridHelperCurveLinear` to rotate the plot and set the
    +  plot boundary.
    +* Using `~.Figure.add_subplot` to create a subplot using the return value from
    +  `~.floating_axes.GridHelperCurveLinear`.
    +* Making a sector plot by adding more features to
    +  `~.floating_axes.GridHelperCurveLinear`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.projections import PolarAxes
    +from matplotlib.transforms import Affine2D
    +import mpl_toolkits.axisartist.angle_helper as angle_helper
    +import mpl_toolkits.axisartist.floating_axes as floating_axes
    +from mpl_toolkits.axisartist.grid_finder import DictFormatter, FixedLocator, MaxNLocator
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def setup_axes1(fig, rect):
    +    """
    +    A simple one.
    +    """
    +    tr = Affine2D().scale(2, 1).rotate_deg(30)
    +
    +    grid_helper = floating_axes.GridHelperCurveLinear(
    +        tr, extremes=(-0.5, 3.5, 0, 4),
    +        grid_locator1=MaxNLocator(nbins=4),
    +        grid_locator2=MaxNLocator(nbins=4))
    +
    +    ax1 = fig.add_subplot(
    +        rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper)
    +    ax1.grid()
    +
    +    aux_ax = ax1.get_aux_axes(tr)
    +
    +    return ax1, aux_ax
    +
    +
    +def setup_axes2(fig, rect):
    +    """
    +    With custom locator and formatter.
    +    Note that the extreme values are swapped.
    +    """
    +    tr = PolarAxes.PolarTransform()
    +
    +    pi = np.pi
    +    angle_ticks = {
    +        0: r"$0$",
    +        pi/4: r"$\frac{1}{4}\pi$",
    +        pi/2: r"$\frac{1}{2}\pi$",
    +    }
    +    grid_helper = floating_axes.GridHelperCurveLinear(
    +        tr, extremes=(.5*pi, 0, 2, 1),
    +        grid_locator1=FixedLocator([*angle_ticks]),
    +        tick_formatter1=DictFormatter(angle_ticks),
    +        grid_locator2=MaxNLocator(2),
    +        tick_formatter2=None,
    +    )
    +    ax1 = fig.add_subplot(
    +        rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper)
    +    ax1.grid()
    +
    +    # create a parasite Axes whose transData in RA, cz
    +    aux_ax = ax1.get_aux_axes(tr)
    +
    +    aux_ax.patch = ax1.patch  # for aux_ax to have a clip path as in ax
    +    ax1.patch.zorder = 0.9  # but this has a side effect that the patch is
    +    # drawn twice, and possibly over some other
    +    # artists. So, we decrease the zorder a bit to
    +    # prevent this.
    +
    +    return ax1, aux_ax
    +
    +
    +def setup_axes3(fig, rect):
    +    """
    +    Sometimes, things like axis_direction need to be adjusted.
    +    """
    +
    +    tr_rotate = Affine2D().translate(-95, 0)  # rotate a bit for better orientation
    +    tr_scale = Affine2D().scale(np.pi/180., 1.)  # scale degree to radians
    +    tr = tr_rotate + tr_scale + PolarAxes.PolarTransform()
    +
    +    # Specify theta limits in degrees
    +    ra0, ra1 = 8.*15, 14.*15
    +    # Specify radial limits
    +    cz0, cz1 = 0, 14000
    +
    +    grid_helper = floating_axes.GridHelperCurveLinear(
    +        tr, extremes=(ra0, ra1, cz0, cz1),
    +        grid_locator1=angle_helper.LocatorHMS(4),
    +        tick_formatter1=angle_helper.FormatterHMS(),
    +        grid_locator2=MaxNLocator(3),
    +        tick_formatter2=None,
    +    )
    +    ax1 = fig.add_subplot(
    +        rect, axes_class=floating_axes.FloatingAxes, grid_helper=grid_helper)
    +
    +    # adjust axis
    +    ax1.axis["left"].set_axis_direction("bottom")
    +    ax1.axis["right"].set_axis_direction("top")
    +
    +    ax1.axis["bottom"].set_visible(False)
    +    ax1.axis["top"].set_axis_direction("bottom")
    +    ax1.axis["top"].toggle(ticklabels=True, label=True)
    +    ax1.axis["top"].major_ticklabels.set_axis_direction("top")
    +    ax1.axis["top"].label.set_axis_direction("top")
    +
    +    ax1.axis["left"].label.set_text(r"cz [km$^{-1}$]")
    +    ax1.axis["top"].label.set_text(r"$\alpha_{1950}$")
    +    ax1.grid()
    +
    +    # create a parasite Axes whose transData in RA, cz
    +    aux_ax = ax1.get_aux_axes(tr)
    +
    +    aux_ax.patch = ax1.patch  # for aux_ax to have a clip path as in ax
    +    ax1.patch.zorder = 0.9  # but this has a side effect that the patch is
    +    # drawn twice, and possibly over some other
    +    # artists. So, we decrease the zorder a bit to
    +    # prevent this.
    +
    +    return ax1, aux_ax
    +
    +
    +# %%
    +fig = plt.figure(figsize=(8, 4))
    +fig.subplots_adjust(wspace=0.3, left=0.05, right=0.95)
    +
    +ax1, aux_ax1 = setup_axes1(fig, 131)
    +aux_ax1.bar([0, 1, 2, 3], [3, 2, 1, 3])
    +
    +ax2, aux_ax2 = setup_axes2(fig, 132)
    +theta = np.random.rand(10)*.5*np.pi
    +radius = np.random.rand(10) + 1.
    +aux_ax2.scatter(theta, radius)
    +
    +ax3, aux_ax3 = setup_axes3(fig, 133)
    +
    +theta = (8 + np.random.rand(10)*(14 - 8))*15.  # in degrees
    +radius = np.random.rand(10)*14000.
    +aux_ax3.scatter(theta, radius)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_floating_axis.py b/galleries/examples/axisartist/demo_floating_axis.py
    new file mode 100644
    index 000000000000..7760ed2089be
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_floating_axis.py
    @@ -0,0 +1,57 @@
    +"""
    +==================
    +floating_axis demo
    +==================
    +
    +Axis within rectangular frame.
    +
    +The following code demonstrates how to put a floating polar curve within a
    +rectangular box. In order to get a better sense of polar curves, please look at
    +:doc:`/gallery/axisartist/demo_curvelinear_grid`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.projections import PolarAxes
    +from matplotlib.transforms import Affine2D
    +from mpl_toolkits.axisartist import GridHelperCurveLinear, HostAxes
    +import mpl_toolkits.axisartist.angle_helper as angle_helper
    +
    +
    +def curvelinear_test2(fig):
    +    """Polar projection, but in a rectangular box."""
    +    # see demo_curvelinear_grid.py for details
    +    grid_helper = GridHelperCurveLinear(
    +        Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform(),
    +        extreme_finder=angle_helper.ExtremeFinderCycle(
    +            20, 20,
    +            lon_cycle=360, lat_cycle=None,
    +            lon_minmax=None, lat_minmax=(0, np.inf),
    +        ),
    +        grid_locator1=angle_helper.LocatorDMS(12),
    +        tick_formatter1=angle_helper.FormatterDMS(),
    +    )
    +    ax1 = fig.add_subplot(axes_class=HostAxes, grid_helper=grid_helper)
    +
    +    # Now create floating axis
    +
    +    # floating axis whose first coordinate (theta) is fixed at 60
    +    ax1.axis["lat"] = axis = ax1.new_floating_axis(0, 60)
    +    axis.label.set_text(r"$\theta = 60^{\circ}$")
    +    axis.label.set_visible(True)
    +
    +    # floating axis whose second coordinate (r) is fixed at 6
    +    ax1.axis["lon"] = axis = ax1.new_floating_axis(1, 6)
    +    axis.label.set_text(r"$r = 6$")
    +
    +    ax1.set_aspect(1.)
    +    ax1.set_xlim(-5, 12)
    +    ax1.set_ylim(-5, 10)
    +
    +    ax1.grid(True)
    +
    +
    +fig = plt.figure(figsize=(5, 5))
    +curvelinear_test2(fig)
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_parasite_axes.py b/galleries/examples/axisartist/demo_parasite_axes.py
    new file mode 100644
    index 000000000000..800b9be32ac8
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_parasite_axes.py
    @@ -0,0 +1,53 @@
    +"""
    +==================
    +Parasite Axes demo
    +==================
    +
    +Create a parasite Axes. Such Axes would share the x scale with a host Axes,
    +but show a different scale in y direction.
    +
    +This approach uses `mpl_toolkits.axes_grid1.parasite_axes.HostAxes` and
    +`mpl_toolkits.axes_grid1.parasite_axes.ParasiteAxes`.
    +
    +The standard and recommended approach is to use instead standard Matplotlib
    +axes, as shown in the :doc:`/gallery/spines/multiple_yaxis_with_spines`
    +example.
    +
    +An alternative approach using `mpl_toolkits.axes_grid1` and
    +`mpl_toolkits.axisartist` is shown in the
    +:doc:`/gallery/axisartist/demo_parasite_axes2` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axisartist.parasite_axes import HostAxes
    +
    +fig = plt.figure()
    +
    +host = fig.add_axes((0.15, 0.1, 0.65, 0.8), axes_class=HostAxes)
    +par1 = host.get_aux_axes(viewlim_mode=None, sharex=host)
    +par2 = host.get_aux_axes(viewlim_mode=None, sharex=host)
    +
    +host.axis["right"].set_visible(False)
    +
    +par1.axis["right"].set_visible(True)
    +par1.axis["right"].major_ticklabels.set_visible(True)
    +par1.axis["right"].label.set_visible(True)
    +
    +par2.axis["right2"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
    +
    +p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
    +p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
    +p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
    +
    +host.set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
    +par1.set(ylim=(0, 4), ylabel="Temperature")
    +par2.set(ylim=(1, 65), ylabel="Velocity")
    +
    +host.legend()
    +
    +host.axis["left"].label.set_color(p1.get_color())
    +par1.axis["right"].label.set_color(p2.get_color())
    +par2.axis["right2"].label.set_color(p3.get_color())
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_parasite_axes2.py b/galleries/examples/axisartist/demo_parasite_axes2.py
    new file mode 100644
    index 000000000000..a2e8bd30022e
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_parasite_axes2.py
    @@ -0,0 +1,56 @@
    +"""
    +==================
    +Parasite axis demo
    +==================
    +
    +This example demonstrates the use of parasite axis to plot multiple datasets
    +onto one single plot.
    +
    +Notice how in this example, *par1* and *par2* are both obtained by calling
    +``twinx()``, which ties their x-limits with the host's x-axis. From there, each
    +of those two axis behave separately from each other: different datasets can be
    +plotted, and the y-limits are adjusted separately.
    +
    +This approach uses `mpl_toolkits.axes_grid1.parasite_axes.host_subplot` and
    +`mpl_toolkits.axisartist.axislines.Axes`.
    +
    +The standard and recommended approach is to use instead standard Matplotlib
    +axes, as shown in the :doc:`/gallery/spines/multiple_yaxis_with_spines`
    +example.
    +
    +An alternative approach using `mpl_toolkits.axes_grid1.parasite_axes.HostAxes`
    +and `mpl_toolkits.axes_grid1.parasite_axes.ParasiteAxes` is shown in the
    +:doc:`/gallery/axisartist/demo_parasite_axes` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits import axisartist
    +from mpl_toolkits.axes_grid1 import host_subplot
    +
    +host = host_subplot(111, axes_class=axisartist.Axes)
    +plt.subplots_adjust(right=0.75)
    +
    +par1 = host.twinx()
    +par2 = host.twinx()
    +
    +par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
    +
    +par1.axis["right"].toggle(all=True)
    +par2.axis["right"].toggle(all=True)
    +
    +p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
    +p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
    +p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
    +
    +host.set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
    +par1.set(ylim=(0, 4), ylabel="Temperature")
    +par2.set(ylim=(1, 65), ylabel="Velocity")
    +
    +host.legend()
    +
    +host.axis["left"].label.set_color(p1.get_color())
    +par1.axis["right"].label.set_color(p2.get_color())
    +par2.axis["right"].label.set_color(p3.get_color())
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_ticklabel_alignment.py b/galleries/examples/axisartist/demo_ticklabel_alignment.py
    new file mode 100644
    index 000000000000..b68b8263f2ed
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_ticklabel_alignment.py
    @@ -0,0 +1,40 @@
    +"""
    +===================
    +Ticklabel alignment
    +===================
    +
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +
    +import mpl_toolkits.axisartist as axisartist
    +
    +
    +def setup_axes(fig, pos):
    +    ax = fig.add_subplot(pos, axes_class=axisartist.Axes)
    +    ax.set_yticks([0.2, 0.8], labels=["short", "loooong"])
    +    ax.set_xticks([0.2, 0.8], labels=[r"$\frac{1}{2}\pi$", r"$\pi$"])
    +    return ax
    +
    +
    +fig = plt.figure(figsize=(3, 5))
    +fig.subplots_adjust(left=0.5, hspace=0.7)
    +
    +ax = setup_axes(fig, 311)
    +ax.set_ylabel("ha=right")
    +ax.set_xlabel("va=baseline")
    +
    +ax = setup_axes(fig, 312)
    +ax.axis["left"].major_ticklabels.set_ha("center")
    +ax.axis["bottom"].major_ticklabels.set_va("top")
    +ax.set_ylabel("ha=center")
    +ax.set_xlabel("va=top")
    +
    +ax = setup_axes(fig, 313)
    +ax.axis["left"].major_ticklabels.set_ha("left")
    +ax.axis["bottom"].major_ticklabels.set_va("bottom")
    +ax.set_ylabel("ha=left")
    +ax.set_xlabel("va=bottom")
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/demo_ticklabel_direction.py b/galleries/examples/axisartist/demo_ticklabel_direction.py
    new file mode 100644
    index 000000000000..2248ea5234d7
    --- /dev/null
    +++ b/galleries/examples/axisartist/demo_ticklabel_direction.py
    @@ -0,0 +1,45 @@
    +"""
    +===================
    +Ticklabel direction
    +===================
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import mpl_toolkits.axisartist.axislines as axislines
    +
    +
    +def setup_axes(fig, pos):
    +    ax = fig.add_subplot(pos, axes_class=axislines.Axes)
    +    ax.set_yticks([0.2, 0.8])
    +    ax.set_xticks([0.2, 0.8])
    +    return ax
    +
    +
    +fig = plt.figure(figsize=(6, 3))
    +fig.subplots_adjust(bottom=0.2)
    +
    +ax = setup_axes(fig, 131)
    +for axis in ax.axis.values():
    +    axis.major_ticks.set_tick_out(True)
    +# or you can simply do "ax.axis[:].major_ticks.set_tick_out(True)"
    +
    +ax = setup_axes(fig, 132)
    +ax.axis["left"].set_axis_direction("right")
    +ax.axis["bottom"].set_axis_direction("top")
    +ax.axis["right"].set_axis_direction("left")
    +ax.axis["top"].set_axis_direction("bottom")
    +
    +ax = setup_axes(fig, 133)
    +ax.axis["left"].set_axis_direction("right")
    +ax.axis[:].major_ticks.set_tick_out(True)
    +
    +ax.axis["left"].label.set_text("Long Label Left")
    +ax.axis["bottom"].label.set_text("Label Bottom")
    +ax.axis["right"].label.set_text("Long Label Right")
    +ax.axis["right"].label.set_visible(True)
    +ax.axis["left"].label.set_pad(0)
    +ax.axis["bottom"].label.set_pad(10)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axis_direction01.py b/galleries/examples/axisartist/simple_axis_direction01.py
    new file mode 100644
    index 000000000000..6c4d1716fa53
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axis_direction01.py
    @@ -0,0 +1,22 @@
    +"""
    +=====================
    +Simple axis direction
    +=====================
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +import mpl_toolkits.axisartist as axisartist
    +
    +fig = plt.figure(figsize=(4, 2.5))
    +ax1 = fig.add_subplot(axes_class=axisartist.Axes)
    +fig.subplots_adjust(right=0.8)
    +
    +ax1.axis["left"].major_ticklabels.set_axis_direction("top")
    +ax1.axis["left"].label.set_text("Left label")
    +
    +ax1.axis["right"].label.set_visible(True)
    +ax1.axis["right"].label.set_text("Right label")
    +ax1.axis["right"].label.set_axis_direction("left")
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axis_direction03.py b/galleries/examples/axisartist/simple_axis_direction03.py
    new file mode 100644
    index 000000000000..b612cf14e119
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axis_direction03.py
    @@ -0,0 +1,38 @@
    +"""
    +==========================================
    +Simple axis tick label and tick directions
    +==========================================
    +
    +First subplot moves the tick labels to inside the spines.
    +Second subplot moves the ticks to inside the spines.
    +These effects can be obtained for a standard Axes by `~.Axes.tick_params`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import mpl_toolkits.axisartist as axisartist
    +
    +
    +def setup_axes(fig, pos):
    +    ax = fig.add_subplot(pos, axes_class=axisartist.Axes)
    +    ax.set_yticks([0.2, 0.8])
    +    ax.set_xticks([0.2, 0.8])
    +    return ax
    +
    +
    +fig = plt.figure(figsize=(5, 2))
    +fig.subplots_adjust(wspace=0.4, bottom=0.3)
    +
    +ax1 = setup_axes(fig, 121)
    +ax1.set_xlabel("ax1 X-label")
    +ax1.set_ylabel("ax1 Y-label")
    +
    +ax1.axis[:].invert_ticklabel_direction()
    +
    +ax2 = setup_axes(fig, 122)
    +ax2.set_xlabel("ax2 X-label")
    +ax2.set_ylabel("ax2 Y-label")
    +
    +ax2.axis[:].major_ticks.set_tick_out(False)
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axis_pad.py b/galleries/examples/axisartist/simple_axis_pad.py
    new file mode 100644
    index 000000000000..f436ae3ab79c
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axis_pad.py
    @@ -0,0 +1,85 @@
    +"""
    +===============
    +Simple axis pad
    +===============
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.projections import PolarAxes
    +from matplotlib.transforms import Affine2D
    +import mpl_toolkits.axisartist as axisartist
    +from mpl_toolkits.axisartist import angle_helper, grid_finder
    +from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
    +
    +
    +def setup_axes(fig, rect):
    +    """Polar projection, but in a rectangular box."""
    +    # see demo_curvelinear_grid.py for details
    +    grid_helper = GridHelperCurveLinear(
    +        Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform(),
    +        extreme_finder=angle_helper.ExtremeFinderCycle(
    +            20, 20,
    +            lon_cycle=360, lat_cycle=None,
    +            lon_minmax=None, lat_minmax=(0, np.inf),
    +        ),
    +        grid_locator1=angle_helper.LocatorDMS(12),
    +        grid_locator2=grid_finder.MaxNLocator(5),
    +        tick_formatter1=angle_helper.FormatterDMS(),
    +    )
    +    ax = fig.add_subplot(
    +        rect, axes_class=axisartist.Axes, grid_helper=grid_helper,
    +        aspect=1, xlim=(-5, 12), ylim=(-5, 10))
    +    ax.axis[:].set_visible(False)
    +    return ax
    +
    +
    +def add_floating_axis1(ax1):
    +    ax1.axis["lat"] = axis = ax1.new_floating_axis(0, 30)
    +    axis.label.set_text(r"$\theta = 30^{\circ}$")
    +    axis.label.set_visible(True)
    +
    +    return axis
    +
    +
    +def add_floating_axis2(ax1):
    +    ax1.axis["lon"] = axis = ax1.new_floating_axis(1, 6)
    +    axis.label.set_text(r"$r = 6$")
    +    axis.label.set_visible(True)
    +
    +    return axis
    +
    +
    +fig = plt.figure(figsize=(9, 3.))
    +fig.subplots_adjust(left=0.01, right=0.99, bottom=0.01, top=0.99,
    +                    wspace=0.01, hspace=0.01)
    +
    +
    +def ann(ax1, d):
    +    ax1.annotate(d, (0.5, 1), (5, -5),
    +                 xycoords="axes fraction", textcoords="offset points",
    +                 va="top", ha="center")
    +
    +
    +ax1 = setup_axes(fig, rect=141)
    +axis = add_floating_axis1(ax1)
    +ann(ax1, r"default")
    +
    +ax1 = setup_axes(fig, rect=142)
    +axis = add_floating_axis1(ax1)
    +axis.major_ticklabels.set_pad(10)
    +ann(ax1, r"ticklabels.set_pad(10)")
    +
    +ax1 = setup_axes(fig, rect=143)
    +axis = add_floating_axis1(ax1)
    +axis.label.set_pad(20)
    +ann(ax1, r"label.set_pad(20)")
    +
    +ax1 = setup_axes(fig, rect=144)
    +axis = add_floating_axis1(ax1)
    +axis.major_ticks.set_tick_out(True)
    +ann(ax1, "ticks.set_tick_out(True)")
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axisartist1.py b/galleries/examples/axisartist/simple_axisartist1.py
    new file mode 100644
    index 000000000000..386347e142a1
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axisartist1.py
    @@ -0,0 +1,52 @@
    +"""
    +=============================
    +Custom spines with axisartist
    +=============================
    +
    +This example showcases the use of :mod:`.axisartist` to draw spines at custom
    +positions (here, at ``y = 0``).
    +
    +Note, however, that it is simpler to achieve this effect using standard
    +`.Spine` methods, as demonstrated in
    +:doc:`/gallery/spines/centered_spines_with_arrows`.
    +
    +.. redirect-from:: /gallery/axisartist/simple_axisline2
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits import axisartist
    +
    +fig = plt.figure(figsize=(6, 3), layout="constrained")
    +# To construct Axes of two different classes, we need to use gridspec (or
    +# MATLAB-style add_subplot calls).
    +gs = fig.add_gridspec(1, 2)
    +
    +
    +ax0 = fig.add_subplot(gs[0, 0], axes_class=axisartist.Axes)
    +# Make a new axis along the first (x) axis which passes through y=0.
    +ax0.axis["y=0"] = ax0.new_floating_axis(nth_coord=0, value=0,
    +                                        axis_direction="bottom")
    +ax0.axis["y=0"].toggle(all=True)
    +ax0.axis["y=0"].label.set_text("y = 0")
    +# Make other axis invisible.
    +ax0.axis["bottom", "top", "right"].set_visible(False)
    +
    +
    +# Alternatively, one can use AxesZero, which automatically sets up two
    +# additional axis, named "xzero" (the y=0 axis) and "yzero" (the x=0 axis).
    +ax1 = fig.add_subplot(gs[0, 1], axes_class=axisartist.axislines.AxesZero)
    +# "xzero" and "yzero" default to invisible; make xzero axis visible.
    +ax1.axis["xzero"].set_visible(True)
    +ax1.axis["xzero"].label.set_text("Axis Zero")
    +# Make other axis invisible.
    +ax1.axis["bottom", "top", "right"].set_visible(False)
    +
    +
    +# Draw some sample data.
    +x = np.arange(0, 2*np.pi, 0.01)
    +ax0.plot(x, np.sin(x))
    +ax1.plot(x, np.sin(x))
    +
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axisline.py b/galleries/examples/axisartist/simple_axisline.py
    new file mode 100644
    index 000000000000..10dab511c62c
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axisline.py
    @@ -0,0 +1,36 @@
    +"""
    +===============
    +Simple Axisline
    +===============
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axisartist.axislines import AxesZero
    +
    +fig = plt.figure()
    +fig.subplots_adjust(right=0.85)
    +ax = fig.add_subplot(axes_class=AxesZero)
    +
    +# make right and top axis invisible
    +ax.axis["right"].set_visible(False)
    +ax.axis["top"].set_visible(False)
    +
    +# make xzero axis (horizontal axis line through y=0) visible.
    +ax.axis["xzero"].set_visible(True)
    +ax.axis["xzero"].label.set_text("Axis Zero")
    +
    +ax.set_ylim(-2, 4)
    +ax.set_xlabel("Label X")
    +ax.set_ylabel("Label Y")
    +# Or:
    +# ax.axis["bottom"].label.set_text("Label X")
    +# ax.axis["left"].label.set_text("Label Y")
    +
    +# make new (right-side) yaxis, but with some offset
    +ax.axis["right2"] = ax.new_fixed_axis(loc="right", offset=(20, 0))
    +ax.axis["right2"].label.set_text("Label Y2")
    +
    +ax.plot([-2, 3, 2])
    +plt.show()
    diff --git a/galleries/examples/axisartist/simple_axisline3.py b/galleries/examples/axisartist/simple_axisline3.py
    new file mode 100644
    index 000000000000..9323674ff25a
    --- /dev/null
    +++ b/galleries/examples/axisartist/simple_axisline3.py
    @@ -0,0 +1,18 @@
    +"""
    +================
    +Simple Axisline3
    +================
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.axisartist.axislines import Axes
    +
    +fig = plt.figure(figsize=(3, 3))
    +
    +ax = fig.add_subplot(axes_class=Axes)
    +
    +ax.axis["right"].set_visible(False)
    +ax.axis["top"].set_visible(False)
    +
    +plt.show()
    diff --git a/galleries/examples/color/README.txt b/galleries/examples/color/README.txt
    new file mode 100644
    index 000000000000..4b8b3bc4b751
    --- /dev/null
    +++ b/galleries/examples/color/README.txt
    @@ -0,0 +1,7 @@
    +.. _color_examples:
    +
    +Color
    +=====
    +
    +For a description of the colormaps available in Matplotlib,
    +see the :ref:`colormaps tutorial `.
    diff --git a/galleries/examples/color/color_by_yvalue.py b/galleries/examples/color/color_by_yvalue.py
    new file mode 100644
    index 000000000000..193f840db39e
    --- /dev/null
    +++ b/galleries/examples/color/color_by_yvalue.py
    @@ -0,0 +1,39 @@
    +"""
    +================
    +Color by y-value
    +================
    +
    +Use masked arrays to plot a line with different colors by y-value.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2.0, 0.01)
    +s = np.sin(2 * np.pi * t)
    +
    +upper = 0.77
    +lower = -0.77
    +
    +supper = np.ma.masked_where(s < upper, s)
    +slower = np.ma.masked_where(s > lower, s)
    +smiddle = np.ma.masked_where((s < lower) | (s > upper), s)
    +
    +fig, ax = plt.subplots()
    +ax.plot(t, smiddle, t, slower, t, supper)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    styling: conditional
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/color/color_cycle_default.py b/galleries/examples/color/color_cycle_default.py
    new file mode 100644
    index 000000000000..af35f6d00f9e
    --- /dev/null
    +++ b/galleries/examples/color/color_cycle_default.py
    @@ -0,0 +1,54 @@
    +"""
    +====================================
    +Colors in the default property cycle
    +====================================
    +
    +Display the colors from the default prop_cycle, which is obtained from the
    +:ref:`rc parameters`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.colors import TABLEAU_COLORS, same_color
    +
    +
    +def f(x, a):
    +    """A nice sigmoid-like parametrized curve, ending approximately at *a*."""
    +    return 0.85 * a * (1 / (1 + np.exp(-x)) + 0.2)
    +
    +
    +fig, ax = plt.subplots()
    +ax.axis('off')
    +ax.set_title("Colors in the default property cycle")
    +
    +prop_cycle = plt.rcParams['axes.prop_cycle']
    +colors = prop_cycle.by_key()['color']
    +x = np.linspace(-4, 4, 200)
    +
    +for i, (color, color_name) in enumerate(zip(colors, TABLEAU_COLORS)):
    +    assert same_color(color, color_name)
    +    pos = 4.5 - i
    +    ax.plot(x, f(x, pos))
    +    ax.text(4.2, pos, f"'C{i}': '{color_name}'", color=color, va="center")
    +    ax.bar(9, 1, width=1.5, bottom=pos-0.5)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.axis`
    +#    - `matplotlib.axes.Axes.text`
    +#    - `matplotlib.colors.same_color`
    +#    - `cycler.Cycler`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    purpose: reference
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/color/color_demo.py b/galleries/examples/color/color_demo.py
    new file mode 100644
    index 000000000000..b8b06c9091e3
    --- /dev/null
    +++ b/galleries/examples/color/color_demo.py
    @@ -0,0 +1,83 @@
    +"""
    +==========
    +Color Demo
    +==========
    +
    +Matplotlib recognizes the following formats to specify a color:
    +
    +1) an RGB or RGBA tuple of float values in ``[0, 1]`` (e.g. ``(0.1, 0.2, 0.5)``
    +   or ``(0.1, 0.2, 0.5, 0.3)``).  RGBA is short for Red, Green, Blue, Alpha;
    +2) a hex RGB or RGBA string (e.g., ``'#0F0F0F'`` or ``'#0F0F0F0F'``);
    +3) a shorthand hex RGB or RGBA string, equivalent to the hex RGB or RGBA
    +   string obtained by duplicating each character, (e.g., ``'#abc'``, equivalent
    +   to ``'#aabbcc'``, or ``'#abcd'``, equivalent to ``'#aabbccdd'``);
    +4) a string representation of a float value in ``[0, 1]`` inclusive for gray
    +   level (e.g., ``'0.5'``);
    +5) a single letter string, i.e. one of
    +   ``{'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'}``, which are short-hand notations
    +   for shades of blue, green, red, cyan, magenta, yellow, black, and white;
    +6) an X11/CSS4 ("html") color name, e.g. ``"blue"``;
    +7) a name from the `xkcd color survey `__,
    +   prefixed with ``'xkcd:'`` (e.g., ``'xkcd:sky blue'``);
    +8) a "Cn" color spec, i.e. ``'C'`` followed by a number, which is an index into
    +   the default property cycle (:rc:`axes.prop_cycle`); the indexing is intended
    +   to occur at rendering time, and defaults to black if the cycle does not
    +   include color.
    +9) one of ``{'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple',
    +   'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan'}`` which are
    +   the Tableau Colors from the 'tab10' categorical palette (which is the
    +   default color cycle);
    +
    +For more information on colors in matplotlib see
    +
    +* the :ref:`colors_def` tutorial;
    +* the `matplotlib.colors` API;
    +* the :doc:`/gallery/color/named_colors` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.linspace(0.0, 2.0, 201)
    +s = np.sin(2 * np.pi * t)
    +
    +# 1) RGB tuple:
    +fig, ax = plt.subplots(facecolor=(.18, .31, .31))
    +# 2) hex string:
    +ax.set_facecolor('#eafff5')
    +# 3) gray level string:
    +ax.set_title('Voltage vs. time chart', color='0.7')
    +# 4) single letter color string
    +ax.set_xlabel('Time [s]', color='c')
    +# 5) a named color:
    +ax.set_ylabel('Voltage [mV]', color='peachpuff')
    +# 6) a named xkcd color:
    +ax.plot(t, s, 'xkcd:crimson')
    +# 7) Cn notation:
    +ax.plot(t, .7*s, color='C4', linestyle='--')
    +# 8) tab notation:
    +ax.tick_params(labelcolor='tab:orange')
    +
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors`
    +#    - `matplotlib.axes.Axes.plot`
    +#    - `matplotlib.axes.Axes.set_facecolor`
    +#    - `matplotlib.axes.Axes.set_title`
    +#    - `matplotlib.axes.Axes.set_xlabel`
    +#    - `matplotlib.axes.Axes.set_ylabel`
    +#    - `matplotlib.axes.Axes.tick_params`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/color/color_sequences.py b/galleries/examples/color/color_sequences.py
    new file mode 100644
    index 000000000000..4d7324b55a12
    --- /dev/null
    +++ b/galleries/examples/color/color_sequences.py
    @@ -0,0 +1,66 @@
    +"""
    +=====================
    +Named color sequences
    +=====================
    +
    +Matplotlib's `~matplotlib.colors.ColorSequenceRegistry` allows access to
    +predefined lists of colors by name e.g.
    +``colors = matplotlib.color_sequences['Set1']``.  This example shows all of the
    +built in color sequences.
    +
    +User-defined sequences can be added via `.ColorSequenceRegistry.register`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib as mpl
    +
    +
    +def plot_color_sequences(names, ax):
    +    # Display each named color sequence horizontally on the supplied axes.
    +
    +    for n, name in enumerate(names):
    +        colors = mpl.color_sequences[name]
    +        n_colors = len(colors)
    +        x = np.arange(n_colors)
    +        y = np.full_like(x, n)
    +
    +        ax.scatter(x, y, facecolor=colors, edgecolor='dimgray', s=200, zorder=2)
    +
    +    ax.set_yticks(range(len(names)), labels=names)
    +    ax.grid(visible=True, axis='y')
    +    ax.yaxis.set_inverted(True)
    +    ax.xaxis.set_visible(False)
    +    ax.spines[:].set_visible(False)
    +    ax.tick_params(left=False)
    +
    +
    +built_in_color_sequences = [
    +    'tab10', 'tab20', 'tab20b', 'tab20c', 'Pastel1', 'Pastel2', 'Paired',
    +    'Accent', 'okabe_ito', 'Dark2', 'Set1', 'Set2', 'Set3', 'petroff6',
    +    'petroff8', 'petroff10']
    +
    +
    +fig, ax = plt.subplots(figsize=(6.4, 9.6), layout='constrained')
    +
    +plot_color_sequences(built_in_color_sequences, ax)
    +ax.set_title('Built In Color Sequences')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors.ColorSequenceRegistry`
    +#    - `matplotlib.axes.Axes.scatter`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    purpose: reference
    diff --git a/galleries/examples/color/colorbar_basics.py b/galleries/examples/color/colorbar_basics.py
    new file mode 100644
    index 000000000000..8a35f8ac2b68
    --- /dev/null
    +++ b/galleries/examples/color/colorbar_basics.py
    @@ -0,0 +1,65 @@
    +"""
    +========
    +Colorbar
    +========
    +
    +Use `~.Figure.colorbar` by specifying the mappable object (here
    +the `.AxesImage` returned by `~.axes.Axes.imshow`)
    +and the Axes to attach the colorbar to.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# setup some generic data
    +N = 37
    +x, y = np.mgrid[:N, :N]
    +Z = (np.cos(x*0.2) + np.sin(y*0.3))
    +
    +# mask out the negative and positive values, respectively
    +Zpos = np.ma.masked_less(Z, 0)
    +Zneg = np.ma.masked_greater(Z, 0)
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(figsize=(13, 3), ncols=3)
    +
    +# plot just the positive data and save the
    +# color "mappable" object returned by ax1.imshow
    +pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')
    +
    +# add the colorbar using the figure's method,
    +# telling which mappable we're talking about and
    +# which Axes object it should be near
    +fig.colorbar(pos, ax=ax1)
    +
    +# repeat everything above for the negative data
    +# you can specify location, anchor and shrink the colorbar
    +neg = ax2.imshow(Zneg, cmap='Reds_r', interpolation='none')
    +fig.colorbar(neg, ax=ax2, location='right', anchor=(0, 0.3), shrink=0.7)
    +
    +# Plot both positive and negative values between +/- 1.2
    +pos_neg_clipped = ax3.imshow(Z, cmap='RdBu', vmin=-1.2, vmax=1.2,
    +                             interpolation='none')
    +# Add minorticks on the colorbar to make it easy to read the
    +# values off the colorbar.
    +cbar = fig.colorbar(pos_neg_clipped, ax=ax3, extend='both')
    +cbar.minorticks_on()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colorbar.Colorbar.minorticks_on`
    +#    - `matplotlib.colorbar.Colorbar.minorticks_off`
    +#
    +# .. tags::
    +#
    +#    component: colorbar
    +#    styling: color
    +#    plot-type: imshow
    +#    level: beginner
    diff --git a/galleries/examples/color/colorbar_histogram.py b/galleries/examples/color/colorbar_histogram.py
    new file mode 100644
    index 000000000000..4a1a07e265a2
    --- /dev/null
    +++ b/galleries/examples/color/colorbar_histogram.py
    @@ -0,0 +1,48 @@
    +"""
    +=====================
    +Histogram as colorbar
    +=====================
    +
    +This example demonstrates how to use a colored histogram instead of a colorbar
    +to not only show the color-to-value mapping, but also visualize the
    +distribution of values.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors as mcolors
    +
    +# surface data
    +delta = 0.025
    +x = y = np.arange(-2.0, 2.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-(((X + 1) * 1.3) ** 2) - ((Y + 1) * 1.3) ** 2)
    +Z2 = 2.5 * np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
    +Z = Z1**0.25 - Z2**0.5
    +
    +# colormap & normalization
    +bins = 30
    +cmap = plt.get_cmap("RdYlBu_r")
    +bin_edges = np.linspace(Z.min(), Z.max(), bins + 1)
    +norm = mcolors.BoundaryNorm(bin_edges, cmap.N)
    +
    +# main plot
    +fig, ax = plt.subplots(layout="constrained")
    +im = ax.imshow(Z, cmap=cmap, origin="lower", extent=[-3, 3, -3, 3], norm=norm)
    +
    +# inset histogram
    +cax = ax.inset_axes([1.18, 0.02, 0.25, 0.95])  # left, bottom, width, height
    +
    +# plot histogram
    +counts, _ = np.histogram(Z, bins=bin_edges)
    +midpoints = (bin_edges[:-1] + bin_edges[1:]) / 2
    +distance = midpoints[1] - midpoints[0]
    +cax.barh(midpoints, counts, height=0.8 * distance, color=cmap(norm(midpoints)))
    +
    +# styling
    +cax.spines[:].set_visible(False)
    +cax.set_yticks(bin_edges)
    +cax.tick_params(axis="both", which="both", length=0)
    +
    +plt.show()
    diff --git a/galleries/examples/color/colormap_reference.py b/galleries/examples/color/colormap_reference.py
    new file mode 100644
    index 000000000000..eedf6ec11737
    --- /dev/null
    +++ b/galleries/examples/color/colormap_reference.py
    @@ -0,0 +1,117 @@
    +"""
    +==================
    +Colormap reference
    +==================
    +
    +Reference for colormaps included with Matplotlib.
    +
    +A reversed version of each of these colormaps is available by appending
    +``_r`` to the name, as shown in :ref:`reverse-cmap`.
    +
    +See :ref:`colormaps` for an in-depth discussion about
    +colormaps, including colorblind-friendliness, and
    +:ref:`colormap-manipulation` for a guide to creating
    +colormaps.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +cmaps = [('Perceptually Uniform Sequential', [
    +            'viridis', 'plasma', 'inferno', 'magma', 'cividis']),
    +         ('Sequential', [
    +            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
    +            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
    +            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']),
    +         ('Sequential (2)', [
    +            'gray', 'bone', 'pink', 'spring', 'summer', 'autumn', 'winter',
    +            'cool', 'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper']),
    +         ('Diverging', [
    +            'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
    +            'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic',
    +            'berlin', 'managua', 'vanimo']),
    +         ('Cyclic', ['twilight', 'twilight_shifted', 'hsv']),
    +         ('Qualitative', [
    +            'Pastel1', 'Pastel2', 'Paired', 'Accent', 'okabe_ito',
    +            'Dark2', 'Set1', 'Set2', 'Set3',
    +            'tab10', 'tab20', 'tab20b', 'tab20c']),
    +         ('Miscellaneous', [
    +            'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
    +            'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
    +            'gist_rainbow', 'rainbow', 'jet', 'turbo', 'nipy_spectral',
    +            'gist_ncar'])]
    +
    +gradient = np.linspace(0, 1, 256)
    +gradient = np.vstack((gradient, gradient))
    +
    +
    +def plot_color_gradients(cmap_category, cmap_list):
    +    # Create figure and adjust figure height to number of colormaps
    +    nrows = len(cmap_list)
    +    figh = 0.35 + 0.15 + (nrows + (nrows-1)*0.1)*0.22
    +    fig, axs = plt.subplots(nrows=nrows, figsize=(6.4, figh))
    +    fig.subplots_adjust(top=1-.35/figh, bottom=.15/figh, left=0.2, right=0.99)
    +
    +    axs[0].set_title(f"{cmap_category} colormaps", fontsize=14)
    +
    +    for ax, cmap_name in zip(axs, cmap_list):
    +        ax.imshow(gradient, aspect='auto', cmap=cmap_name)
    +        ax.text(-.01, .5, cmap_name, va='center', ha='right', fontsize=10,
    +                transform=ax.transAxes)
    +
    +    # Turn off *all* ticks & spines, not just the ones with colormaps.
    +    for ax in axs:
    +        ax.set_axis_off()
    +
    +
    +for cmap_category, cmap_list in cmaps:
    +    plot_color_gradients(cmap_category, cmap_list)
    +
    +
    +# %%
    +#
    +# .. admonition:: Discouraged
    +#
    +#    For backward compatibility we additionally support the following colormap
    +#    names, which are identical to other builtin colormaps. Their use is
    +#    discouraged. Use the suggested replacement instead.
    +#
    +#    =========  =================================
    +#    Colormap   Use identical replacement instead
    +#    =========  =================================
    +#    gist_gray 	gray
    +#    gist_yarg 	gray_r
    +#    binary 	gray_r
    +#    =========  =================================
    +#
    +#
    +# .. _reverse-cmap:
    +#
    +# Reversed colormaps
    +# ------------------
    +#
    +# Append ``_r`` to the name of any built-in colormap to get the reversed
    +# version:
    +
    +plot_color_gradients("Original and reversed ", ['viridis', 'viridis_r'])
    +
    +# %%
    +# The built-in reversed colormaps are generated using `.Colormap.reversed`.
    +# For an example, see :ref:`reversing-colormap`
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors`
    +#    - `matplotlib.axes.Axes.imshow`
    +#    - `matplotlib.figure.Figure.text`
    +#    - `matplotlib.axes.Axes.set_axis_off`
    +#
    +# .. tags::
    +#
    +#    styling: colormap
    +#    purpose: reference
    diff --git a/galleries/examples/color/custom_cmap.py b/galleries/examples/color/custom_cmap.py
    new file mode 100644
    index 000000000000..616ab9f279fd
    --- /dev/null
    +++ b/galleries/examples/color/custom_cmap.py
    @@ -0,0 +1,288 @@
    +"""
    +=======================================
    +Create a colormap from a list of colors
    +=======================================
    +
    +For more detail on creating and manipulating colormaps see
    +:ref:`colormap-manipulation`.
    +
    +Creating a :ref:`colormap ` from a list of colors
    +can be done with the `.LinearSegmentedColormap.from_list` method.  You must
    +pass a list of RGB tuples that define the mixture of colors from 0 to 1.
    +
    +
    +Creating custom colormaps
    +=========================
    +It is also possible to create a custom mapping for a colormap. This is
    +accomplished by creating dictionary that specifies how the RGB channels
    +change from one end of the cmap to the other.
    +
    +Example: suppose you want red to increase from 0 to 1 over the bottom
    +half, green to do the same over the middle half, and blue over the top
    +half.  Then you would use::
    +
    +    cdict = {
    +        'red': (
    +            (0.0,  0.0, 0.0),
    +            (0.5,  1.0, 1.0),
    +            (1.0,  1.0, 1.0),
    +        ),
    +        'green': (
    +            (0.0,  0.0, 0.0),
    +            (0.25, 0.0, 0.0),
    +            (0.75, 1.0, 1.0),
    +            (1.0,  1.0, 1.0),
    +        ),
    +        'blue': (
    +            (0.0,  0.0, 0.0),
    +            (0.5,  0.0, 0.0),
    +            (1.0,  1.0, 1.0),
    +        )
    +    }
    +
    +If, as in this example, there are no discontinuities in the r, g, and b
    +components, then it is quite simple: the second and third element of
    +each tuple, above, is the same -- call it "``y``".  The first element ("``x``")
    +defines interpolation intervals over the full range of 0 to 1, and it
    +must span that whole range.  In other words, the values of ``x`` divide the
    +0-to-1 range into a set of segments, and ``y`` gives the end-point color
    +values for each segment.
    +
    +Now consider the green, ``cdict['green']`` is saying that for:
    +
    +- 0 <= ``x`` <= 0.25, ``y`` is zero; no green.
    +- 0.25 < ``x`` <= 0.75, ``y`` varies linearly from 0 to 1.
    +- 0.75 < ``x`` <= 1, ``y`` remains at 1, full green.
    +
    +If there are discontinuities, then it is a little more complicated. Label the 3
    +elements in each row in the ``cdict`` entry for a given color as ``(x, y0,
    +y1)``. Then for values of ``x`` between ``x[i]`` and ``x[i+1]`` the color value
    +is interpolated between ``y1[i]`` and ``y0[i+1]``.
    +
    +Going back to a cookbook example::
    +
    +    cdict = {
    +        'red': (
    +            (0.0,  0.0, 0.0),
    +            (0.5,  1.0, 0.7),
    +            (1.0,  1.0, 1.0),
    +        ),
    +        'green': (
    +            (0.0,  0.0, 0.0),
    +            (0.5,  1.0, 0.0),
    +            (1.0,  1.0, 1.0),
    +        ),
    +        'blue': (
    +            (0.0,  0.0, 0.0),
    +            (0.5,  0.0, 0.0),
    +            (1.0,  1.0, 1.0),
    +        )
    +    }
    +
    +and look at ``cdict['red'][1]``; because ``y0 != y1``, it is saying that for
    +``x`` from 0 to 0.5, red increases from 0 to 1, but then it jumps down, so that
    +for ``x`` from 0.5 to 1, red increases from 0.7 to 1.  Green ramps from 0 to 1
    +as ``x`` goes from 0 to 0.5, then jumps back to 0, and ramps back to 1 as ``x``
    +goes from 0.5 to 1. ::
    +
    +  row i:   x  y0  y1
    +                 /
    +                /
    +  row i+1: x  y0  y1
    +
    +Above is an attempt to show that for ``x`` in the range ``x[i]`` to ``x[i+1]``,
    +the interpolation is between ``y1[i]`` and ``y0[i+1]``.  So, ``y0[0]`` and
    +``y1[-1]`` are never used.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib as mpl
    +from matplotlib.colors import LinearSegmentedColormap
    +
    +# Make some illustrative fake data:
    +
    +x = np.arange(0, np.pi, 0.1)
    +y = np.arange(0, 2 * np.pi, 0.1)
    +X, Y = np.meshgrid(x, y)
    +Z = np.cos(X) * np.sin(Y) * 10
    +
    +
    +# %%
    +# Colormaps from a list
    +# ---------------------
    +
    +colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]  # R -> G -> B
    +n_bins = [3, 6, 10, 100]  # Discretizes the interpolation into bins
    +cmap_name = 'my_list'
    +fig, axs = plt.subplots(2, 2, figsize=(6, 9))
    +fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
    +for n_bin, ax in zip(n_bins, axs.flat):
    +    # Create the colormap
    +    cmap = LinearSegmentedColormap.from_list(cmap_name, colors, N=n_bin)
    +    # Fewer bins will result in "coarser" colomap interpolation
    +    im = ax.imshow(Z, origin='lower', cmap=cmap)
    +    ax.set_title("N bins: %s" % n_bin)
    +    fig.colorbar(im, ax=ax)
    +
    +
    +# %%
    +# Custom colormaps
    +# ----------------
    +
    +cdict1 = {
    +    'red': (
    +        (0.0, 0.0, 0.0),
    +        (0.5, 0.0, 0.1),
    +        (1.0, 1.0, 1.0),
    +    ),
    +    'green': (
    +        (0.0, 0.0, 0.0),
    +        (1.0, 0.0, 0.0),
    +    ),
    +    'blue': (
    +        (0.0, 0.0, 1.0),
    +        (0.5, 0.1, 0.0),
    +        (1.0, 0.0, 0.0),
    +    )
    +}
    +
    +cdict2 = {
    +    'red': (
    +        (0.0, 0.0, 0.0),
    +        (0.5, 0.0, 1.0),
    +        (1.0, 0.1, 1.0),
    +    ),
    +    'green': (
    +        (0.0, 0.0, 0.0),
    +        (1.0, 0.0, 0.0),
    +    ),
    +    'blue': (
    +        (0.0, 0.0, 0.1),
    +        (0.5, 1.0, 0.0),
    +        (1.0, 0.0, 0.0),
    +    )
    +}
    +
    +cdict3 = {
    +    'red': (
    +        (0.0, 0.0, 0.0),
    +        (0.25, 0.0, 0.0),
    +        (0.5, 0.8, 1.0),
    +        (0.75, 1.0, 1.0),
    +        (1.0, 0.4, 1.0),
    +    ),
    +    'green': (
    +        (0.0, 0.0, 0.0),
    +        (0.25, 0.0, 0.0),
    +        (0.5, 0.9, 0.9),
    +        (0.75, 0.0, 0.0),
    +        (1.0, 0.0, 0.0),
    +    ),
    +    'blue': (
    +        (0.0, 0.0, 0.4),
    +        (0.25, 1.0, 1.0),
    +        (0.5, 1.0, 0.8),
    +        (0.75, 0.0, 0.0),
    +        (1.0, 0.0, 0.0),
    +    )
    +}
    +
    +# Make a modified version of cdict3 with some transparency
    +# in the middle of the range.
    +cdict4 = {
    +    **cdict3,
    +    'alpha': (
    +        (0.0, 1.0, 1.0),
    +        # (0.25, 1.0, 1.0),
    +        (0.5, 0.3, 0.3),
    +        # (0.75, 1.0, 1.0),
    +        (1.0, 1.0, 1.0),
    +    ),
    +}
    +
    +
    +# %%
    +# Now we will use this example to illustrate 2 ways of
    +# handling custom colormaps.
    +# First, the most direct and explicit:
    +
    +blue_red1 = LinearSegmentedColormap('BlueRed1', cdict1)
    +
    +# %%
    +# Second, create the map explicitly and register it.
    +# Like the first method, this method works with any kind
    +# of Colormap, not just
    +# a LinearSegmentedColormap:
    +
    +mpl.colormaps.register(LinearSegmentedColormap('BlueRed2', cdict2))
    +mpl.colormaps.register(LinearSegmentedColormap('BlueRed3', cdict3))
    +mpl.colormaps.register(LinearSegmentedColormap('BlueRedAlpha', cdict4))
    +
    +# %%
    +# Make the figure, with 4 subplots:
    +
    +fig, axs = plt.subplots(2, 2, figsize=(6, 9))
    +fig.subplots_adjust(left=0.02, bottom=0.06, right=0.95, top=0.94, wspace=0.05)
    +
    +im1 = axs[0, 0].imshow(Z, cmap=blue_red1)
    +fig.colorbar(im1, ax=axs[0, 0])
    +
    +im2 = axs[1, 0].imshow(Z, cmap='BlueRed2')
    +fig.colorbar(im2, ax=axs[1, 0])
    +
    +# Now we will set the third cmap as the default.  One would
    +# not normally do this in the middle of a script like this;
    +# it is done here just to illustrate the method.
    +
    +plt.rcParams['image.cmap'] = 'BlueRed3'
    +
    +im3 = axs[0, 1].imshow(Z)
    +fig.colorbar(im3, ax=axs[0, 1])
    +axs[0, 1].set_title("Alpha = 1")
    +
    +# Or as yet another variation, we can replace the rcParams
    +# specification *before* the imshow with the following *after*
    +# imshow.
    +# This sets the new default *and* sets the colormap of the last
    +# image-like item plotted via pyplot, if any.
    +#
    +
    +# Draw a line with low zorder so it will be behind the image.
    +axs[1, 1].plot([0, 10 * np.pi], [0, 20 * np.pi], color='c', lw=20, zorder=-1)
    +
    +im4 = axs[1, 1].imshow(Z)
    +fig.colorbar(im4, ax=axs[1, 1])
    +
    +# Here it is: changing the colormap for the current image and its
    +# colorbar after they have been plotted.
    +im4.set_cmap('BlueRedAlpha')
    +axs[1, 1].set_title("Varying alpha")
    +
    +fig.suptitle('Custom Blue-Red colormaps', fontsize=16)
    +fig.subplots_adjust(top=0.9)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors`
    +#    - `matplotlib.colors.LinearSegmentedColormap`
    +#    - `matplotlib.colors.LinearSegmentedColormap.from_list`
    +#    - `matplotlib.cm`
    +#    - `matplotlib.cm.ScalarMappable.set_cmap`
    +#    - `matplotlib.cm.ColormapRegistry.register`
    +#
    +# .. tags::
    +#
    +#    styling: colormap
    +#    plot-type: imshow
    +#    level: intermediate
    diff --git a/galleries/examples/color/individual_colors_from_cmap.py b/galleries/examples/color/individual_colors_from_cmap.py
    new file mode 100644
    index 000000000000..1b0c4382c7d4
    --- /dev/null
    +++ b/galleries/examples/color/individual_colors_from_cmap.py
    @@ -0,0 +1,74 @@
    +"""
    +===========================================
    +Selecting individual colors from a colormap
    +===========================================
    +
    +Sometimes we want to use more colors or a different set of colors than the default color
    +cycle provides. Selecting individual colors from one of the provided colormaps can be a
    +convenient way to do this.
    +
    +We can retrieve colors from any `.Colormap` by calling it with a float or a list of
    +floats in the range [0, 1]; e.g. ``cmap(0.5)`` will give the middle color. See also
    +`.Colormap.__call__`.
    +
    +Extracting colors from a continuous colormap
    +--------------------------------------------
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib as mpl
    +
    +n_lines = 21
    +cmap = mpl.colormaps['plasma']
    +
    +# Take colors at regular intervals spanning the colormap.
    +colors = cmap(np.linspace(0, 1, n_lines))
    +
    +fig, ax = plt.subplots(layout='constrained')
    +
    +for i, color in enumerate(colors):
    +    ax.plot([0, i], color=color)
    +
    +plt.show()
    +
    +# %%
    +#
    +# Extracting colors from a discrete colormap
    +# ------------------------------------------
    +# The list of all colors in a `.ListedColormap` is available as the ``colors``
    +# attribute.  Note that all the colors from Matplotlib's qualitative color maps
    +# are also available as color sequences, so may be accessed more directly from
    +# the color sequence registry.  See :doc:`/gallery/color/color_sequences`.
    +
    +colors = mpl.colormaps['Dark2'].colors
    +
    +fig, ax = plt.subplots(layout='constrained')
    +
    +for i, color in enumerate(colors):
    +    ax.plot([0, i], color=color)
    +
    +plt.show()
    +
    +# %%
    +# See Also
    +# --------
    +#
    +# For more details about manipulating colormaps, see :ref:`colormap-manipulation`.  To
    +# change the default color cycle, see :ref:`color_cycle`.
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors.Colormap`
    +#    - `matplotlib.colors.Colormap.resampled`
    +#
    +# .. tags::
    +#
    +#    styling: colormap
    +#    styling: color
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/color/named_colors.py b/galleries/examples/color/named_colors.py
    new file mode 100644
    index 000000000000..a5bcf00cb0cb
    --- /dev/null
    +++ b/galleries/examples/color/named_colors.py
    @@ -0,0 +1,128 @@
    +"""
    +====================
    +List of named colors
    +====================
    +
    +This plots a list of the named colors supported by Matplotlib.
    +For more information on colors in matplotlib see
    +
    +* the :ref:`colors_def` tutorial;
    +* the `matplotlib.colors` API;
    +* the :doc:`/gallery/color/color_demo`.
    +
    +----------------------------
    +Helper Function for Plotting
    +----------------------------
    +First we define a helper function for making a table of colors, then we use it
    +on some common color categories.
    +"""
    +
    +import math
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.colors as mcolors
    +from matplotlib.patches import Rectangle
    +
    +
    +def plot_colortable(colors, *, ncols=4, sort_colors=True):
    +
    +    cell_width = 212
    +    cell_height = 22
    +    swatch_width = 48
    +    margin = 12
    +
    +    # Sort colors by hue, saturation, value and name.
    +    if sort_colors is True:
    +        names = sorted(
    +            colors, key=lambda c: tuple(mcolors.rgb_to_hsv(mcolors.to_rgb(c))))
    +    else:
    +        names = list(colors)
    +
    +    n = len(names)
    +    nrows = math.ceil(n / ncols)
    +
    +    width = cell_width * ncols + 2 * margin
    +    height = cell_height * nrows + 2 * margin
    +    dpi = 72
    +
    +    fig, ax = plt.subplots(figsize=(width / dpi, height / dpi), dpi=dpi)
    +    fig.subplots_adjust(margin/width, margin/height,
    +                        (width-margin)/width, (height-margin)/height)
    +    ax.set_xlim(0, cell_width * ncols)
    +    ax.set_ylim(cell_height * (nrows-0.5), -cell_height/2.)
    +    ax.yaxis.set_visible(False)
    +    ax.xaxis.set_visible(False)
    +    ax.set_axis_off()
    +
    +    for i, name in enumerate(names):
    +        row = i % nrows
    +        col = i // nrows
    +        y = row * cell_height
    +
    +        swatch_start_x = cell_width * col
    +        text_pos_x = cell_width * col + swatch_width + 7
    +
    +        ax.text(text_pos_x, y, name, fontsize=14,
    +                horizontalalignment='left',
    +                verticalalignment='center')
    +
    +        ax.add_patch(
    +            Rectangle(xy=(swatch_start_x, y-9), width=swatch_width,
    +                      height=18, facecolor=colors[name], edgecolor='0.7')
    +        )
    +
    +    return fig
    +
    +# %%
    +# -----------
    +# Base colors
    +# -----------
    +
    +plot_colortable(mcolors.BASE_COLORS, ncols=3, sort_colors=False)
    +
    +# %%
    +# ---------------
    +# Tableau Palette
    +# ---------------
    +
    +plot_colortable(mcolors.TABLEAU_COLORS, ncols=2, sort_colors=False)
    +
    +# %%
    +# ----------
    +# CSS Colors
    +# ----------
    +
    +# sphinx_gallery_thumbnail_number = 3
    +plot_colortable(mcolors.CSS4_COLORS)
    +plt.show()
    +
    +# %%
    +# -----------
    +# XKCD Colors
    +# -----------
    +# Matplotlib supports colors from the
    +# `xkcd color survey `_, e.g. ``"xkcd:sky blue"``. Since
    +# this contains almost 1000 colors, a figure of this would be very large and is thus
    +# omitted here. You can use the following code to generate the overview yourself ::
    +#
    +#     xkcd_fig = plot_colortable(mcolors.XKCD_COLORS)
    +#     xkcd_fig.savefig("XKCD_Colors.png")
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors`
    +#    - `matplotlib.colors.rgb_to_hsv`
    +#    - `matplotlib.colors.to_rgba`
    +#    - `matplotlib.figure.Figure.get_size_inches`
    +#    - `matplotlib.figure.Figure.subplots_adjust`
    +#    - `matplotlib.axes.Axes.text`
    +#    - `matplotlib.patches.Rectangle`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    purpose: reference
    diff --git a/galleries/examples/color/set_alpha.py b/galleries/examples/color/set_alpha.py
    new file mode 100644
    index 000000000000..b8ba559f5f4a
    --- /dev/null
    +++ b/galleries/examples/color/set_alpha.py
    @@ -0,0 +1,59 @@
    +"""
    +=================================
    +Ways to set a color's alpha value
    +=================================
    +
    +Compare setting alpha by the *alpha* keyword argument and by one of the Matplotlib color
    +formats. Often, the *alpha* keyword is the only tool needed to add transparency to a
    +color. In some cases, the *(matplotlib_color, alpha)* color format provides an easy way
    +to fine-tune the appearance of a Figure.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility.
    +np.random.seed(19680801)
    +
    +fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4))
    +
    +x_values = [n for n in range(20)]
    +y_values = np.random.randn(20)
    +
    +facecolors = ['green' if y > 0 else 'red' for y in y_values]
    +edgecolors = facecolors
    +
    +ax1.bar(x_values, y_values, color=facecolors, edgecolor=edgecolors, alpha=0.5)
    +ax1.set_title("Explicit 'alpha' keyword value\nshared by all bars and edges")
    +
    +
    +# Normalize y values to get distinct face alpha values.
    +abs_y = [abs(y) for y in y_values]
    +face_alphas = [n / max(abs_y) for n in abs_y]
    +edge_alphas = [1 - alpha for alpha in face_alphas]
    +
    +colors_with_alphas = list(zip(facecolors, face_alphas))
    +edgecolors_with_alphas = list(zip(edgecolors, edge_alphas))
    +
    +ax2.bar(x_values, y_values, color=colors_with_alphas,
    +        edgecolor=edgecolors_with_alphas)
    +ax2.set_title('Normalized alphas for\neach bar and each edge')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bar`
    +#    - `matplotlib.pyplot.subplots`
    +#
    +# .. tags::
    +#
    +#    styling: color
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/event_handling/README.txt b/galleries/examples/event_handling/README.txt
    new file mode 100644
    index 000000000000..44aa9cf3ac6a
    --- /dev/null
    +++ b/galleries/examples/event_handling/README.txt
    @@ -0,0 +1,13 @@
    +.. _event_handling_examples:
    +
    +Event handling
    +==============
    +
    +Matplotlib supports :ref:`event handling ` with
    +a GUI neutral event model, so you can connect to Matplotlib events without
    +knowledge of what user interface Matplotlib will ultimately be plugged in to.
    +This has two advantages: the code you write will be more portable, and
    +Matplotlib events are aware of things like data coordinate space and which
    +axes the event occurs in so you don't have to mess with low level
    +transformation details to go from canvas space to data space.  Object picking
    +examples are also included.
    diff --git a/galleries/examples/event_handling/close_event.py b/galleries/examples/event_handling/close_event.py
    new file mode 100644
    index 000000000000..060388269c8c
    --- /dev/null
    +++ b/galleries/examples/event_handling/close_event.py
    @@ -0,0 +1,26 @@
    +"""
    +===========
    +Close event
    +===========
    +
    +Example to show connecting events that occur when the figure closes.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import matplotlib.pyplot as plt
    +
    +
    +def on_close(event):
    +    print('Closed Figure!')
    +
    +fig = plt.figure()
    +fig.canvas.mpl_connect('close_event', on_close)
    +
    +plt.text(0.35, 0.5, 'Close Me!', dict(size=30))
    +plt.show()
    diff --git a/galleries/examples/event_handling/coords_demo.py b/galleries/examples/event_handling/coords_demo.py
    new file mode 100644
    index 000000000000..a7d2d044fe3b
    --- /dev/null
    +++ b/galleries/examples/event_handling/coords_demo.py
    @@ -0,0 +1,44 @@
    +"""
    +===========================
    +Mouse move and click events
    +===========================
    +
    +An example of how to interact with the plotting canvas by connecting to move
    +and click events.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.backend_bases import MouseButton
    +
    +t = np.arange(0.0, 1.0, 0.01)
    +s = np.sin(2 * np.pi * t)
    +fig, ax = plt.subplots()
    +ax.plot(t, s)
    +
    +
    +def on_move(event):
    +    if event.inaxes:
    +        print(f'data coords {event.xdata} {event.ydata},',
    +              f'pixel coords {event.x} {event.y}')
    +
    +
    +def on_click(event):
    +    if event.button is MouseButton.LEFT:
    +        print('disconnecting callback')
    +        plt.disconnect(binding_id)
    +
    +
    +binding_id = plt.connect('motion_notify_event', on_move)
    +plt.connect('button_press_event', on_click)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/cursor_demo.py b/galleries/examples/event_handling/cursor_demo.py
    new file mode 100644
    index 000000000000..6c12d67ae6d4
    --- /dev/null
    +++ b/galleries/examples/event_handling/cursor_demo.py
    @@ -0,0 +1,242 @@
    +"""
    +=================
    +Cross-hair cursor
    +=================
    +
    +This example adds a cross-hair as a data cursor.  The cross-hair is
    +implemented as regular line objects that are updated on mouse move.
    +
    +We show three implementations:
    +
    +1) A simple cursor implementation that redraws the figure on every mouse move.
    +   This is a bit slow, and you may notice some lag of the cross-hair movement.
    +2) A cursor that uses blitting for speedup of the rendering.
    +3) A cursor that snaps to data points.
    +
    +Faster cursoring is possible using native GUI drawing, as in
    +:doc:`/gallery/user_interfaces/wxcursor_demo_sgskip`.
    +
    +The mpldatacursor__ and mplcursors__ third-party packages can be used to
    +achieve a similar effect.
    +
    +__ https://github.com/joferkington/mpldatacursor
    +__ https://github.com/anntzer/mplcursors
    +
    +.. redirect-from:: /gallery/misc/cursor_demo
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.backend_bases import MouseEvent
    +
    +
    +class Cursor:
    +    """
    +    A cross hair cursor.
    +    """
    +    def __init__(self, ax):
    +        self.ax = ax
    +        self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
    +        self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
    +        # text location in axes coordinates
    +        self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
    +
    +    def set_cross_hair_visible(self, visible):
    +        need_redraw = self.horizontal_line.get_visible() != visible
    +        self.horizontal_line.set_visible(visible)
    +        self.vertical_line.set_visible(visible)
    +        self.text.set_visible(visible)
    +        return need_redraw
    +
    +    def on_mouse_move(self, event):
    +        if not event.inaxes:
    +            need_redraw = self.set_cross_hair_visible(False)
    +            if need_redraw:
    +                self.ax.figure.canvas.draw()
    +        else:
    +            self.set_cross_hair_visible(True)
    +            x, y = event.xdata, event.ydata
    +            # update the line positions
    +            self.horizontal_line.set_ydata([y])
    +            self.vertical_line.set_xdata([x])
    +            self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
    +            self.ax.figure.canvas.draw()
    +
    +
    +x = np.arange(0, 1, 0.01)
    +y = np.sin(2 * 2 * np.pi * x)
    +
    +fig, ax = plt.subplots()
    +ax.set_title('Simple cursor')
    +ax.plot(x, y, 'o')
    +cursor = Cursor(ax)
    +fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
    +
    +# Simulate a mouse move to (0.5, 0.5), needed for online docs
    +t = ax.transData
    +MouseEvent(
    +    "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5))
    +)._process()
    +
    +# %%
    +# Faster redrawing using blitting
    +# """""""""""""""""""""""""""""""
    +# This technique stores the rendered plot as a background image. Only the
    +# changed artists (cross-hair lines and text) are rendered anew. They are
    +# combined with the background using blitting.
    +#
    +# This technique is significantly faster. It requires a bit more setup because
    +# the background has to be stored without the cross-hair lines (see
    +# ``create_new_background()``). Additionally, a new background has to be
    +# created whenever the figure changes. This is achieved by connecting to the
    +# ``'draw_event'``.
    +
    +
    +class BlittedCursor:
    +    """
    +    A cross-hair cursor using blitting for faster redraw.
    +    """
    +    def __init__(self, ax):
    +        self.ax = ax
    +        self.background = None
    +        self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
    +        self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
    +        # text location in axes coordinates
    +        self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
    +        self._creating_background = False
    +        ax.figure.canvas.mpl_connect('draw_event', self.on_draw)
    +
    +    def on_draw(self, event):
    +        self.create_new_background()
    +
    +    def set_cross_hair_visible(self, visible):
    +        need_redraw = self.horizontal_line.get_visible() != visible
    +        self.horizontal_line.set_visible(visible)
    +        self.vertical_line.set_visible(visible)
    +        self.text.set_visible(visible)
    +        return need_redraw
    +
    +    def create_new_background(self):
    +        if self._creating_background:
    +            # discard calls triggered from within this function
    +            return
    +        self._creating_background = True
    +        self.set_cross_hair_visible(False)
    +        self.ax.figure.canvas.draw()
    +        self.background = self.ax.figure.canvas.copy_from_bbox(self.ax.bbox)
    +        self.set_cross_hair_visible(True)
    +        self._creating_background = False
    +
    +    def on_mouse_move(self, event):
    +        if self.background is None:
    +            self.create_new_background()
    +        if not event.inaxes:
    +            need_redraw = self.set_cross_hair_visible(False)
    +            if need_redraw:
    +                self.ax.figure.canvas.restore_region(self.background)
    +                self.ax.figure.canvas.blit(self.ax.bbox)
    +        else:
    +            self.set_cross_hair_visible(True)
    +            # update the line positions
    +            x, y = event.xdata, event.ydata
    +            self.horizontal_line.set_ydata([y])
    +            self.vertical_line.set_xdata([x])
    +            self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
    +
    +            self.ax.figure.canvas.restore_region(self.background)
    +            self.ax.draw_artist(self.horizontal_line)
    +            self.ax.draw_artist(self.vertical_line)
    +            self.ax.draw_artist(self.text)
    +            self.ax.figure.canvas.blit(self.ax.bbox)
    +
    +
    +x = np.arange(0, 1, 0.01)
    +y = np.sin(2 * 2 * np.pi * x)
    +
    +fig, ax = plt.subplots()
    +ax.set_title('Blitted cursor')
    +ax.plot(x, y, 'o')
    +blitted_cursor = BlittedCursor(ax)
    +fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move)
    +
    +# Simulate a mouse move to (0.5, 0.5), needed for online docs
    +t = ax.transData
    +MouseEvent(
    +    "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5))
    +)._process()
    +
    +# %%
    +# Snapping to data points
    +# """""""""""""""""""""""
    +# The following cursor snaps its position to the data points of a `.Line2D`
    +# object.
    +#
    +# To save unnecessary redraws, the index of the last indicated data point is
    +# saved in ``self._last_index``. A redraw is only triggered when the mouse
    +# moves far enough so that another data point must be selected. This reduces
    +# the lag due to many redraws. Of course, blitting could still be added on top
    +# for additional speedup.
    +
    +
    +class SnappingCursor:
    +    """
    +    A cross-hair cursor that snaps to the data point of a line, which is
    +    closest to the *x* position of the cursor.
    +
    +    For simplicity, this assumes that *x* values of the data are sorted.
    +    """
    +    def __init__(self, ax, line):
    +        self.ax = ax
    +        self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
    +        self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
    +        self.x, self.y = line.get_data()
    +        self._last_index = None
    +        # text location in axes coords
    +        self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
    +
    +    def set_cross_hair_visible(self, visible):
    +        need_redraw = self.horizontal_line.get_visible() != visible
    +        self.horizontal_line.set_visible(visible)
    +        self.vertical_line.set_visible(visible)
    +        self.text.set_visible(visible)
    +        return need_redraw
    +
    +    def on_mouse_move(self, event):
    +        if not event.inaxes:
    +            self._last_index = None
    +            need_redraw = self.set_cross_hair_visible(False)
    +            if need_redraw:
    +                self.ax.figure.canvas.draw()
    +        else:
    +            self.set_cross_hair_visible(True)
    +            x, y = event.xdata, event.ydata
    +            index = min(np.searchsorted(self.x, x), len(self.x) - 1)
    +            if index == self._last_index:
    +                return  # still on the same data point. Nothing to do.
    +            self._last_index = index
    +            x = self.x[index]
    +            y = self.y[index]
    +            # update the line positions
    +            self.horizontal_line.set_ydata([y])
    +            self.vertical_line.set_xdata([x])
    +            self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
    +            self.ax.figure.canvas.draw()
    +
    +
    +x = np.arange(0, 1, 0.01)
    +y = np.sin(2 * 2 * np.pi * x)
    +
    +fig, ax = plt.subplots()
    +ax.set_title('Snapping cursor')
    +line, = ax.plot(x, y, 'o')
    +snap_cursor = SnappingCursor(ax, line)
    +fig.canvas.mpl_connect('motion_notify_event', snap_cursor.on_mouse_move)
    +
    +# Simulate a mouse move to (0.5, 0.5), needed for online docs
    +t = ax.transData
    +MouseEvent(
    +    "motion_notify_event", ax.figure.canvas, *t.transform((0.5, 0.5))
    +)._process()
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/data_browser.py b/galleries/examples/event_handling/data_browser.py
    new file mode 100644
    index 000000000000..3d2dfeddc3a3
    --- /dev/null
    +++ b/galleries/examples/event_handling/data_browser.py
    @@ -0,0 +1,110 @@
    +"""
    +============
    +Data browser
    +============
    +
    +Connecting data between multiple canvases.
    +
    +This example covers how to interact data with multiple canvases. This
    +lets you select and highlight a point on one axis, and generating the
    +data of that point on the other axis.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import numpy as np
    +
    +
    +class PointBrowser:
    +    """
    +    Click on a point to select and highlight it -- the data that
    +    generated the point will be shown in the lower Axes.  Use the 'n'
    +    and 'p' keys to browse through the next and previous points
    +    """
    +
    +    def __init__(self):
    +        self.lastind = 0
    +
    +        self.text = ax.text(0.05, 0.95, 'selected: none',
    +                            transform=ax.transAxes, va='top')
    +        self.selected, = ax.plot([xs[0]], [ys[0]], 'o', ms=12, alpha=0.4,
    +                                 color='yellow', visible=False)
    +
    +    def on_press(self, event):
    +        if self.lastind is None:
    +            return
    +        if event.key not in ('n', 'p'):
    +            return
    +        if event.key == 'n':
    +            inc = 1
    +        else:
    +            inc = -1
    +
    +        self.lastind += inc
    +        self.lastind = np.clip(self.lastind, 0, len(xs) - 1)
    +        self.update()
    +
    +    def on_pick(self, event):
    +
    +        if event.artist != line:
    +            return True
    +
    +        N = len(event.ind)
    +        if not N:
    +            return True
    +
    +        # the click locations
    +        x = event.mouseevent.xdata
    +        y = event.mouseevent.ydata
    +
    +        distances = np.hypot(x - xs[event.ind], y - ys[event.ind])
    +        indmin = distances.argmin()
    +        dataind = event.ind[indmin]
    +
    +        self.lastind = dataind
    +        self.update()
    +
    +    def update(self):
    +        if self.lastind is None:
    +            return
    +
    +        dataind = self.lastind
    +
    +        ax2.clear()
    +        ax2.plot(X[dataind])
    +
    +        ax2.text(0.05, 0.9, f'mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}',
    +                 transform=ax2.transAxes, va='top')
    +        ax2.set_ylim(-0.5, 1.5)
    +        self.selected.set_visible(True)
    +        self.selected.set_data([xs[dataind]], [ys[dataind]])
    +
    +        self.text.set_text('selected: %d' % dataind)
    +        fig.canvas.draw()
    +
    +
    +if __name__ == '__main__':
    +    import matplotlib.pyplot as plt
    +
    +    # Fixing random state for reproducibility
    +    np.random.seed(19680801)
    +
    +    X = np.random.rand(100, 200)
    +    xs = np.mean(X, axis=1)
    +    ys = np.std(X, axis=1)
    +
    +    fig, (ax, ax2) = plt.subplots(2, 1)
    +    ax.set_title('click on point to plot time series')
    +    line, = ax.plot(xs, ys, 'o', picker=True, pickradius=5)
    +
    +    browser = PointBrowser()
    +
    +    fig.canvas.mpl_connect('pick_event', browser.on_pick)
    +    fig.canvas.mpl_connect('key_press_event', browser.on_press)
    +
    +    plt.show()
    diff --git a/galleries/examples/event_handling/figure_axes_enter_leave.py b/galleries/examples/event_handling/figure_axes_enter_leave.py
    new file mode 100644
    index 000000000000..0793f2901585
    --- /dev/null
    +++ b/galleries/examples/event_handling/figure_axes_enter_leave.py
    @@ -0,0 +1,52 @@
    +"""
    +==================================
    +Figure/Axes enter and leave events
    +==================================
    +
    +Illustrate the figure and Axes enter and leave events by changing the
    +frame colors on enter and leave.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import matplotlib.pyplot as plt
    +
    +
    +def on_enter_axes(event):
    +    print('enter_axes', event.inaxes)
    +    event.inaxes.patch.set_facecolor('yellow')
    +    event.canvas.draw()
    +
    +
    +def on_leave_axes(event):
    +    print('leave_axes', event.inaxes)
    +    event.inaxes.patch.set_facecolor('white')
    +    event.canvas.draw()
    +
    +
    +def on_enter_figure(event):
    +    print('enter_figure', event.canvas.figure)
    +    event.canvas.figure.patch.set_facecolor('red')
    +    event.canvas.draw()
    +
    +
    +def on_leave_figure(event):
    +    print('leave_figure', event.canvas.figure)
    +    event.canvas.figure.patch.set_facecolor('grey')
    +    event.canvas.draw()
    +
    +
    +fig, axs = plt.subplots(2, 1)
    +fig.suptitle('mouse hover over figure or Axes to trigger events')
    +
    +fig.canvas.mpl_connect('figure_enter_event', on_enter_figure)
    +fig.canvas.mpl_connect('figure_leave_event', on_leave_figure)
    +fig.canvas.mpl_connect('axes_enter_event', on_enter_axes)
    +fig.canvas.mpl_connect('axes_leave_event', on_leave_axes)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/ginput_manual_clabel_sgskip.py b/galleries/examples/event_handling/ginput_manual_clabel_sgskip.py
    new file mode 100644
    index 000000000000..a4f4f670f620
    --- /dev/null
    +++ b/galleries/examples/event_handling/ginput_manual_clabel_sgskip.py
    @@ -0,0 +1,100 @@
    +"""
    +=====================
    +Interactive functions
    +=====================
    +
    +This provides examples of uses of interactive functions, such as ginput,
    +waitforbuttonpress and manual clabel placement.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def tellme(s):
    +    print(s)
    +    plt.title(s, fontsize=16)
    +    plt.draw()
    +
    +# %%
    +# Define a triangle by clicking three points
    +
    +
    +plt.figure()
    +plt.xlim(0, 1)
    +plt.ylim(0, 1)
    +
    +tellme('You will define a triangle, click to begin')
    +
    +plt.waitforbuttonpress()
    +
    +while True:
    +    pts = []
    +    while len(pts) < 3:
    +        tellme('Select 3 corners with mouse')
    +        pts = np.asarray(plt.ginput(3, timeout=-1))
    +        if len(pts) < 3:
    +            tellme('Too few points, starting over')
    +            time.sleep(1)  # Wait a second
    +
    +    ph = plt.fill(pts[:, 0], pts[:, 1], 'r', lw=2)
    +
    +    tellme('Happy? Key click for yes, mouse click for no')
    +
    +    if plt.waitforbuttonpress():
    +        break
    +
    +    # Get rid of fill
    +    for p in ph:
    +        p.remove()
    +
    +
    +# %%
    +# Now contour according to distance from triangle
    +# corners - just an example
    +
    +# Define a nice function of distance from individual pts
    +def f(x, y, pts):
    +    z = np.zeros_like(x)
    +    for p in pts:
    +        z = z + 1/(np.sqrt((x - p[0])**2 + (y - p[1])**2))
    +    return 1/z
    +
    +
    +X, Y = np.meshgrid(np.linspace(-1, 1, 51), np.linspace(-1, 1, 51))
    +Z = f(X, Y, pts)
    +
    +CS = plt.contour(X, Y, Z, 20)
    +
    +tellme('Use mouse to select contour label locations, middle button to finish')
    +CL = plt.clabel(CS, manual=True)
    +
    +# %%
    +# Now do a zoom
    +
    +tellme('Now do a nested zoom, click to begin')
    +plt.waitforbuttonpress()
    +
    +while True:
    +    tellme('Select two corners of zoom, middle mouse button to finish')
    +    pts = plt.ginput(2, timeout=-1)
    +    if len(pts) < 2:
    +        break
    +    (x0, y0), (x1, y1) = pts
    +    xmin, xmax = sorted([x0, x1])
    +    ymin, ymax = sorted([y0, y1])
    +    plt.xlim(xmin, xmax)
    +    plt.ylim(ymin, ymax)
    +
    +tellme('All Done!')
    +plt.show()
    diff --git a/galleries/examples/event_handling/image_slices_viewer.py b/galleries/examples/event_handling/image_slices_viewer.py
    new file mode 100644
    index 000000000000..a9abe8070b64
    --- /dev/null
    +++ b/galleries/examples/event_handling/image_slices_viewer.py
    @@ -0,0 +1,53 @@
    +"""
    +============
    +Scroll event
    +============
    +
    +In this example a scroll wheel event is used to scroll through 2D slices of
    +3D data.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +class IndexTracker:
    +    def __init__(self, ax, X):
    +        self.index = 0
    +        self.X = X
    +        self.ax = ax
    +        self.im = ax.imshow(self.X[:, :, self.index])
    +        self.update()
    +
    +    def on_scroll(self, event):
    +        print(event.button, event.step)
    +        increment = 1 if event.button == 'up' else -1
    +        max_index = self.X.shape[-1] - 1
    +        self.index = np.clip(self.index + increment, 0, max_index)
    +        self.update()
    +
    +    def update(self):
    +        self.im.set_data(self.X[:, :, self.index])
    +        self.ax.set_title(
    +            f'Use scroll wheel to navigate\nindex {self.index}')
    +        self.im.axes.figure.canvas.draw()
    +
    +
    +x, y, z = np.ogrid[-10:10:100j, -10:10:100j, 1:10:20j]
    +X = np.sin(x * y * z) / (x * y * z)
    +
    +fig, ax = plt.subplots()
    +# create an IndexTracker and make sure it lives during the whole
    +# lifetime of the figure by assigning it to a variable
    +tracker = IndexTracker(ax, X)
    +
    +fig.canvas.mpl_connect('scroll_event', tracker.on_scroll)
    +plt.show()
    diff --git a/galleries/examples/event_handling/keypress_demo.py b/galleries/examples/event_handling/keypress_demo.py
    new file mode 100644
    index 000000000000..6342e113d241
    --- /dev/null
    +++ b/galleries/examples/event_handling/keypress_demo.py
    @@ -0,0 +1,41 @@
    +"""
    +==============
    +Keypress event
    +==============
    +
    +Show how to connect to keypress events.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import sys
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def on_press(event):
    +    print('press', event.key)
    +    sys.stdout.flush()
    +    if event.key == 'x':
    +        visible = xl.get_visible()
    +        xl.set_visible(not visible)
    +        fig.canvas.draw()
    +
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, ax = plt.subplots()
    +
    +fig.canvas.mpl_connect('key_press_event', on_press)
    +
    +ax.plot(np.random.rand(12), np.random.rand(12), 'go')
    +xl = ax.set_xlabel('easy come, easy go')
    +ax.set_title('Press a key')
    +plt.show()
    diff --git a/galleries/examples/event_handling/lasso_demo.py b/galleries/examples/event_handling/lasso_demo.py
    new file mode 100644
    index 000000000000..ed493a289993
    --- /dev/null
    +++ b/galleries/examples/event_handling/lasso_demo.py
    @@ -0,0 +1,66 @@
    +"""
    +==========
    +Lasso Demo
    +==========
    +
    +Use a lasso to select a set of points and get the indices of the selected points.
    +A callback is used to change the color of the selected points.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import colors as mcolors
    +from matplotlib import path
    +from matplotlib.collections import RegularPolyCollection
    +from matplotlib.widgets import Lasso
    +
    +
    +class LassoManager:
    +    def __init__(self, ax, data):
    +        # The information of whether a point has been selected or not is stored in the
    +        # collection's array (0 = out, 1 = in), which then gets colormapped to blue
    +        # (out) and red (in).
    +        self.collection = RegularPolyCollection(
    +            6, sizes=(100,), offset_transform=ax.transData,
    +            offsets=data, array=np.zeros(len(data)),
    +            clim=(0, 1), cmap=mcolors.ListedColormap(["tab:blue", "tab:red"]))
    +        ax.add_collection(self.collection)
    +        canvas = ax.figure.canvas
    +        canvas.mpl_connect('button_press_event', self.on_press)
    +        canvas.mpl_connect('button_release_event', self.on_release)
    +
    +    def callback(self, verts):
    +        data = self.collection.get_offsets()
    +        self.collection.set_array(path.Path(verts).contains_points(data))
    +        canvas = self.collection.figure.canvas
    +        canvas.draw_idle()
    +        del self.lasso
    +
    +    def on_press(self, event):
    +        canvas = self.collection.figure.canvas
    +        if event.inaxes is not self.collection.axes or canvas.widgetlock.locked():
    +            return
    +        self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback)
    +        canvas.widgetlock(self.lasso)  # acquire a lock on the widget drawing
    +
    +    def on_release(self, event):
    +        canvas = self.collection.figure.canvas
    +        if hasattr(self, 'lasso') and canvas.widgetlock.isowner(self.lasso):
    +            canvas.widgetlock.release(self.lasso)
    +
    +
    +if __name__ == '__main__':
    +    np.random.seed(19680801)
    +    ax = plt.figure().add_subplot(
    +        xlim=(0, 1), ylim=(0, 1), title='Lasso points using left mouse button')
    +    manager = LassoManager(ax, np.random.rand(100, 2))
    +    plt.show()
    diff --git a/galleries/examples/event_handling/legend_picking.py b/galleries/examples/event_handling/legend_picking.py
    new file mode 100644
    index 000000000000..2fed3c9c1649
    --- /dev/null
    +++ b/galleries/examples/event_handling/legend_picking.py
    @@ -0,0 +1,63 @@
    +"""
    +==============
    +Legend picking
    +==============
    +
    +Enable picking on the legend to toggle the original line on and off
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.linspace(0, 1)
    +y1 = 2 * np.sin(2 * np.pi * t)
    +y2 = 4 * np.sin(2 * np.pi * 2 * t)
    +
    +fig, ax = plt.subplots()
    +ax.set_title('Click on legend line to toggle line on/off')
    +(line1, ) = ax.plot(t, y1, lw=2, label='1 Hz')
    +(line2, ) = ax.plot(t, y2, lw=2, label='2 Hz')
    +leg = ax.legend(fancybox=True, shadow=True)
    +
    +lines = [line1, line2]
    +map_legend_to_ax = {}  # Will map legend lines to original lines.
    +
    +pickradius = 5  # Points (Pt). How close the click needs to be to trigger an event.
    +
    +for legend_line, ax_line in zip(leg.get_lines(), lines):
    +    legend_line.set_picker(pickradius)  # Enable picking on the legend line.
    +    map_legend_to_ax[legend_line] = ax_line
    +
    +
    +def on_pick(event):
    +    # On the pick event, find the original line corresponding to the legend
    +    # proxy line, and toggle its visibility.
    +    legend_line = event.artist
    +
    +    # Do nothing if the source of the event is not a legend line.
    +    if legend_line not in map_legend_to_ax:
    +        return
    +
    +    ax_line = map_legend_to_ax[legend_line]
    +    visible = not ax_line.get_visible()
    +    ax_line.set_visible(visible)
    +    # Change the alpha on the line in the legend, so we can see what lines
    +    # have been toggled.
    +    legend_line.set_alpha(1.0 if visible else 0.2)
    +    fig.canvas.draw()
    +
    +
    +fig.canvas.mpl_connect('pick_event', on_pick)
    +
    +# Works even if the legend is draggable. This is independent from picking legend lines.
    +leg.set_draggable(True)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/looking_glass.py b/galleries/examples/event_handling/looking_glass.py
    new file mode 100644
    index 000000000000..a2a5f396c75a
    --- /dev/null
    +++ b/galleries/examples/event_handling/looking_glass.py
    @@ -0,0 +1,68 @@
    +"""
    +=============
    +Looking glass
    +=============
    +
    +Example using mouse events to simulate a looking glass for inspecting data.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.patches as patches
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +x, y = np.random.rand(2, 200)
    +
    +fig, ax = plt.subplots()
    +circ = patches.Circle((0.5, 0.5), 0.25, alpha=0.8, fc='yellow')
    +ax.add_patch(circ)
    +
    +
    +ax.plot(x, y, alpha=0.2)
    +line, = ax.plot(x, y, alpha=1.0, clip_path=circ)
    +ax.set_title("Left click and drag to move looking glass")
    +
    +
    +class EventHandler:
    +    def __init__(self):
    +        fig.canvas.mpl_connect('button_press_event', self.on_press)
    +        fig.canvas.mpl_connect('button_release_event', self.on_release)
    +        fig.canvas.mpl_connect('motion_notify_event', self.on_move)
    +        self.x0, self.y0 = circ.center
    +        self.pressevent = None
    +
    +    def on_press(self, event):
    +        if event.inaxes != ax:
    +            return
    +
    +        if not circ.contains(event)[0]:
    +            return
    +
    +        self.pressevent = event
    +
    +    def on_release(self, event):
    +        self.pressevent = None
    +        self.x0, self.y0 = circ.center
    +
    +    def on_move(self, event):
    +        if self.pressevent is None or event.inaxes != self.pressevent.inaxes:
    +            return
    +
    +        dx = event.xdata - self.pressevent.xdata
    +        dy = event.ydata - self.pressevent.ydata
    +        circ.center = self.x0 + dx, self.y0 + dy
    +        line.set_clip_path(circ)
    +        fig.canvas.draw()
    +
    +handler = EventHandler()
    +plt.show()
    diff --git a/galleries/examples/event_handling/path_editor.py b/galleries/examples/event_handling/path_editor.py
    new file mode 100644
    index 000000000000..2af54bad53ed
    --- /dev/null
    +++ b/galleries/examples/event_handling/path_editor.py
    @@ -0,0 +1,148 @@
    +"""
    +===========
    +Path editor
    +===========
    +
    +Sharing events across GUIs.
    +
    +This example demonstrates a cross-GUI application using Matplotlib event
    +handling to interact with and modify objects on the canvas.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.backend_bases import MouseButton
    +from matplotlib.patches import PathPatch
    +from matplotlib.path import Path
    +
    +fig, ax = plt.subplots()
    +
    +pathdata = [
    +    (Path.MOVETO, (1.58, -2.57)),
    +    (Path.CURVE4, (0.35, -1.1)),
    +    (Path.CURVE4, (-1.75, 2.0)),
    +    (Path.CURVE4, (0.375, 2.0)),
    +    (Path.LINETO, (0.85, 1.15)),
    +    (Path.CURVE4, (2.2, 3.2)),
    +    (Path.CURVE4, (3, 0.05)),
    +    (Path.CURVE4, (2.0, -0.5)),
    +    (Path.CLOSEPOLY, (1.58, -2.57)),
    +]
    +
    +codes, verts = zip(*pathdata)
    +path = Path(verts, codes)
    +patch = PathPatch(
    +    path, facecolor='green', edgecolor='yellow', alpha=0.5)
    +ax.add_patch(patch)
    +
    +
    +class PathInteractor:
    +    """
    +    A path editor.
    +
    +    Press 't' to toggle vertex markers on and off.  When vertex markers are on,
    +    they can be dragged with the mouse.
    +    """
    +
    +    showverts = True
    +    epsilon = 5  # max pixel distance to count as a vertex hit
    +
    +    def __init__(self, pathpatch):
    +
    +        self.ax = pathpatch.axes
    +        canvas = self.ax.figure.canvas
    +        self.pathpatch = pathpatch
    +        self.pathpatch.set_animated(True)
    +
    +        x, y = zip(*self.pathpatch.get_path().vertices)
    +
    +        self.line, = ax.plot(
    +            x, y, marker='o', markerfacecolor='r', animated=True)
    +
    +        self._ind = None  # the active vertex
    +
    +        canvas.mpl_connect('draw_event', self.on_draw)
    +        canvas.mpl_connect('button_press_event', self.on_button_press)
    +        canvas.mpl_connect('key_press_event', self.on_key_press)
    +        canvas.mpl_connect('button_release_event', self.on_button_release)
    +        canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
    +        self.canvas = canvas
    +
    +    def get_ind_under_point(self, event):
    +        """
    +        Return the index of the point closest to the event position or *None*
    +        if no point is within ``self.epsilon`` to the event position.
    +        """
    +        xy = self.pathpatch.get_path().vertices
    +        xyt = self.pathpatch.get_transform().transform(xy)  # to display coords
    +        xt, yt = xyt[:, 0], xyt[:, 1]
    +        d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2)
    +        ind = d.argmin()
    +        return ind if d[ind] < self.epsilon else None
    +
    +    def on_draw(self, event):
    +        """Callback for draws."""
    +        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
    +        self.ax.draw_artist(self.pathpatch)
    +        self.ax.draw_artist(self.line)
    +
    +    def on_button_press(self, event):
    +        """Callback for mouse button presses."""
    +        if (event.inaxes is None
    +                or event.button != MouseButton.LEFT
    +                or not self.showverts):
    +            return
    +        self._ind = self.get_ind_under_point(event)
    +
    +    def on_button_release(self, event):
    +        """Callback for mouse button releases."""
    +        if (event.button != MouseButton.LEFT
    +                or not self.showverts):
    +            return
    +        self._ind = None
    +
    +    def on_key_press(self, event):
    +        """Callback for key presses."""
    +        if not event.inaxes:
    +            return
    +        if event.key == 't':
    +            self.showverts = not self.showverts
    +            self.line.set_visible(self.showverts)
    +            if not self.showverts:
    +                self._ind = None
    +        self.canvas.draw()
    +
    +    def on_mouse_move(self, event):
    +        """Callback for mouse movements."""
    +        if (self._ind is None
    +                or event.inaxes is None
    +                or event.button != MouseButton.LEFT
    +                or not self.showverts):
    +            return
    +
    +        vertices = self.pathpatch.get_path().vertices
    +
    +        vertices[self._ind] = event.xdata, event.ydata
    +        self.line.set_data(zip(*vertices))
    +
    +        self.canvas.restore_region(self.background)
    +        self.ax.draw_artist(self.pathpatch)
    +        self.ax.draw_artist(self.line)
    +        self.canvas.blit(self.ax.bbox)
    +
    +
    +interactor = PathInteractor(patch)
    +ax.set_title('drag vertices to update path')
    +ax.set_xlim(-3, 4)
    +ax.set_ylim(-3, 4)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/pick_event_demo.py b/galleries/examples/event_handling/pick_event_demo.py
    new file mode 100644
    index 000000000000..163aaf923d77
    --- /dev/null
    +++ b/galleries/examples/event_handling/pick_event_demo.py
    @@ -0,0 +1,208 @@
    +"""
    +===============
    +Pick event demo
    +===============
    +
    +You can enable picking by setting the "picker" property of an artist
    +(for example, a Matplotlib Line2D, Text, Patch, Polygon, AxesImage,
    +etc.)
    +
    +There are a variety of meanings of the picker property:
    +
    +* *None* - picking is disabled for this artist (default)
    +
    +* bool - if *True* then picking will be enabled and the artist will fire a pick
    +  event if the mouse event is over the artist.
    +
    +  Setting ``pickradius`` will add an epsilon tolerance in points and the artist
    +  will fire off an event if its data is within epsilon of the mouse event.  For
    +  some artists like lines and patch collections, the artist may provide
    +  additional data to the pick event that is generated, for example, the indices
    +  of the data within epsilon of the pick event
    +
    +* function - if picker is callable, it is a user supplied function which
    +  determines whether the artist is hit by the mouse event. ::
    +
    +     hit, props = picker(artist, mouseevent)
    +
    +  to determine the hit test.  If the mouse event is over the artist, return
    +  hit=True and props is a dictionary of properties you want added to the
    +  PickEvent attributes.
    +
    +After you have enabled an artist for picking by setting the "picker"
    +property, you need to connect to the figure canvas pick_event to get
    +pick callbacks on mouse press events.  For example, ::
    +
    +  def pick_handler(event):
    +      mouseevent = event.mouseevent
    +      artist = event.artist
    +      # now do something with this...
    +
    +
    +The pick event (matplotlib.backend_bases.PickEvent) which is passed to
    +your callback is always fired with two attributes:
    +
    +mouseevent
    +  the mouse event that generate the pick event.
    +
    +  The mouse event in turn has attributes like x and y (the coordinates in
    +  display space, such as pixels from left, bottom) and xdata, ydata (the
    +  coords in data space).  Additionally, you can get information about
    +  which buttons were pressed, which keys were pressed, which Axes
    +  the mouse is over, etc.  See matplotlib.backend_bases.MouseEvent
    +  for details.
    +
    +artist
    +  the matplotlib.artist that generated the pick event.
    +
    +Additionally, certain artists like Line2D and PatchCollection may
    +attach additional metadata like the indices into the data that meet
    +the picker criteria (for example, all the points in the line that are within
    +the specified epsilon tolerance)
    +
    +The examples below illustrate each of these methods.
    +
    +.. note::
    +    These examples exercises the interactive capabilities of Matplotlib, and
    +    this will not appear in the static documentation. Please run this code on
    +    your machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +from numpy.random import rand
    +
    +from matplotlib.image import AxesImage
    +from matplotlib.lines import Line2D
    +from matplotlib.patches import Rectangle
    +from matplotlib.text import Text
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +# %%
    +# Simple picking, lines, rectangles and text
    +# ------------------------------------------
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1)
    +ax1.set_title('click on points, rectangles or text', picker=True)
    +ax1.set_ylabel('ylabel', picker=True, bbox=dict(facecolor='red'))
    +line, = ax1.plot(rand(100), 'o', picker=True, pickradius=5)
    +
    +# Pick the rectangle.
    +ax2.bar(range(10), rand(10), picker=True)
    +for label in ax2.get_xticklabels():  # Make the xtick labels pickable.
    +    label.set_picker(True)
    +
    +
    +def onpick1(event):
    +    if isinstance(event.artist, Line2D):
    +        thisline = event.artist
    +        xdata = thisline.get_xdata()
    +        ydata = thisline.get_ydata()
    +        ind = event.ind
    +        print('onpick1 line:', np.column_stack([xdata[ind], ydata[ind]]))
    +    elif isinstance(event.artist, Rectangle):
    +        patch = event.artist
    +        print('onpick1 patch:', patch.get_path())
    +    elif isinstance(event.artist, Text):
    +        text = event.artist
    +        print('onpick1 text:', text.get_text())
    +
    +
    +fig.canvas.mpl_connect('pick_event', onpick1)
    +
    +
    +# %%
    +# Picking with a custom hit test function
    +# ---------------------------------------
    +# You can define custom pickers by setting picker to a callable function. The
    +# function has the signature::
    +#
    +#  hit, props = func(artist, mouseevent)
    +#
    +# to determine the hit test. If the mouse event is over the artist, return
    +# ``hit=True`` and ``props`` is a dictionary of properties you want added to
    +# the `.PickEvent` attributes.
    +
    +def line_picker(line, mouseevent):
    +    """
    +    Find the points within a certain distance from the mouseclick in
    +    data coords and attach some extra attributes, pickx and picky
    +    which are the data points that were picked.
    +    """
    +    if mouseevent.xdata is None:
    +        return False, dict()
    +    xdata = line.get_xdata()
    +    ydata = line.get_ydata()
    +    maxd = 0.05
    +    d = np.sqrt(
    +        (xdata - mouseevent.xdata)**2 + (ydata - mouseevent.ydata)**2)
    +
    +    ind, = np.nonzero(d <= maxd)
    +    if len(ind):
    +        pickx = xdata[ind]
    +        picky = ydata[ind]
    +        props = dict(ind=ind, pickx=pickx, picky=picky)
    +        return True, props
    +    else:
    +        return False, dict()
    +
    +
    +def onpick2(event):
    +    print('onpick2 line:', event.pickx, event.picky)
    +
    +
    +fig, ax = plt.subplots()
    +ax.set_title('custom picker for line data')
    +line, = ax.plot(rand(100), rand(100), 'o', picker=line_picker)
    +fig.canvas.mpl_connect('pick_event', onpick2)
    +
    +
    +# %%
    +# Picking on a scatter plot
    +# -------------------------
    +# A scatter plot is backed by a `~matplotlib.collections.PathCollection`.
    +
    +x, y, c, s = rand(4, 100)
    +
    +
    +def onpick3(event):
    +    ind = event.ind
    +    print('onpick3 scatter:', ind, x[ind], y[ind])
    +
    +
    +fig, ax = plt.subplots()
    +ax.scatter(x, y, 100*s, c, picker=True)
    +fig.canvas.mpl_connect('pick_event', onpick3)
    +
    +
    +# %%
    +# Picking images
    +# --------------
    +# Images plotted using `.Axes.imshow` are `~matplotlib.image.AxesImage`
    +# objects.
    +
    +fig, ax = plt.subplots()
    +ax.imshow(rand(10, 5), extent=(1, 2, 1, 2), picker=True)
    +ax.imshow(rand(5, 10), extent=(3, 4, 1, 2), picker=True)
    +ax.imshow(rand(20, 25), extent=(1, 2, 3, 4), picker=True)
    +ax.imshow(rand(30, 12), extent=(3, 4, 3, 4), picker=True)
    +ax.set(xlim=(0, 5), ylim=(0, 5))
    +
    +
    +def onpick4(event):
    +    artist = event.artist
    +    if isinstance(artist, AxesImage):
    +        im = artist
    +        A = im.get_array()
    +        print('onpick4 image', A.shape)
    +
    +
    +fig.canvas.mpl_connect('pick_event', onpick4)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/pick_event_demo2.py b/galleries/examples/event_handling/pick_event_demo2.py
    new file mode 100644
    index 000000000000..5efaecb0d341
    --- /dev/null
    +++ b/galleries/examples/event_handling/pick_event_demo2.py
    @@ -0,0 +1,53 @@
    +"""
    +=================
    +Pick event demo 2
    +=================
    +
    +Compute the mean (mu) and standard deviation (sigma) of 100 data sets and plot
    +mu vs. sigma.  When you click on one of the (mu, sigma) points, plot the raw
    +data from the dataset that generated this point.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +X = np.random.rand(100, 1000)
    +xs = np.mean(X, axis=1)
    +ys = np.std(X, axis=1)
    +
    +fig, ax = plt.subplots()
    +ax.set_title('click on point to plot time series')
    +line, = ax.plot(xs, ys, 'o', picker=True, pickradius=5)
    +
    +
    +def onpick(event):
    +
    +    if event.artist != line:
    +        return
    +
    +    N = len(event.ind)
    +    if not N:
    +        return
    +
    +    figi, axs = plt.subplots(N, squeeze=False)
    +    for ax, dataind in zip(axs.flat, event.ind):
    +        ax.plot(X[dataind])
    +        ax.text(.05, .9, f'mu={xs[dataind]:1.3f}\nsigma={ys[dataind]:1.3f}',
    +                transform=ax.transAxes, va='top')
    +        ax.set_ylim(-0.5, 1.5)
    +    figi.show()
    +
    +
    +fig.canvas.mpl_connect('pick_event', onpick)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/poly_editor.py b/galleries/examples/event_handling/poly_editor.py
    new file mode 100644
    index 000000000000..9cc2e5373ae5
    --- /dev/null
    +++ b/galleries/examples/event_handling/poly_editor.py
    @@ -0,0 +1,208 @@
    +"""
    +==============
    +Polygon editor
    +==============
    +
    +This is an example to show how to build cross-GUI applications using
    +Matplotlib event handling to interact with objects on the canvas.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import numpy as np
    +
    +from matplotlib.artist import Artist
    +from matplotlib.lines import Line2D
    +
    +
    +def dist_point_to_segment(p, s0, s1):
    +    """
    +    Get the distance from the point *p* to the segment (*s0*, *s1*), where
    +    *p*, *s0*, *s1* are ``[x, y]`` arrays.
    +    """
    +    s01 = s1 - s0
    +    s0p = p - s0
    +    if (s01 == 0).all():
    +        return np.hypot(*s0p)
    +    # Project onto segment, without going past segment ends.
    +    p1 = s0 + np.clip((s0p @ s01) / (s01 @ s01), 0, 1) * s01
    +    return np.hypot(*(p - p1))
    +
    +
    +class PolygonInteractor:
    +    """
    +    A polygon editor.
    +
    +    Key-bindings
    +
    +      't' toggle vertex markers on and off.  When vertex markers are on,
    +          you can move them, delete them
    +
    +      'd' delete the vertex under point
    +
    +      'i' insert a vertex at point.  You must be within epsilon of the
    +          line connecting two existing vertices
    +
    +    """
    +
    +    showverts = True
    +    epsilon = 5  # max pixel distance to count as a vertex hit
    +
    +    def __init__(self, ax, poly):
    +        if poly.figure is None:
    +            raise RuntimeError('You must first add the polygon to a figure '
    +                               'or canvas before defining the interactor')
    +        self.ax = ax
    +        canvas = poly.figure.canvas
    +        self.poly = poly
    +
    +        x, y = zip(*self.poly.xy)
    +        self.line = Line2D(x, y,
    +                           marker='o', markerfacecolor='r',
    +                           animated=True)
    +        self.ax.add_line(self.line)
    +
    +        self.cid = self.poly.add_callback(self.poly_changed)
    +        self._ind = None  # the active vert
    +
    +        canvas.mpl_connect('draw_event', self.on_draw)
    +        canvas.mpl_connect('button_press_event', self.on_button_press)
    +        canvas.mpl_connect('key_press_event', self.on_key_press)
    +        canvas.mpl_connect('button_release_event', self.on_button_release)
    +        canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
    +        self.canvas = canvas
    +
    +    def on_draw(self, event):
    +        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
    +        self.ax.draw_artist(self.poly)
    +        self.ax.draw_artist(self.line)
    +        # do not need to blit here, this will fire before the screen is
    +        # updated
    +
    +    def poly_changed(self, poly):
    +        """This method is called whenever the pathpatch object is called."""
    +        # only copy the artist props to the line (except visibility)
    +        vis = self.line.get_visible()
    +        Artist.update_from(self.line, poly)
    +        self.line.set_visible(vis)  # don't use the poly visibility state
    +
    +    def get_ind_under_point(self, event):
    +        """
    +        Return the index of the point closest to the event position or *None*
    +        if no point is within ``self.epsilon`` to the event position.
    +        """
    +        # display coords
    +        xy = np.asarray(self.poly.xy)
    +        xyt = self.poly.get_transform().transform(xy)
    +        xt, yt = xyt[:, 0], xyt[:, 1]
    +        d = np.hypot(xt - event.x, yt - event.y)
    +        indseq, = np.nonzero(d == d.min())
    +        ind = indseq[0]
    +
    +        if d[ind] >= self.epsilon:
    +            ind = None
    +
    +        return ind
    +
    +    def on_button_press(self, event):
    +        """Callback for mouse button presses."""
    +        if not self.showverts:
    +            return
    +        if event.inaxes is None:
    +            return
    +        if event.button != 1:
    +            return
    +        self._ind = self.get_ind_under_point(event)
    +
    +    def on_button_release(self, event):
    +        """Callback for mouse button releases."""
    +        if not self.showverts:
    +            return
    +        if event.button != 1:
    +            return
    +        self._ind = None
    +
    +    def on_key_press(self, event):
    +        """Callback for key presses."""
    +        if not event.inaxes:
    +            return
    +        if event.key == 't':
    +            self.showverts = not self.showverts
    +            self.line.set_visible(self.showverts)
    +            if not self.showverts:
    +                self._ind = None
    +        elif event.key == 'd':
    +            ind = self.get_ind_under_point(event)
    +            if ind is not None:
    +                self.poly.xy = np.delete(self.poly.xy,
    +                                         ind, axis=0)
    +                self.line.set_data(zip(*self.poly.xy))
    +        elif event.key == 'i':
    +            xys = self.poly.get_transform().transform(self.poly.xy)
    +            p = event.x, event.y  # display coords
    +            for i in range(len(xys) - 1):
    +                s0 = xys[i]
    +                s1 = xys[i + 1]
    +                d = dist_point_to_segment(p, s0, s1)
    +                if d <= self.epsilon:
    +                    self.poly.xy = np.insert(
    +                        self.poly.xy, i+1,
    +                        [event.xdata, event.ydata],
    +                        axis=0)
    +                    self.line.set_data(zip(*self.poly.xy))
    +                    break
    +        if self.line.stale:
    +            self.canvas.draw_idle()
    +
    +    def on_mouse_move(self, event):
    +        """Callback for mouse movements."""
    +        if not self.showverts:
    +            return
    +        if self._ind is None:
    +            return
    +        if event.inaxes is None:
    +            return
    +        if event.button != 1:
    +            return
    +        x, y = event.xdata, event.ydata
    +
    +        self.poly.xy[self._ind] = x, y
    +        if self._ind == 0:
    +            self.poly.xy[-1] = x, y
    +        elif self._ind == len(self.poly.xy) - 1:
    +            self.poly.xy[0] = x, y
    +        self.line.set_data(zip(*self.poly.xy))
    +
    +        self.canvas.restore_region(self.background)
    +        self.ax.draw_artist(self.poly)
    +        self.ax.draw_artist(self.line)
    +        self.canvas.blit(self.ax.bbox)
    +
    +
    +if __name__ == '__main__':
    +    import matplotlib.pyplot as plt
    +
    +    from matplotlib.patches import Polygon
    +
    +    theta = np.arange(0, 2*np.pi, 0.1)
    +    r = 1.5
    +
    +    xs = r * np.cos(theta)
    +    ys = r * np.sin(theta)
    +
    +    poly = Polygon(np.column_stack([xs, ys]), animated=True)
    +
    +    fig, ax = plt.subplots()
    +    ax.add_patch(poly)
    +    p = PolygonInteractor(ax, poly)
    +
    +    ax.set_title('Click and drag a point to move it')
    +    ax.set_xlim(-2, 2)
    +    ax.set_ylim(-2, 2)
    +    plt.show()
    diff --git a/galleries/examples/event_handling/pong_sgskip.py b/galleries/examples/event_handling/pong_sgskip.py
    new file mode 100644
    index 000000000000..2c4c35a7cb35
    --- /dev/null
    +++ b/galleries/examples/event_handling/pong_sgskip.py
    @@ -0,0 +1,336 @@
    +"""
    +====
    +Pong
    +====
    +
    +A Matplotlib based game of Pong illustrating one way to write interactive
    +animations that are easily ported to multiple backends.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +from numpy.random import randint, randn
    +
    +from matplotlib.font_manager import FontProperties
    +
    +instructions = """
    +Player A:       Player B:
    +  'e'      up     'i'
    +  'd'     down    'k'
    +
    +press 't' -- close these instructions
    +            (animation will be much faster)
    +press 'a' -- add a puck
    +press 'A' -- remove a puck
    +press '1' -- slow down all pucks
    +press '2' -- speed up all pucks
    +press '3' -- slow down distractors
    +press '4' -- speed up distractors
    +press ' ' -- reset the first puck
    +press 'n' -- toggle distractors on/off
    +press 'g' -- toggle the game on/off
    +
    +  """
    +
    +
    +class Pad:
    +    def __init__(self, disp, x, y, type='l'):
    +        self.disp = disp
    +        self.x = x
    +        self.y = y
    +        self.w = .3
    +        self.score = 0
    +        self.xoffset = 0.3
    +        self.yoffset = 0.1
    +        if type == 'r':
    +            self.xoffset *= -1.0
    +
    +        if type == 'l' or type == 'r':
    +            self.signx = -1.0
    +            self.signy = 1.0
    +        else:
    +            self.signx = 1.0
    +            self.signy = -1.0
    +
    +    def contains(self, loc):
    +        return self.disp.get_bbox().contains(loc.x, loc.y)
    +
    +
    +class Puck:
    +    def __init__(self, disp, pad, field):
    +        self.vmax = .2
    +        self.disp = disp
    +        self.field = field
    +        self._reset(pad)
    +
    +    def _reset(self, pad):
    +        self.x = pad.x + pad.xoffset
    +        if pad.y < 0:
    +            self.y = pad.y + pad.yoffset
    +        else:
    +            self.y = pad.y - pad.yoffset
    +        self.vx = pad.x - self.x
    +        self.vy = pad.y + pad.w/2 - self.y
    +        self._speedlimit()
    +        self._slower()
    +        self._slower()
    +
    +    def update(self, pads):
    +        self.x += self.vx
    +        self.y += self.vy
    +        for pad in pads:
    +            if pad.contains(self):
    +                self.vx *= 1.2 * pad.signx
    +                self.vy *= 1.2 * pad.signy
    +        fudge = .001
    +        # probably cleaner with something like...
    +        if self.x < fudge:
    +            pads[1].score += 1
    +            self._reset(pads[0])
    +            return True
    +        if self.x > 7 - fudge:
    +            pads[0].score += 1
    +            self._reset(pads[1])
    +            return True
    +        if self.y < -1 + fudge or self.y > 1 - fudge:
    +            self.vy *= -1.0
    +            # add some randomness, just to make it interesting
    +            self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy)
    +        self._speedlimit()
    +        return False
    +
    +    def _slower(self):
    +        self.vx /= 5.0
    +        self.vy /= 5.0
    +
    +    def _faster(self):
    +        self.vx *= 5.0
    +        self.vy *= 5.0
    +
    +    def _speedlimit(self):
    +        if self.vx > self.vmax:
    +            self.vx = self.vmax
    +        if self.vx < -self.vmax:
    +            self.vx = -self.vmax
    +
    +        if self.vy > self.vmax:
    +            self.vy = self.vmax
    +        if self.vy < -self.vmax:
    +            self.vy = -self.vmax
    +
    +
    +class Game:
    +    def __init__(self, ax):
    +        # create the initial line
    +        self.ax = ax
    +        ax.xaxis.set_visible(False)
    +        ax.set_xlim(0, 7)
    +        ax.yaxis.set_visible(False)
    +        ax.set_ylim(-1, 1)
    +        pad_a_x = 0
    +        pad_b_x = .50
    +        pad_a_y = pad_b_y = .30
    +        pad_b_x += 6.3
    +
    +        # pads
    +        pA, = self.ax.barh(pad_a_y, .2,
    +                           height=.3, color='k', alpha=.5, edgecolor='b',
    +                           lw=2, label="Player B",
    +                           animated=True)
    +        pB, = self.ax.barh(pad_b_y, .2,
    +                           height=.3, left=pad_b_x, color='k', alpha=.5,
    +                           edgecolor='r', lw=2, label="Player A",
    +                           animated=True)
    +
    +        # distractors
    +        self.x = np.arange(0, 2.22*np.pi, 0.01)
    +        self.line, = self.ax.plot(self.x, np.sin(self.x), "r",
    +                                  animated=True, lw=4)
    +        self.line2, = self.ax.plot(self.x, np.cos(self.x), "g",
    +                                   animated=True, lw=4)
    +        self.line3, = self.ax.plot(self.x, np.cos(self.x), "g",
    +                                   animated=True, lw=4)
    +        self.line4, = self.ax.plot(self.x, np.cos(self.x), "r",
    +                                   animated=True, lw=4)
    +
    +        # center line
    +        self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k',
    +                                        alpha=.5, animated=True, lw=8)
    +
    +        # puck (s)
    +        self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_',
    +                                        s=200, c='g',
    +                                        alpha=.9, animated=True)
    +
    +        self.canvas = self.ax.figure.canvas
    +        self.background = None
    +        self.cnt = 0
    +        self.distract = True
    +        self.res = 100.0
    +        self.on = False
    +        self.inst = True    # show instructions from the beginning
    +        self.pads = [Pad(pA, pad_a_x, pad_a_y),
    +                     Pad(pB, pad_b_x, pad_b_y, 'r')]
    +        self.pucks = []
    +        self.i = self.ax.annotate(instructions, (.5, 0.5),
    +                                  name='monospace',
    +                                  verticalalignment='center',
    +                                  horizontalalignment='center',
    +                                  multialignment='left',
    +                                  xycoords='axes fraction',
    +                                  animated=False)
    +        self.canvas.mpl_connect('key_press_event', self.on_key_press)
    +
    +    def draw(self):
    +        draw_artist = self.ax.draw_artist
    +        if self.background is None:
    +            self.background = self.canvas.copy_from_bbox(self.ax.bbox)
    +
    +        # restore the clean slate background
    +        self.canvas.restore_region(self.background)
    +
    +        # show the distractors
    +        if self.distract:
    +            self.line.set_ydata(np.sin(self.x + self.cnt/self.res))
    +            self.line2.set_ydata(np.cos(self.x - self.cnt/self.res))
    +            self.line3.set_ydata(np.tan(self.x + self.cnt/self.res))
    +            self.line4.set_ydata(np.tan(self.x - self.cnt/self.res))
    +            draw_artist(self.line)
    +            draw_artist(self.line2)
    +            draw_artist(self.line3)
    +            draw_artist(self.line4)
    +
    +        # pucks and pads
    +        if self.on:
    +            self.ax.draw_artist(self.centerline)
    +            for pad in self.pads:
    +                pad.disp.set_y(pad.y)
    +                pad.disp.set_x(pad.x)
    +                self.ax.draw_artist(pad.disp)
    +
    +            for puck in self.pucks:
    +                if puck.update(self.pads):
    +                    # we only get here if someone scored
    +                    self.pads[0].disp.set_label(f"   {self.pads[0].score}")
    +                    self.pads[1].disp.set_label(f"   {self.pads[1].score}")
    +                    self.ax.legend(loc='center', framealpha=.2,
    +                                   facecolor='0.5',
    +                                   prop=FontProperties(size='xx-large',
    +                                                       weight='bold'))
    +
    +                    self.background = None
    +                    self.ax.figure.canvas.draw_idle()
    +                    return
    +                puck.disp.set_offsets([[puck.x, puck.y]])
    +                self.ax.draw_artist(puck.disp)
    +
    +        # just redraw the Axes rectangle
    +        self.canvas.blit(self.ax.bbox)
    +        self.canvas.flush_events()
    +        if self.cnt == 50000:
    +            # just so we don't get carried away
    +            print("...and you've been playing for too long!!!")
    +            plt.close()
    +
    +        self.cnt += 1
    +
    +    def on_key_press(self, event):
    +        if event.key == '3':
    +            self.res *= 5.0
    +        if event.key == '4':
    +            self.res /= 5.0
    +
    +        if event.key == 'e':
    +            self.pads[0].y += .1
    +            if self.pads[0].y > 1 - .3:
    +                self.pads[0].y = 1 - .3
    +        if event.key == 'd':
    +            self.pads[0].y -= .1
    +            if self.pads[0].y < -1:
    +                self.pads[0].y = -1
    +
    +        if event.key == 'i':
    +            self.pads[1].y += .1
    +            if self.pads[1].y > 1 - .3:
    +                self.pads[1].y = 1 - .3
    +        if event.key == 'k':
    +            self.pads[1].y -= .1
    +            if self.pads[1].y < -1:
    +                self.pads[1].y = -1
    +
    +        if event.key == 'a':
    +            self.pucks.append(Puck(self.puckdisp,
    +                                   self.pads[randint(2)],
    +                                   self.ax.bbox))
    +        if event.key == 'A' and len(self.pucks):
    +            self.pucks.pop()
    +        if event.key == ' ' and len(self.pucks):
    +            self.pucks[0]._reset(self.pads[randint(2)])
    +        if event.key == '1':
    +            for p in self.pucks:
    +                p._slower()
    +        if event.key == '2':
    +            for p in self.pucks:
    +                p._faster()
    +
    +        if event.key == 'n':
    +            self.distract = not self.distract
    +
    +        if event.key == 'g':
    +            self.on = not self.on
    +        if event.key == 't':
    +            self.inst = not self.inst
    +            self.i.set_visible(not self.i.get_visible())
    +            self.background = None
    +            self.canvas.draw_idle()
    +        if event.key == 'q':
    +            plt.close()
    +
    +
    +fig, ax = plt.subplots()
    +canvas = ax.figure.canvas
    +animation = Game(ax)
    +
    +# disable the default key bindings
    +if fig.canvas.manager.key_press_handler_id is not None:
    +    canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)
    +
    +
    +# reset the blitting background on redraw
    +def on_redraw(event):
    +    animation.background = None
    +
    +
    +# bootstrap after the first draw
    +def start_anim(event):
    +    canvas.mpl_disconnect(start_anim.cid)
    +
    +    start_anim.timer.add_callback(animation.draw)
    +    start_anim.timer.start()
    +    canvas.mpl_connect('draw_event', on_redraw)
    +
    +
    +start_anim.cid = canvas.mpl_connect('draw_event', start_anim)
    +start_anim.timer = animation.canvas.new_timer(interval=1)
    +
    +tstart = time.time()
    +
    +plt.show()
    +print('FPS: %f' % (animation.cnt/(time.time() - tstart)))
    +
    +#
    +# %%
    +# .. tags::
    +#
    +#    interactivity: event-handling
    +#    purpose: fun
    diff --git a/galleries/examples/event_handling/resample.py b/galleries/examples/event_handling/resample.py
    new file mode 100644
    index 000000000000..a573552e800e
    --- /dev/null
    +++ b/galleries/examples/event_handling/resample.py
    @@ -0,0 +1,91 @@
    +"""
    +===============
    +Resampling Data
    +===============
    +
    +Downsampling lowers the sample rate or sample size of a signal. In
    +this tutorial, the signal is downsampled when the plot is adjusted
    +through dragging and zooming.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +# A class that will downsample the data and recompute when zoomed.
    +class DataDisplayDownsampler:
    +    def __init__(self, xdata, y1data, y2data):
    +        self.origY1Data = y1data
    +        self.origY2Data = y2data
    +        self.origXData = xdata
    +        self.max_points = 50
    +        self.delta = xdata[-1] - xdata[0]
    +
    +    def plot(self, ax):
    +        x, y1, y2 = self._downsample(self.origXData.min(), self.origXData.max())
    +        (self.line,) = ax.plot(x, y1, 'o-')
    +        self.poly_collection = ax.fill_between(x, y1, y2, step="pre", color="r")
    +
    +    def _downsample(self, xstart, xend):
    +        # get the points in the view range
    +        mask = (self.origXData > xstart) & (self.origXData < xend)
    +        # dilate the mask by one to catch the points just outside
    +        # of the view range to not truncate the line
    +        mask = np.convolve([1, 1, 1], mask, mode='same').astype(bool)
    +        # sort out how many points to drop
    +        ratio = max(np.sum(mask) // self.max_points, 1)
    +
    +        # mask data
    +        xdata = self.origXData[mask]
    +        y1data = self.origY1Data[mask]
    +        y2data = self.origY2Data[mask]
    +
    +        # downsample data
    +        xdata = xdata[::ratio]
    +        y1data = y1data[::ratio]
    +        y2data = y2data[::ratio]
    +
    +        print(f"using {len(y1data)} of {np.sum(mask)} visible points")
    +
    +        return xdata, y1data, y2data
    +
    +    def update(self, ax):
    +        # Update the artists
    +        lims = ax.viewLim
    +        if abs(lims.width - self.delta) > 1e-8:
    +            self.delta = lims.width
    +            xstart, xend = lims.intervalx
    +            x, y1, y2 = self._downsample(xstart, xend)
    +            self.line.set_data(x, y1)
    +            self.poly_collection.set_data(x, y1, y2, step="pre")
    +            ax.figure.canvas.draw_idle()
    +
    +
    +# Create a signal
    +xdata = np.linspace(16, 365, (365-16)*4)
    +y1data = np.sin(2*np.pi*xdata/153) + np.cos(2*np.pi*xdata/127)
    +y2data = y1data + .2
    +
    +d = DataDisplayDownsampler(xdata, y1data, y2data)
    +
    +fig, ax = plt.subplots()
    +
    +# Hook up the line
    +d.plot(ax)
    +ax.set_autoscale_on(False)  # Otherwise, infinite loop
    +
    +# Connect for changing the view limits
    +ax.callbacks.connect('xlim_changed', d.update)
    +ax.set_xlim(16, 365)
    +plt.show()
    +
    +# %%
    +# .. tags:: interactivity: zoom, interactivity: event-handling
    diff --git a/galleries/examples/event_handling/timers.py b/galleries/examples/event_handling/timers.py
    new file mode 100644
    index 000000000000..bf60492e353f
    --- /dev/null
    +++ b/galleries/examples/event_handling/timers.py
    @@ -0,0 +1,44 @@
    +"""
    +======
    +Timers
    +======
    +
    +Simple example of using general timer objects. This is used to update
    +the time placed in the title of the figure.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +from datetime import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def update_title(axes):
    +    axes.set_title(datetime.now())
    +    axes.figure.canvas.draw()
    +
    +fig, ax = plt.subplots()
    +
    +x = np.linspace(-3, 3)
    +ax.plot(x, x ** 2)
    +
    +# Create a new timer object. Set the interval to 100 milliseconds
    +# (1000 is default) and tell the timer what function should be called.
    +timer = fig.canvas.new_timer(interval=100)
    +timer.add_callback(update_title, ax)
    +timer.start()
    +
    +# Or could start the timer on first figure draw:
    +# def start_timer(event):
    +#     timer.start()
    +#     fig.canvas.mpl_disconnect(drawid)
    +# drawid = fig.canvas.mpl_connect('draw_event', start_timer)
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/trifinder_event_demo.py b/galleries/examples/event_handling/trifinder_event_demo.py
    new file mode 100644
    index 000000000000..e5b70b42724e
    --- /dev/null
    +++ b/galleries/examples/event_handling/trifinder_event_demo.py
    @@ -0,0 +1,70 @@
    +"""
    +====================
    +Trifinder Event Demo
    +====================
    +
    +Example showing the use of a TriFinder object.  As the mouse is moved over the
    +triangulation, the triangle under the cursor is highlighted and the index of
    +the triangle is displayed in the plot title.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Polygon
    +from matplotlib.tri import Triangulation
    +
    +
    +def update_polygon(tri):
    +    if tri == -1:
    +        points = [0, 0, 0]
    +    else:
    +        points = triang.triangles[tri]
    +    xs = triang.x[points]
    +    ys = triang.y[points]
    +    polygon.set_xy(np.column_stack([xs, ys]))
    +
    +
    +def on_mouse_move(event):
    +    if event.inaxes is None:
    +        tri = -1
    +    else:
    +        tri = trifinder(event.xdata, event.ydata)
    +    update_polygon(tri)
    +    ax.set_title(f'In triangle {tri}')
    +    event.canvas.draw()
    +
    +
    +# Create a Triangulation.
    +n_angles = 16
    +n_radii = 5
    +min_radius = 0.25
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi / n_angles
    +x = (radii*np.cos(angles)).flatten()
    +y = (radii*np.sin(angles)).flatten()
    +triang = Triangulation(x, y)
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +# Use the triangulation's default TriFinder object.
    +trifinder = triang.get_trifinder()
    +
    +# Setup plot and callbacks.
    +fig, ax = plt.subplots(subplot_kw={'aspect': 'equal'})
    +ax.triplot(triang, 'bo-')
    +polygon = Polygon([[0, 0], [0, 0]], facecolor='y')  # dummy data for (xs, ys)
    +update_polygon(-1)
    +ax.add_patch(polygon)
    +fig.canvas.mpl_connect('motion_notify_event', on_mouse_move)
    +plt.show()
    diff --git a/galleries/examples/event_handling/viewlims.py b/galleries/examples/event_handling/viewlims.py
    new file mode 100644
    index 000000000000..ebc3e6de5fb8
    --- /dev/null
    +++ b/galleries/examples/event_handling/viewlims.py
    @@ -0,0 +1,92 @@
    +"""
    +========
    +Viewlims
    +========
    +
    +Creates two identical panels.  Zooming in on the right panel will show
    +a rectangle in the first panel, denoting the zoomed region.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import functools
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Rectangle
    +
    +
    +# A class that will regenerate a fractal set as we zoom in, so that you
    +# can actually see the increasing detail.  A box in the left panel will show
    +# the area to which we are zoomed.
    +class MandelbrotDisplay:
    +    def __init__(self, h=500, w=500, niter=50, radius=2., power=2):
    +        self.height = h
    +        self.width = w
    +        self.niter = niter
    +        self.radius = radius
    +        self.power = power
    +
    +    def compute_image(self, xlim, ylim):
    +        self.x = np.linspace(*xlim, self.width)
    +        self.y = np.linspace(*ylim, self.height).reshape(-1, 1)
    +        c = self.x + 1.0j * self.y
    +        threshold_time = np.zeros((self.height, self.width))
    +        z = np.zeros(threshold_time.shape, dtype=complex)
    +        mask = np.ones(threshold_time.shape, dtype=bool)
    +        for i in range(self.niter):
    +            z[mask] = z[mask]**self.power + c[mask]
    +            mask = (np.abs(z) < self.radius)
    +            threshold_time += mask
    +        return threshold_time
    +
    +    def ax_update(self, ax):
    +        ax.set_autoscale_on(False)  # Otherwise, infinite loop
    +        # Get the number of points from the number of pixels in the window
    +        self.width, self.height = ax.patch.get_window_extent().size.round().astype(int)
    +        # Update the image object with our new data and extent
    +        ax.images[-1].set(data=self.compute_image(ax.get_xlim(), ax.get_ylim()),
    +                          extent=(*ax.get_xlim(), *ax.get_ylim()))
    +        ax.figure.canvas.draw_idle()
    +
    +
    +md = MandelbrotDisplay()
    +
    +fig1, (ax_full, ax_zoom) = plt.subplots(1, 2)
    +ax_zoom.imshow([[0]], origin="lower")  # Empty initial image.
    +ax_zoom.set_title("Zoom here")
    +
    +rect = Rectangle(
    +    [0, 0], 0, 0, facecolor="none", edgecolor="black", linewidth=1.0)
    +ax_full.add_patch(rect)
    +
    +
    +def update_rect(rect, ax):  # Let the rectangle track the bounds of the zoom axes.
    +    xlo, xhi = ax.get_xlim()
    +    ylo, yhi = ax.get_ylim()
    +    rect.set_bounds((xlo, ylo, xhi - xlo, yhi - ylo))
    +    ax.figure.canvas.draw_idle()
    +
    +
    +# Connect for changing the view limits.
    +ax_zoom.callbacks.connect("xlim_changed", functools.partial(update_rect, rect))
    +ax_zoom.callbacks.connect("ylim_changed", functools.partial(update_rect, rect))
    +
    +ax_zoom.callbacks.connect("xlim_changed", md.ax_update)
    +ax_zoom.callbacks.connect("ylim_changed", md.ax_update)
    +
    +# Initialize: trigger image computation by setting view limits; set colormap limits;
    +# copy image to full view.
    +ax_zoom.set(xlim=(-2, .5), ylim=(-1.25, 1.25))
    +im = ax_zoom.images[0]
    +ax_zoom.images[0].set(clim=(im.get_array().min(), im.get_array().max()))
    +ax_full.imshow(im.get_array(), extent=im.get_extent(), origin="lower")
    +
    +plt.show()
    diff --git a/galleries/examples/event_handling/zoom_window.py b/galleries/examples/event_handling/zoom_window.py
    new file mode 100644
    index 000000000000..6a90a175fb68
    --- /dev/null
    +++ b/galleries/examples/event_handling/zoom_window.py
    @@ -0,0 +1,54 @@
    +"""
    +========================
    +Zoom modifies other Axes
    +========================
    +
    +This example shows how to connect events in one window, for example, a mouse
    +press, to another figure window.
    +
    +If you click on a point in the first window, the z and y limits of the second
    +will be adjusted so that the center of the zoom in the second window will be
    +the (x, y) coordinates of the clicked point.
    +
    +Note the diameter of the circles in the scatter are defined in points**2, so
    +their size is independent of the zoom.
    +
    +.. note::
    +    This example exercises the interactive capabilities of Matplotlib, and this
    +    will not appear in the static documentation. Please run this code on your
    +    machine to see the interactivity.
    +
    +    You can copy and paste individual parts, or download the entire example
    +    using the link at the bottom of the page.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +figsrc, axsrc = plt.subplots(figsize=(3.7, 3.7))
    +figzoom, axzoom = plt.subplots(figsize=(3.7, 3.7))
    +axsrc.set(xlim=(0, 1), ylim=(0, 1), autoscale_on=False,
    +          title='Click to zoom')
    +axzoom.set(xlim=(0.45, 0.55), ylim=(0.4, 0.6), autoscale_on=False,
    +           title='Zoom window')
    +
    +x, y, s, c = np.random.rand(4, 200)
    +s *= 200
    +
    +axsrc.scatter(x, y, s, c)
    +axzoom.scatter(x, y, s, c)
    +
    +
    +def on_press(event):
    +    if event.button != 1:
    +        return
    +    x, y = event.xdata, event.ydata
    +    axzoom.set_xlim(x - 0.1, x + 0.1)
    +    axzoom.set_ylim(y - 0.1, y + 0.1)
    +    figzoom.canvas.draw()
    +
    +figsrc.canvas.mpl_connect('button_press_event', on_press)
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/README.txt b/galleries/examples/images_contours_and_fields/README.txt
    new file mode 100644
    index 000000000000..882de6f57c37
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/README.txt
    @@ -0,0 +1,4 @@
    +.. _images_contours_and_fields_examples:
    +
    +Images, contours and fields
    +===========================
    diff --git a/galleries/examples/images_contours_and_fields/affine_image.py b/galleries/examples/images_contours_and_fields/affine_image.py
    new file mode 100644
    index 000000000000..f56ebfbdf9d2
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/affine_image.py
    @@ -0,0 +1,76 @@
    +"""
    +============================
    +Affine transform of an image
    +============================
    +
    +
    +Prepending an affine transformation (`~.transforms.Affine2D`) to the :ref:`data
    +transform ` of an image allows to manipulate the image's shape and
    +orientation.  This is an example of the concept of :ref:`transform chaining
    +`.
    +
    +The image of the output should have its boundary match the dashed yellow
    +rectangle.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.transforms as mtransforms
    +
    +
    +def get_image():
    +    delta = 0.25
    +    x = y = np.arange(-3.0, 3.0, delta)
    +    X, Y = np.meshgrid(x, y)
    +    Z1 = np.exp(-X**2 - Y**2)
    +    Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +    Z = (Z1 - Z2)
    +    return Z
    +
    +
    +def do_plot(ax, Z, transform):
    +    im = ax.imshow(Z, interpolation='none',
    +                   origin='lower',
    +                   extent=[-2, 4, -3, 2], clip_on=True)
    +
    +    trans_data = transform + ax.transData
    +    im.set_transform(trans_data)
    +
    +    # display intended extent of the image
    +    x1, x2, y1, y2 = im.get_extent()
    +    ax.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "y--",
    +            transform=trans_data)
    +    ax.set_xlim(-5, 5)
    +    ax.set_ylim(-4, 4)
    +
    +
    +# prepare image and figure
    +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
    +Z = get_image()
    +
    +# image rotation
    +do_plot(ax1, Z, mtransforms.Affine2D().rotate_deg(30))
    +
    +# image skew
    +do_plot(ax2, Z, mtransforms.Affine2D().skew_deg(30, 15))
    +
    +# scale and reflection
    +do_plot(ax3, Z, mtransforms.Affine2D().scale(-1, .5))
    +
    +# everything and a translation
    +do_plot(ax4, Z, mtransforms.Affine2D().
    +        rotate_deg(30).skew_deg(30, 15).scale(-1, .5).translate(.5, -1))
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.transforms.Affine2D`
    diff --git a/galleries/examples/images_contours_and_fields/barb_demo.py b/galleries/examples/images_contours_and_fields/barb_demo.py
    new file mode 100644
    index 000000000000..9229b5262a2c
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/barb_demo.py
    @@ -0,0 +1,65 @@
    +"""
    +==========
    +Wind barbs
    +==========
    +
    +Demonstration of wind barb plots.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.linspace(-5, 5, 5)
    +X, Y = np.meshgrid(x, x)
    +U, V = 12 * X, 12 * Y
    +
    +data = [(-1.5, .5, -6, -6),
    +        (1, -1, -46, 46),
    +        (-3, -1, 11, -11),
    +        (1, 1.5, 80, 80),
    +        (0.5, 0.25, 25, 15),
    +        (-1.5, -0.5, -5, 40)]
    +
    +data = np.array(data, dtype=[('x', np.float32), ('y', np.float32),
    +                             ('u', np.float32), ('v', np.float32)])
    +
    +fig1, axs1 = plt.subplots(nrows=2, ncols=2)
    +# Default parameters, uniform grid
    +axs1[0, 0].barbs(X, Y, U, V)
    +
    +# Arbitrary set of vectors, make them longer and change the pivot point
    +# (point around which they're rotated) to be the middle
    +axs1[0, 1].barbs(
    +    data['x'], data['y'], data['u'], data['v'], length=8, pivot='middle')
    +
    +# Showing colormapping with uniform grid.  Fill the circle for an empty barb,
    +# don't round the values, and change some of the size parameters
    +axs1[1, 0].barbs(
    +    X, Y, U, V, np.sqrt(U ** 2 + V ** 2), fill_empty=True, rounding=False,
    +    sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3))
    +
    +# Change colors as well as the increments for parts of the barbs
    +axs1[1, 1].barbs(data['x'], data['y'], data['u'], data['v'], flagcolor='r',
    +                 barbcolor=['b', 'g'], flip_barb=True,
    +                 barb_increments=dict(half=10, full=20, flag=100))
    +
    +# Masked arrays are also supported
    +masked_u = np.ma.masked_array(data['u'])
    +masked_u[4] = 1000  # Bad value that should not be plotted when masked
    +masked_u[4] = np.ma.masked
    +
    +# %%
    +# Identical plot to panel 2 in the first figure, but with the point at
    +# (0.5, 0.25) missing (masked)
    +fig2, ax2 = plt.subplots()
    +ax2.barbs(data['x'], data['y'], masked_u, data['v'], length=8, pivot='middle')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.barbs` / `matplotlib.pyplot.barbs`
    diff --git a/galleries/examples/images_contours_and_fields/barcode_demo.py b/galleries/examples/images_contours_and_fields/barcode_demo.py
    new file mode 100644
    index 000000000000..5df58535650d
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/barcode_demo.py
    @@ -0,0 +1,53 @@
    +"""
    +=======
    +Barcode
    +=======
    +This demo shows how to produce a bar code.
    +
    +The figure size is calculated so that the width in pixels is a multiple of the
    +number of data points to prevent interpolation artifacts. Additionally, the
    +``Axes`` is defined to span the whole figure and all ``Axis`` are turned off.
    +
    +The data itself is rendered with `~.Axes.imshow` using
    +
    +- ``code.reshape(1, -1)`` to turn the data into a 2D array with one row.
    +- ``imshow(..., aspect='auto')`` to allow for non-square pixels.
    +- ``imshow(..., interpolation='nearest')`` to prevent blurred edges. This
    +  should not happen anyway because we fine-tuned the figure width in pixels,
    +  but just to be safe.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +code = np.array([
    +    1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1,
    +    0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
    +    1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
    +    1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1])
    +
    +pixel_per_bar = 4
    +dpi = 100
    +
    +fig = plt.figure(figsize=(len(code) * pixel_per_bar / dpi, 2), dpi=dpi)
    +ax = fig.add_axes((0, 0, 1, 1))  # span the whole figure
    +ax.set_axis_off()
    +ax.imshow(code.reshape(1, -1), cmap='binary', aspect='auto',
    +          interpolation='nearest')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.add_axes`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: imshow
    +#    purpose: fun
    diff --git a/galleries/examples/images_contours_and_fields/colormap_interactive_adjustment.py b/galleries/examples/images_contours_and_fields/colormap_interactive_adjustment.py
    new file mode 100644
    index 000000000000..3db799894c95
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/colormap_interactive_adjustment.py
    @@ -0,0 +1,33 @@
    +"""
    +========================================
    +Interactive adjustment of colormap range
    +========================================
    +
    +Demonstration of how a colorbar can be used to interactively adjust the
    +range of colormapping on an image. To use the interactive feature, you must
    +be in either zoom mode (magnifying glass toolbar button) or
    +pan mode (4-way arrow toolbar button) and click inside the colorbar.
    +
    +When zooming, the bounding box of the zoom region defines the new vmin and
    +vmax of the norm. Zooming using the right mouse button will expand the
    +vmin and vmax proportionally to the selected region, in the same manner that
    +one can zoom out on an axis. When panning, the vmin and vmax of the norm are
    +both shifted according to the direction of movement. The
    +Home/Back/Forward buttons can also be used to get back to a previous state.
    +
    +.. redirect-from:: /gallery/userdemo/colormap_interactive_adjustment
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.linspace(0, 2 * np.pi, 1024)
    +data2d = np.sin(t)[:, np.newaxis] * np.cos(t)[np.newaxis, :]
    +
    +fig, ax = plt.subplots()
    +im = ax.imshow(data2d)
    +ax.set_title('Pan on the colorbar to shift the color mapping\n'
    +             'Zoom on the colorbar to scale the color mapping')
    +
    +fig.colorbar(im, ax=ax, label='Interactive colorbar')
    +
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/colormap_normalizations.py b/galleries/examples/images_contours_and_fields/colormap_normalizations.py
    new file mode 100644
    index 000000000000..1d81336b4964
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/colormap_normalizations.py
    @@ -0,0 +1,153 @@
    +"""
    +=======================
    +Colormap normalizations
    +=======================
    +
    +Demonstration of using norm to map colormaps onto data in non-linear ways.
    +
    +.. redirect-from:: /gallery/userdemo/colormap_normalizations
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors as colors
    +
    +N = 100
    +
    +# %%
    +# LogNorm
    +# -------
    +# This example data has a low hump with a spike coming out of its center. If plotted
    +# using a linear colour scale, then only the spike will be visible. To see both hump and
    +# spike, this requires the z/colour axis on a log scale.
    +#
    +# Instead of transforming the data with ``pcolor(log10(Z))``, the color mapping can be
    +# made logarithmic using a `.LogNorm`.
    +
    +X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2)
    +Z = Z1 + 50 * Z2
    +
    +fig, ax = plt.subplots(2, 1)
    +
    +pcm = ax[0].pcolor(X, Y, Z, cmap='PuBu_r', shading='nearest')
    +fig.colorbar(pcm, ax=ax[0], extend='max', label='linear scaling')
    +
    +pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r', shading='nearest',
    +                   norm=colors.LogNorm(vmin=Z.min(), vmax=Z.max()))
    +fig.colorbar(pcm, ax=ax[1], extend='max', label='LogNorm')
    +
    +# %%
    +# PowerNorm
    +# ---------
    +# This example data mixes a power-law trend in X with a rectified sine wave in Y. If
    +# plotted using a linear colour scale, then the power-law trend in X partially obscures
    +# the sine wave in Y.
    +#
    +# The power law can be removed using a `.PowerNorm`.
    +
    +X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)]
    +Z = (1 + np.sin(Y * 10)) * X**2
    +
    +fig, ax = plt.subplots(2, 1)
    +
    +pcm = ax[0].pcolormesh(X, Y, Z, cmap='PuBu_r', shading='nearest')
    +fig.colorbar(pcm, ax=ax[0], extend='max', label='linear scaling')
    +
    +pcm = ax[1].pcolormesh(X, Y, Z, cmap='PuBu_r', shading='nearest',
    +                       norm=colors.PowerNorm(gamma=0.5))
    +fig.colorbar(pcm, ax=ax[1], extend='max', label='PowerNorm')
    +
    +# %%
    +# SymLogNorm
    +# ----------
    +# This example data has two humps, one negative and one positive, The positive hump has
    +# 5 times the amplitude of the negative. If plotted with a linear colour scale, then
    +# the detail in the negative hump is obscured.
    +#
    +# Here we logarithmically scale the positive and negative data separately with
    +# `.SymLogNorm`.
    +#
    +# Note that colorbar labels do not come out looking very good.
    +
    +X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (5 * Z1 - Z2) * 2
    +
    +fig, ax = plt.subplots(2, 1)
    +
    +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       vmin=-np.max(Z))
    +fig.colorbar(pcm, ax=ax[0], extend='both', label='linear scaling')
    +
    +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       norm=colors.SymLogNorm(linthresh=0.015,
    +                                              vmin=-10.0, vmax=10.0, base=10))
    +fig.colorbar(pcm, ax=ax[1], extend='both', label='SymLogNorm')
    +
    +# %%
    +# Custom Norm
    +# -----------
    +# Alternatively, the above example data can be scaled with a customized normalization.
    +# This one normalizes the negative data differently from the positive.
    +
    +
    +# Example of making your own norm.  Also see matplotlib.colors.
    +# From Joe Kington: This one gives two different linear ramps:
    +class MidpointNormalize(colors.Normalize):
    +    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
    +        self.midpoint = midpoint
    +        super().__init__(vmin, vmax, clip)
    +
    +    def __call__(self, value, clip=None):
    +        # I'm ignoring masked values and all kinds of edge cases to make a
    +        # simple example...
    +        x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
    +        return np.ma.masked_array(np.interp(value, x, y))
    +
    +
    +# %%
    +fig, ax = plt.subplots(2, 1)
    +
    +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       vmin=-np.max(Z))
    +fig.colorbar(pcm, ax=ax[0], extend='both', label='linear scaling')
    +
    +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       norm=MidpointNormalize(midpoint=0))
    +fig.colorbar(pcm, ax=ax[1], extend='both', label='Custom norm')
    +
    +# %%
    +# BoundaryNorm
    +# ------------
    +# For arbitrarily dividing the color scale, the `.BoundaryNorm` may be used; by
    +# providing the boundaries for colors, this norm puts the first color in between the
    +# first pair, the second color between the second pair, etc.
    +
    +fig, ax = plt.subplots(3, 1, layout='constrained')
    +
    +pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       vmin=-np.max(Z))
    +fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical',
    +             label='linear scaling')
    +
    +# Evenly-spaced bounds gives a contour-like effect.
    +bounds = np.linspace(-2, 2, 11)
    +norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
    +pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       norm=norm)
    +fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical',
    +             label='BoundaryNorm\nlinspace(-2, 2, 11)')
    +
    +# Unevenly-spaced bounds changes the colormapping.
    +bounds = np.array([-1, -0.5, 0, 2.5, 5])
    +norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
    +pcm = ax[2].pcolormesh(X, Y, Z, cmap='RdBu_r', shading='nearest',
    +                       norm=norm)
    +fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical',
    +             label='BoundaryNorm\n[-1, -0.5, 0, 2.5, 5]')
    +
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py b/galleries/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py
    new file mode 100644
    index 000000000000..14d54d170e7a
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/colormap_normalizations_symlognorm.py
    @@ -0,0 +1,85 @@
    +"""
    +==================================
    +Colormap normalizations SymLogNorm
    +==================================
    +
    +Demonstration of using norm to map colormaps onto data in non-linear ways.
    +
    +.. redirect-from:: /gallery/userdemo/colormap_normalization_symlognorm
    +"""
    +
    +# %%
    +# Synthetic dataset consisting of two humps, one negative and one positive,
    +# the positive with 8-times the amplitude.
    +# Linearly, the negative hump is almost invisible,
    +# and it is very difficult to see any detail of its profile.
    +# With the logarithmic scaling applied to both positive and negative values,
    +# it is much easier to see the shape of each hump.
    +#
    +# See `~.colors.SymLogNorm`.
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors as colors
    +
    +
    +def rbf(x, y):
    +    return 1.0 / (1 + 5 * ((x ** 2) + (y ** 2)))
    +
    +N = 200
    +gain = 8
    +X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
    +Z1 = rbf(X + 0.5, Y + 0.5)
    +Z2 = rbf(X - 0.5, Y - 0.5)
    +Z = gain * Z1 - Z2
    +
    +shadeopts = {'cmap': 'PRGn', 'shading': 'gouraud'}
    +colormap = 'PRGn'
    +lnrwidth = 0.5
    +
    +fig, ax = plt.subplots(2, 1, sharex=True, sharey=True)
    +
    +pcm = ax[0].pcolormesh(X, Y, Z,
    +                       norm=colors.SymLogNorm(linthresh=lnrwidth, linscale=1,
    +                                              vmin=-gain, vmax=gain, base=10),
    +                       **shadeopts)
    +fig.colorbar(pcm, ax=ax[0], extend='both')
    +ax[0].text(-2.5, 1.5, 'symlog')
    +
    +pcm = ax[1].pcolormesh(X, Y, Z, vmin=-gain, vmax=gain,
    +                       **shadeopts)
    +fig.colorbar(pcm, ax=ax[1], extend='both')
    +ax[1].text(-2.5, 1.5, 'linear')
    +
    +
    +# %%
    +# In order to find the best visualization for any particular dataset,
    +# it may be necessary to experiment with multiple different color scales.
    +# As well as the `~.colors.SymLogNorm` scaling, there is also
    +# the option of using `~.colors.AsinhNorm` (experimental), which has a smoother
    +# transition between the linear and logarithmic regions of the transformation
    +# applied to the data values, "Z".
    +# In the plots below, it may be possible to see contour-like artifacts
    +# around each hump despite there being no sharp features
    +# in the dataset itself. The ``asinh`` scaling shows a smoother shading
    +# of each hump.
    +
    +fig, ax = plt.subplots(2, 1, sharex=True, sharey=True)
    +
    +pcm = ax[0].pcolormesh(X, Y, Z,
    +                       norm=colors.SymLogNorm(linthresh=lnrwidth, linscale=1,
    +                                              vmin=-gain, vmax=gain, base=10),
    +                       **shadeopts)
    +fig.colorbar(pcm, ax=ax[0], extend='both')
    +ax[0].text(-2.5, 1.5, 'symlog')
    +
    +pcm = ax[1].pcolormesh(X, Y, Z,
    +                       norm=colors.AsinhNorm(linear_width=lnrwidth,
    +                                             vmin=-gain, vmax=gain),
    +                       **shadeopts)
    +fig.colorbar(pcm, ax=ax[1], extend='both')
    +ax[1].text(-2.5, 1.5, 'asinh')
    +
    +
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/contour_corner_mask.py b/galleries/examples/images_contours_and_fields/contour_corner_mask.py
    new file mode 100644
    index 000000000000..696231146733
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contour_corner_mask.py
    @@ -0,0 +1,50 @@
    +"""
    +===================
    +Contour corner mask
    +===================
    +
    +Illustrate the difference between ``corner_mask=False`` and
    +``corner_mask=True`` for masked contour plots.  The default is controlled by
    +:rc:`contour.corner_mask`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Data to plot.
    +x, y = np.meshgrid(np.arange(7), np.arange(10))
    +z = np.sin(0.5 * x) * np.cos(0.52 * y)
    +
    +# Mask various z values.
    +mask = np.zeros_like(z, dtype=bool)
    +mask[2, 3:5] = True
    +mask[3:5, 4] = True
    +mask[7, 2] = True
    +mask[5, 0] = True
    +mask[0, 6] = True
    +z = np.ma.array(z, mask=mask)
    +
    +corner_masks = [False, True]
    +fig, axs = plt.subplots(ncols=2)
    +for ax, corner_mask in zip(axs, corner_masks):
    +    cs = ax.contourf(x, y, z, corner_mask=corner_mask)
    +    ax.contour(cs, colors='k')
    +    ax.set_title(f'{corner_mask=}')
    +
    +    # Plot grid.
    +    ax.grid(c='k', ls='-', alpha=0.3)
    +
    +    # Indicate masked points with red circles.
    +    ax.plot(np.ma.array(x, mask=~mask), y, 'ro')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    diff --git a/galleries/examples/images_contours_and_fields/contour_demo.py b/galleries/examples/images_contours_and_fields/contour_demo.py
    new file mode 100644
    index 000000000000..05fd3d5e3be8
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contour_demo.py
    @@ -0,0 +1,121 @@
    +"""
    +============
    +Contour Demo
    +============
    +
    +Illustrate simple contour plotting, contours on an image with
    +a colorbar for the contours, and labelled contours.
    +
    +See also the :doc:`contour image example
    +`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +delta = 0.025
    +x = np.arange(-3.0, 3.0, delta)
    +y = np.arange(-2.0, 2.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +# %%
    +# Create a simple contour plot with labels using default colors.  The inline
    +# argument to clabel will control whether the labels are draw over the line
    +# segments of the contour, removing the lines beneath the label.
    +
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z)
    +ax.clabel(CS, fontsize=10)
    +ax.set_title('Simplest default with labels')
    +
    +# %%
    +# Contour labels can be placed manually by providing list of positions (in data
    +# coordinate).  See :doc:`/gallery/event_handling/ginput_manual_clabel_sgskip`
    +# for interactive placement.
    +
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z)
    +manual_locations = [
    +    (-1, -1.4), (-0.62, -0.7), (-2, 0.5), (1.7, 1.2), (2.0, 1.4), (2.4, 1.7)]
    +ax.clabel(CS, fontsize=10, manual=manual_locations)
    +ax.set_title('labels at selected locations')
    +
    +# %%
    +# You can force all the contours to be the same color.
    +
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z, 6, colors='k')  # Negative contours default to dashed.
    +ax.clabel(CS, fontsize=9)
    +ax.set_title('Single color - negative contours dashed')
    +
    +# %%
    +# You can set negative contours to be solid instead of dashed:
    +
    +plt.rcParams['contour.negative_linestyle'] = 'solid'
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z, 6, colors='k')  # Negative contours default to dashed.
    +ax.clabel(CS, fontsize=9)
    +ax.set_title('Single color - negative contours solid')
    +
    +# %%
    +# And you can manually specify the colors of the contour
    +
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z, 6,
    +                linewidths=np.arange(.5, 4, .5),
    +                colors=('r', 'green', 'blue', (1, 1, 0), '#afeeee', '0.5'),
    +                )
    +ax.clabel(CS, fontsize=9)
    +ax.set_title('Crazy lines')
    +
    +# %%
    +# Or you can use a colormap to specify the colors; the default
    +# colormap will be used for the contour lines
    +
    +fig, ax = plt.subplots()
    +im = ax.imshow(Z, interpolation='bilinear', origin='lower',
    +               cmap="gray", extent=(-3, 3, -2, 2))
    +levels = np.arange(-1.2, 1.6, 0.2)
    +CS = ax.contour(Z, levels, origin='lower', cmap='flag', extend='both',
    +                linewidths=2, extent=(-3, 3, -2, 2))
    +
    +# Thicken the zero contour.
    +lws = np.resize(CS.get_linewidth(), len(levels))
    +lws[6] = 4
    +CS.set_linewidth(lws)
    +
    +ax.clabel(CS, levels[1::2],  # label every second level
    +          fmt='%1.1f', fontsize=14)
    +
    +# make a colorbar for the contour lines
    +CB = fig.colorbar(CS, shrink=0.8)
    +
    +ax.set_title('Lines with colorbar')
    +
    +# We can still add a colorbar for the image, too.
    +CBI = fig.colorbar(im, orientation='horizontal', shrink=0.8)
    +
    +# This makes the original colorbar look a bit out of place,
    +# so let's improve its position.
    +
    +l, b, w, h = ax.get_position().bounds
    +ll, bb, ww, hh = CB.ax.get_position().bounds
    +CB.ax.set_position([ll, b + 0.1*h, ww, h*0.8])
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel`
    +#    - `matplotlib.axes.Axes.get_position`
    +#    - `matplotlib.axes.Axes.set_position`
    diff --git a/galleries/examples/images_contours_and_fields/contour_image.py b/galleries/examples/images_contours_and_fields/contour_image.py
    new file mode 100644
    index 000000000000..28ed02030aee
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contour_image.py
    @@ -0,0 +1,107 @@
    +"""
    +=============
    +Contour image
    +=============
    +
    +Test combinations of contouring, filled contouring, and image plotting.
    +For contour labelling, see also the :doc:`contour demo example
    +`.
    +
    +The emphasis in this demo is on showing how to make contours register
    +correctly on images, and on how to get both of them oriented as desired.
    +In particular, note the usage of the :ref:`"origin" and "extent"
    +` keyword arguments to imshow and
    +contour.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cm
    +
    +# Default delta is large because that makes it fast, and it illustrates
    +# the correct registration between image and contours.
    +delta = 0.5
    +
    +extent = (-3, 4, -4, 3)
    +
    +x = np.arange(-3.0, 4.001, delta)
    +y = np.arange(-4.0, 3.001, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +# Boost the upper limit to avoid truncation errors.
    +levels = np.arange(-2.0, 1.601, 0.4)
    +
    +norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
    +cmap = plt.colormaps["PRGn"]
    +
    +fig, _axs = plt.subplots(nrows=2, ncols=2)
    +fig.subplots_adjust(hspace=0.3)
    +axs = _axs.flatten()
    +
    +cset1 = axs[0].contourf(X, Y, Z, levels, norm=norm,
    +                        cmap=cmap.resampled(len(levels) - 1))
    +# It is not necessary, but for the colormap, we need only the
    +# number of levels minus 1.  To avoid discretization error, use
    +# either this number or a large number such as the default (256).
    +
    +# If we want lines as well as filled regions, we need to call
    +# contour separately; don't try to change the edgecolor or edgewidth
    +# of the polygons in the collections returned by contourf.
    +# Use levels output from previous call to guarantee they are the same.
    +
    +cset2 = axs[0].contour(X, Y, Z, cset1.levels, colors='k')
    +
    +# We don't really need dashed contour lines to indicate negative
    +# regions, so let's turn them off.
    +cset2.set_linestyle('solid')
    +
    +# It is easier here to make a separate call to contour than
    +# to set up an array of colors and linewidths.
    +# We are making a thick green line as a zero contour.
    +# Specify the zero level as a tuple with only 0 in it.
    +
    +cset3 = axs[0].contour(X, Y, Z, (0,), colors='g', linewidths=2)
    +axs[0].set_title('Filled contours')
    +fig.colorbar(cset1, ax=axs[0])
    +
    +
    +axs[1].imshow(Z, extent=extent, cmap=cmap, norm=norm)
    +axs[1].contour(Z, levels, colors='k', origin='upper', extent=extent)
    +axs[1].set_title("Image, origin 'upper'")
    +
    +axs[2].imshow(Z, origin='lower', extent=extent, cmap=cmap, norm=norm)
    +axs[2].contour(Z, levels, colors='k', origin='lower', extent=extent)
    +axs[2].set_title("Image, origin 'lower'")
    +
    +# We will use the interpolation "nearest" here to show the actual
    +# image pixels.
    +# Note that the contour lines don't extend to the edge of the box.
    +# This is intentional. The Z values are defined at the center of each
    +# image pixel (each color block on the following subplot), so the
    +# domain that is contoured does not extend beyond these pixel centers.
    +im = axs[3].imshow(Z, interpolation='nearest', extent=extent,
    +                   cmap=cmap, norm=norm)
    +axs[3].contour(Z, levels, colors='k', origin='image', extent=extent)
    +ylim = axs[3].get_ylim()
    +axs[3].set_ylim(ylim[::-1])
    +axs[3].set_title("Origin from rc, reversed y-axis")
    +fig.colorbar(im, ax=axs[3])
    +
    +fig.tight_layout()
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors.Normalize`
    diff --git a/galleries/examples/images_contours_and_fields/contour_label_demo.py b/galleries/examples/images_contours_and_fields/contour_label_demo.py
    new file mode 100644
    index 000000000000..0b24b57f6afd
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contour_label_demo.py
    @@ -0,0 +1,87 @@
    +"""
    +==================
    +Contour Label Demo
    +==================
    +
    +Illustrate some of the more advanced things that one can do with
    +contour labels.
    +
    +See also the :doc:`contour demo example
    +`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as ticker
    +
    +# %%
    +# Define our surface
    +
    +delta = 0.025
    +x = np.arange(-3.0, 3.0, delta)
    +y = np.arange(-2.0, 2.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +# %%
    +# Make contour labels with custom level formatters
    +
    +
    +# This custom formatter removes trailing zeros, e.g. "1.0" becomes "1", and
    +# then adds a percent sign.
    +def fmt(x):
    +    s = f"{x:.1f}"
    +    if s.endswith("0"):
    +        s = f"{x:.0f}"
    +    return rf"{s} \%" if plt.rcParams["text.usetex"] else f"{s} %"
    +
    +
    +# Basic contour plot
    +fig, ax = plt.subplots()
    +CS = ax.contour(X, Y, Z)
    +
    +ax.clabel(CS, CS.levels, fmt=fmt, fontsize=10)
    +
    +# %%
    +# Label contours with arbitrary strings using a dictionary
    +
    +fig1, ax1 = plt.subplots()
    +
    +# Basic contour plot
    +CS1 = ax1.contour(X, Y, Z)
    +
    +fmt = {}
    +strs = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh']
    +for l, s in zip(CS1.levels, strs):
    +    fmt[l] = s
    +
    +# Label every other level using strings
    +ax1.clabel(CS1, CS1.levels[::2], fmt=fmt, fontsize=10)
    +
    +# %%
    +# Use a Formatter
    +
    +fig2, ax2 = plt.subplots()
    +
    +CS2 = ax2.contour(X, Y, 100**Z, locator=plt.LogLocator())
    +fmt = ticker.LogFormatterMathtext()
    +fmt.create_dummy_axis()
    +ax2.clabel(CS2, CS2.levels, fmt=fmt)
    +ax2.set_title("$100^Z$")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel`
    +#    - `matplotlib.ticker.LogFormatterMathtext`
    +#    - `matplotlib.ticker.TickHelper.create_dummy_axis`
    diff --git a/galleries/examples/images_contours_and_fields/contourf_demo.py b/galleries/examples/images_contours_and_fields/contourf_demo.py
    new file mode 100644
    index 000000000000..18c13d922e38
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contourf_demo.py
    @@ -0,0 +1,138 @@
    +"""
    +=============
    +Contourf demo
    +=============
    +
    +How to use the `.axes.Axes.contourf` method to create filled contour plots.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +delta = 0.025
    +
    +x = y = np.arange(-3.0, 3.01, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +nr, nc = Z.shape
    +
    +# put NaNs in one corner:
    +Z[-nr // 6:, -nc // 6:] = np.nan
    +# contourf will convert these to masked
    +
    +
    +Z = np.ma.array(Z)
    +# mask another corner:
    +Z[:nr // 6, :nc // 6] = np.ma.masked
    +
    +# mask a circle in the middle:
    +interior = np.sqrt(X**2 + Y**2) < 0.5
    +Z[interior] = np.ma.masked
    +
    +# %%
    +# Automatic contour levels
    +# ------------------------
    +# We are using automatic selection of contour levels; this is usually not such
    +# a good idea, because they don't occur on nice boundaries, but we do it here
    +# for purposes of illustration.
    +
    +fig1, ax2 = plt.subplots(layout='constrained')
    +CS = ax2.contourf(X, Y, Z, 10, cmap="bone")
    +
    +# Note that in the following, we explicitly pass in a subset of the contour
    +# levels used for the filled contours.  Alternatively, we could pass in
    +# additional levels to provide extra resolution, or leave out the *levels*
    +# keyword argument to use all of the original levels.
    +
    +CS2 = ax2.contour(CS, levels=CS.levels[::2], colors='r')
    +
    +ax2.set_title('Nonsense (3 masked regions)')
    +ax2.set_xlabel('word length anomaly')
    +ax2.set_ylabel('sentence length anomaly')
    +
    +# Make a colorbar for the ContourSet returned by the contourf call.
    +cbar = fig1.colorbar(CS)
    +cbar.ax.set_ylabel('verbosity coefficient')
    +# Add the contour line levels to the colorbar
    +cbar.add_lines(CS2)
    +
    +# %%
    +# Explicit contour levels
    +# -----------------------
    +# Now make a contour plot with the levels specified, and with the colormap
    +# generated automatically from a list of colors.
    +
    +fig2, ax2 = plt.subplots(layout='constrained')
    +levels = [-1.5, -1, -0.5, 0, 0.5, 1]
    +CS3 = ax2.contourf(X, Y, Z, levels, colors=('r', 'g', 'b'), extend='both')
    +# Our data range extends outside the range of levels; make
    +# data below the lowest contour level yellow, and above the
    +# highest level cyan:
    +CS3.cmap.set_under('yellow')
    +CS3.cmap.set_over('cyan')
    +
    +CS4 = ax2.contour(X, Y, Z, levels, colors=('k',), linewidths=(3,))
    +ax2.set_title('Listed colors (3 masked regions)')
    +ax2.clabel(CS4, fmt='%2.1f', colors='w', fontsize=14)
    +
    +# Notice that the colorbar gets all the information it
    +# needs from the ContourSet object, CS3.
    +fig2.colorbar(CS3)
    +
    +# %%
    +# Extension settings
    +# ------------------
    +# Illustrate all 4 possible "extend" settings:
    +extends = ["neither", "both", "min", "max"]
    +cmap = plt.colormaps["winter"].with_extremes(under="magenta", over="yellow")
    +# Note: contouring simply excludes masked or nan regions, so
    +# instead of using the "bad" colormap value for them, it draws
    +# nothing at all in them.  Therefore, the following would have
    +# no effect:
    +# cmap.set_bad("red")
    +
    +fig, axs = plt.subplots(2, 2, layout="constrained")
    +
    +for ax, extend in zip(axs.flat, extends):
    +    cs = ax.contourf(X, Y, Z, levels, cmap=cmap, extend=extend)
    +    fig.colorbar(cs, ax=ax, shrink=0.9)
    +    ax.set_title("extend = %s" % extend)
    +    ax.locator_params(nbins=4)
    +
    +plt.show()
    +
    +# %%
    +# Orient contour plots using the origin keyword
    +# ---------------------------------------------
    +# This code demonstrates orienting contour plot data using the "origin" keyword
    +
    +x = np.arange(1, 10)
    +y = x.reshape(-1, 1)
    +h = x * y
    +
    +fig, (ax1, ax2) = plt.subplots(ncols=2)
    +
    +ax1.set_title("origin='upper'")
    +ax2.set_title("origin='lower'")
    +ax1.contourf(h, levels=np.arange(5, 70, 5), extend='both', origin="upper")
    +ax2.contourf(h, levels=np.arange(5, 70, 5), extend='both', origin="lower")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.axes.Axes.clabel` / `matplotlib.pyplot.clabel`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors.Colormap`
    +#    - `matplotlib.colors.Colormap.set_bad`
    +#    - `matplotlib.colors.Colormap.set_under`
    +#    - `matplotlib.colors.Colormap.set_over`
    diff --git a/galleries/examples/images_contours_and_fields/contourf_hatching.py b/galleries/examples/images_contours_and_fields/contourf_hatching.py
    new file mode 100644
    index 000000000000..020c20b44ec4
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contourf_hatching.py
    @@ -0,0 +1,55 @@
    +"""
    +=================
    +Contourf hatching
    +=================
    +
    +Demo filled contour plots with hatched patterns.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# invent some numbers, turning the x and y arrays into simple
    +# 2d arrays, which make combining them together easier.
    +x = np.linspace(-3, 5, 150).reshape(1, -1)
    +y = np.linspace(-3, 5, 120).reshape(-1, 1)
    +z = np.cos(x) + np.sin(y)
    +
    +# we no longer need x and y to be 2 dimensional, so flatten them.
    +x, y = x.flatten(), y.flatten()
    +
    +# %%
    +# Plot 1: the simplest hatched plot with a colorbar
    +
    +fig1, ax1 = plt.subplots()
    +cs = ax1.contourf(x, y, z, hatches=['-', '/', '\\', '//'],
    +                  cmap='gray', extend='both', alpha=0.5)
    +fig1.colorbar(cs)
    +
    +# %%
    +# Plot 2: a plot of hatches without color with a legend
    +
    +fig2, ax2 = plt.subplots()
    +n_levels = 6
    +ax2.contour(x, y, z, n_levels, colors='black', linestyles='-')
    +cs = ax2.contourf(x, y, z, n_levels, colors='none',
    +                  hatches=['.', '/', '\\', None, '\\\\', '*'],
    +                  extend='lower')
    +
    +# create a legend for the contour set
    +artists, labels = cs.legend_elements(str_format='{:2.1f}'.format)
    +ax2.legend(artists, labels, handleheight=2, framealpha=1)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#    - `matplotlib.contour.ContourSet`
    +#    - `matplotlib.contour.ContourSet.legend_elements`
    diff --git a/galleries/examples/images_contours_and_fields/contourf_log.py b/galleries/examples/images_contours_and_fields/contourf_log.py
    new file mode 100644
    index 000000000000..408976adb9c2
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contourf_log.py
    @@ -0,0 +1,62 @@
    +"""
    +============================
    +Contourf and log color scale
    +============================
    +
    +Demonstrate use of a log color scale in contourf
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +from numpy import ma
    +
    +from matplotlib import ticker
    +
    +N = 100
    +x = np.linspace(-3.0, 3.0, N)
    +y = np.linspace(-2.0, 2.0, N)
    +
    +X, Y = np.meshgrid(x, y)
    +
    +# A low hump with a spike coming out.
    +# Needs to have z/colour axis on a log scale, so we see both hump and spike.
    +# A linear scale only shows the spike.
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2)
    +z = Z1 + 50 * Z2
    +
    +# Put in some negative values (lower left corner) to cause trouble with logs:
    +z[:5, :5] = -1
    +
    +# The following is not strictly essential, but it will eliminate
    +# a warning.  Comment it out to see the warning.
    +z = ma.masked_where(z <= 0, z)
    +
    +
    +# Automatic selection of levels works; setting the
    +# log locator tells contourf to use a log scale:
    +fig, ax = plt.subplots()
    +cs = ax.contourf(X, Y, z, locator=ticker.LogLocator(), cmap="PuBu_r")
    +
    +# Alternatively, you can manually set the levels
    +# and the norm:
    +# lev_exp = np.arange(np.floor(np.log10(z.min())-1),
    +#                    np.ceil(np.log10(z.max())+1))
    +# levs = np.power(10, lev_exp)
    +# cs = ax.contourf(X, Y, z, levs, norm=colors.LogNorm())
    +
    +cbar = fig.colorbar(cs)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#    - `matplotlib.ticker.LogLocator`
    diff --git a/galleries/examples/images_contours_and_fields/contours_in_optimization_demo.py b/galleries/examples/images_contours_and_fields/contours_in_optimization_demo.py
    new file mode 100644
    index 000000000000..ec0d5d384d9a
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/contours_in_optimization_demo.py
    @@ -0,0 +1,61 @@
    +"""
    +==============================================
    +Contouring the solution space of optimizations
    +==============================================
    +
    +Contour plotting is particularly handy when illustrating the solution
    +space of optimization problems.  Not only can `.axes.Axes.contour` be
    +used to represent the topography of the objective function, it can be
    +used to generate boundary curves of the constraint functions.  The
    +constraint lines can be drawn with
    +`~matplotlib.patheffects.TickedStroke` to distinguish the valid and
    +invalid sides of the constraint boundaries.
    +
    +`.axes.Axes.contour` generates curves with larger values to the left
    +of the contour.  The angle parameter is measured zero ahead with
    +increasing values to the left.  Consequently, when using
    +`~matplotlib.patheffects.TickedStroke` to illustrate a constraint in
    +a typical optimization problem, the angle should be set between
    +zero and 180 degrees.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import patheffects
    +
    +fig, ax = plt.subplots(figsize=(6, 6))
    +
    +nx = 101
    +ny = 105
    +
    +# Set up survey vectors
    +xvec = np.linspace(0.001, 4.0, nx)
    +yvec = np.linspace(0.001, 4.0, ny)
    +
    +# Set up survey matrices.  Design disk loading and gear ratio.
    +x1, x2 = np.meshgrid(xvec, yvec)
    +
    +# Evaluate some stuff to plot
    +obj = x1**2 + x2**2 - 2*x1 - 2*x2 + 2
    +g1 = -(3*x1 + x2 - 5.5)
    +g2 = -(x1 + 2*x2 - 4.5)
    +g3 = 0.8 + x1**-3 - x2
    +
    +cntr = ax.contour(x1, x2, obj, [0.01, 0.1, 0.5, 1, 2, 4, 8, 16],
    +                  colors='black')
    +ax.clabel(cntr, fmt="%2.1f", use_clabeltext=True)
    +
    +cg1 = ax.contour(x1, x2, g1, [0], colors='sandybrown')
    +cg1.set(path_effects=[patheffects.withTickedStroke(angle=135)])
    +
    +cg2 = ax.contour(x1, x2, g2, [0], colors='orangered')
    +cg2.set(path_effects=[patheffects.withTickedStroke(angle=60, length=2)])
    +
    +cg3 = ax.contour(x1, x2, g3, [0], colors='mediumblue')
    +cg3.set(path_effects=[patheffects.withTickedStroke(spacing=7)])
    +
    +ax.set_xlim(0, 4)
    +ax.set_ylim(0, 4)
    +
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/demo_bboximage.py b/galleries/examples/images_contours_and_fields/demo_bboximage.py
    new file mode 100644
    index 000000000000..660aa0a58523
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/demo_bboximage.py
    @@ -0,0 +1,62 @@
    +"""
    +==============
    +BboxImage Demo
    +==============
    +
    +A `~matplotlib.image.BboxImage` can be used to position an image according to
    +a bounding box. This demo shows how to show an image inside a `.text.Text`'s
    +bounding box as well as how to manually create a bounding box for the image.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.image import BboxImage
    +from matplotlib.transforms import Bbox, TransformedBbox
    +
    +fig, (ax1, ax2) = plt.subplots(ncols=2)
    +
    +# ----------------------------
    +# Create a BboxImage with Text
    +# ----------------------------
    +txt = ax1.text(0.5, 0.5, "test", size=30, ha="center", color="w")
    +ax1.add_artist(
    +    BboxImage(txt.get_window_extent, data=np.arange(256).reshape((1, -1))))
    +
    +# ------------------------------------
    +# Create a BboxImage for each colormap
    +# ------------------------------------
    +# List of all colormaps; skip reversed colormaps.
    +cmap_names = sorted(m for m in plt.colormaps if not m.endswith("_r"))
    +
    +ncol = 2
    +nrow = len(cmap_names) // ncol + 1
    +
    +xpad_fraction = 0.3
    +dx = 1 / (ncol + xpad_fraction * (ncol - 1))
    +
    +ypad_fraction = 0.3
    +dy = 1 / (nrow + ypad_fraction * (nrow - 1))
    +
    +for i, cmap_name in enumerate(cmap_names):
    +    ix, iy = divmod(i, nrow)
    +    bbox0 = Bbox.from_bounds(ix*dx*(1+xpad_fraction),
    +                             1 - iy*dy*(1+ypad_fraction) - dy,
    +                             dx, dy)
    +    bbox = TransformedBbox(bbox0, ax2.transAxes)
    +    ax2.add_artist(
    +        BboxImage(bbox, cmap=cmap_name, data=np.arange(256).reshape((1, -1))))
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.image.BboxImage`
    +#    - `matplotlib.transforms.Bbox`
    +#    - `matplotlib.transforms.TransformedBbox`
    +#    - `matplotlib.text.Text`
    diff --git a/galleries/examples/images_contours_and_fields/figimage_demo.py b/galleries/examples/images_contours_and_fields/figimage_demo.py
    new file mode 100644
    index 000000000000..c8fe44956a86
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/figimage_demo.py
    @@ -0,0 +1,29 @@
    +"""
    +=============
    +Figimage Demo
    +=============
    +
    +This illustrates placing images directly in the figure, with no Axes objects.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +Z = np.arange(10000).reshape((100, 100))
    +Z[:, 50:] = 1
    +
    +im1 = fig.figimage(Z, xo=50, yo=0, origin='lower')
    +im2 = fig.figimage(Z, xo=100, yo=100, alpha=.8, origin='lower')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure`
    +#    - `matplotlib.figure.Figure.figimage` / `matplotlib.pyplot.figimage`
    diff --git a/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py b/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py
    new file mode 100644
    index 000000000000..df4f204e7fda
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_annotated_heatmap.py
    @@ -0,0 +1,308 @@
    +"""
    +=================
    +Annotated heatmap
    +=================
    +
    +It is often desirable to show data which depends on two independent
    +variables as a color coded image plot. This is often referred to as a
    +heatmap. If the data is categorical, this would be called a categorical
    +heatmap.
    +
    +Matplotlib's `~matplotlib.axes.Axes.imshow` function makes
    +production of such plots particularly easy.
    +
    +The following examples show how to create a heatmap with annotations.
    +We will start with an easy example and expand it to be usable as a
    +universal function.
    +"""
    +
    +
    +# %%
    +#
    +# A simple categorical heatmap
    +# ----------------------------
    +#
    +# We may start by defining some data. What we need is a 2D list or array
    +# which defines the data to color code. We then also need two lists or arrays
    +# of categories; of course the number of elements in those lists
    +# need to match the data along the respective axes.
    +# The heatmap itself is an `~matplotlib.axes.Axes.imshow` plot
    +# with the labels set to the categories we have.
    +# Note that it is important to set both, the tick locations
    +# (`~matplotlib.axes.Axes.set_xticks`) as well as the
    +# tick labels (`~matplotlib.axes.Axes.set_xticklabels`),
    +# otherwise they would become out of sync. The locations are just
    +# the ascending integer numbers, while the ticklabels are the labels to show.
    +# Finally, we can label the data itself by creating a `~matplotlib.text.Text`
    +# within each cell showing the value of that cell.
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib
    +import matplotlib as mpl
    +
    +# sphinx_gallery_thumbnail_number = 2
    +
    +vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
    +              "potato", "wheat", "barley"]
    +farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening",
    +           "Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."]
    +
    +harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
    +                    [2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
    +                    [1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
    +                    [0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
    +                    [0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
    +                    [1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
    +                    [0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
    +
    +
    +fig, ax = plt.subplots()
    +im = ax.imshow(harvest)
    +
    +# Show all ticks and label them with the respective list entries
    +ax.set_xticks(range(len(farmers)), labels=farmers,
    +              rotation=45, rotation_mode="xtick")
    +ax.set_yticks(range(len(vegetables)), labels=vegetables)
    +
    +# Loop over data dimensions and create text annotations.
    +for i in range(len(vegetables)):
    +    for j in range(len(farmers)):
    +        text = ax.text(j, i, harvest[i, j],
    +                       ha="center", va="center", color="w")
    +
    +ax.set_title("Harvest of local farmers (in tons/year)")
    +fig.tight_layout()
    +plt.show()
    +
    +
    +# %%
    +# Using the helper function code style
    +# ------------------------------------
    +#
    +# As discussed in the :ref:`Coding styles `
    +# one might want to reuse such code to create some kind of heatmap
    +# for different input data and/or on different axes.
    +# We create a function that takes the data and the row and column labels as
    +# input, and allows arguments that are used to customize the plot
    +#
    +# Here, in addition to the above we also want to create a colorbar and
    +# position the labels above of the heatmap instead of below it.
    +# The annotations shall get different colors depending on a threshold
    +# for better contrast against the pixel color.
    +# Finally, we turn the surrounding axes spines off and create
    +# a grid of white lines to separate the cells.
    +
    +
    +def heatmap(data, row_labels, col_labels, ax=None,
    +            cbar_kw=None, cbarlabel="", **kwargs):
    +    """
    +    Create a heatmap from a numpy array and two lists of labels.
    +
    +    Parameters
    +    ----------
    +    data
    +        A 2D numpy array of shape (M, N).
    +    row_labels
    +        A list or array of length M with the labels for the rows.
    +    col_labels
    +        A list or array of length N with the labels for the columns.
    +    ax
    +        A `matplotlib.axes.Axes` instance to which the heatmap is plotted.  If
    +        not provided, use current Axes or create a new one.  Optional.
    +    cbar_kw
    +        A dictionary with arguments to `matplotlib.Figure.colorbar`.  Optional.
    +    cbarlabel
    +        The label for the colorbar.  Optional.
    +    **kwargs
    +        All other arguments are forwarded to `imshow`.
    +    """
    +
    +    if ax is None:
    +        ax = plt.gca()
    +
    +    if cbar_kw is None:
    +        cbar_kw = {}
    +
    +    # Plot the heatmap
    +    im = ax.imshow(data, **kwargs)
    +
    +    # Create colorbar
    +    cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw)
    +    cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom")
    +
    +    # Show all ticks and label them with the respective list entries.
    +    ax.set_xticks(range(data.shape[1]), labels=col_labels,
    +                  rotation=-30, rotation_mode="xtick")
    +    ax.set_yticks(range(data.shape[0]), labels=row_labels)
    +
    +    # Let the horizontal axes labeling appear on top.
    +    ax.tick_params(top=True, bottom=False,
    +                   labeltop=True, labelbottom=False)
    +
    +    # Turn spines off and create white grid.
    +    ax.spines[:].set_visible(False)
    +
    +    ax.set_xticks(np.arange(data.shape[1]+1)-.5, minor=True)
    +    ax.set_yticks(np.arange(data.shape[0]+1)-.5, minor=True)
    +    ax.grid(which="minor", color="w", linestyle='-', linewidth=3)
    +    ax.tick_params(which="minor", bottom=False, left=False)
    +
    +    return im, cbar
    +
    +
    +def annotate_heatmap(im, data=None, valfmt="{x:.2f}",
    +                     textcolors=("black", "white"),
    +                     threshold=None, **textkw):
    +    """
    +    A function to annotate a heatmap.
    +
    +    Parameters
    +    ----------
    +    im
    +        The AxesImage to be labeled.
    +    data
    +        Data used to annotate.  If None, the image's data is used.  Optional.
    +    valfmt
    +        The format of the annotations inside the heatmap.  This should either
    +        use the string format method, e.g. "$ {x:.2f}", or be a
    +        `matplotlib.ticker.Formatter`.  Optional.
    +    textcolors
    +        A pair of colors.  The first is used for values below a threshold,
    +        the second for those above.  Optional.
    +    threshold
    +        Value in data units according to which the colors from textcolors are
    +        applied.  If None (the default) uses the middle of the colormap as
    +        separation.  Optional.
    +    **kwargs
    +        All other arguments are forwarded to each call to `text` used to create
    +        the text labels.
    +    """
    +
    +    if not isinstance(data, (list, np.ndarray)):
    +        data = im.get_array()
    +
    +    # Normalize the threshold to the images color range.
    +    if threshold is not None:
    +        threshold = im.norm(threshold)
    +    else:
    +        threshold = im.norm(data.max())/2.
    +
    +    # Set default alignment to center, but allow it to be
    +    # overwritten by textkw.
    +    kw = dict(horizontalalignment="center",
    +              verticalalignment="center")
    +    kw.update(textkw)
    +
    +    # Get the formatter in case a string is supplied
    +    if isinstance(valfmt, str):
    +        valfmt = matplotlib.ticker.StrMethodFormatter(valfmt)
    +
    +    # Loop over the data and create a `Text` for each "pixel".
    +    # Change the text's color depending on the data.
    +    texts = []
    +    for i in range(data.shape[0]):
    +        for j in range(data.shape[1]):
    +            kw.update(color=textcolors[int(im.norm(data[i, j]) > threshold)])
    +            text = im.axes.text(j, i, valfmt(data[i, j], None), **kw)
    +            texts.append(text)
    +
    +    return texts
    +
    +
    +# %%
    +# The above now allows us to keep the actual plot creation pretty compact.
    +#
    +
    +fig, ax = plt.subplots()
    +
    +im, cbar = heatmap(harvest, vegetables, farmers, ax=ax,
    +                   cmap="YlGn", cbarlabel="harvest [t/year]")
    +texts = annotate_heatmap(im, valfmt="{x:.1f} t")
    +
    +fig.tight_layout()
    +plt.show()
    +
    +
    +# %%
    +# Some more complex heatmap examples
    +# ----------------------------------
    +#
    +# In the following we show the versatility of the previously created
    +# functions by applying it in different cases and using different arguments.
    +#
    +
    +np.random.seed(19680801)
    +
    +fig, ((ax, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(8, 6))
    +
    +# Replicate the above example with a different font size and colormap.
    +
    +im, _ = heatmap(harvest, vegetables, farmers, ax=ax,
    +                cmap="Wistia", cbarlabel="harvest [t/year]")
    +annotate_heatmap(im, valfmt="{x:.1f}", size=7)
    +
    +# Create some new data, give further arguments to imshow (vmin),
    +# use an integer format on the annotations and provide some colors.
    +
    +data = np.random.randint(2, 100, size=(7, 7))
    +y = [f"Book {i}" for i in range(1, 8)]
    +x = [f"Store {i}" for i in list("ABCDEFG")]
    +im, _ = heatmap(data, y, x, ax=ax2, vmin=0,
    +                cmap="magma_r", cbarlabel="weekly sold copies")
    +annotate_heatmap(im, valfmt="{x:d}", size=7, threshold=20,
    +                 textcolors=("red", "white"))
    +
    +# Sometimes even the data itself is categorical. Here we use a
    +# `matplotlib.colors.BoundaryNorm` to get the data into classes
    +# and use this to colorize the plot, but also to obtain the class
    +# labels from an array of classes.
    +
    +data = np.random.randn(6, 6)
    +y = [f"Prod. {i}" for i in range(10, 70, 10)]
    +x = [f"Cycle {i}" for i in range(1, 7)]
    +
    +qrates = list("ABCDEFG")
    +norm = matplotlib.colors.BoundaryNorm(np.linspace(-3.5, 3.5, 8), 7)
    +fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: qrates[::-1][norm(x)])
    +
    +im, _ = heatmap(data, y, x, ax=ax3,
    +                cmap=mpl.colormaps["PiYG"].resampled(7), norm=norm,
    +                cbar_kw=dict(ticks=np.arange(-3, 4), format=fmt),
    +                cbarlabel="Quality Rating")
    +
    +annotate_heatmap(im, valfmt=fmt, size=9, fontweight="bold", threshold=-1,
    +                 textcolors=("red", "black"))
    +
    +# We can nicely plot a correlation matrix. Since this is bound by -1 and 1,
    +# we use those as vmin and vmax. We may also remove leading zeros and hide
    +# the diagonal elements (which are all 1) by using a
    +# `matplotlib.ticker.FuncFormatter`.
    +
    +corr_matrix = np.corrcoef(harvest)
    +im, _ = heatmap(corr_matrix, vegetables, vegetables, ax=ax4,
    +                cmap="PuOr", vmin=-1, vmax=1,
    +                cbarlabel="correlation coeff.")
    +
    +
    +def func(x, pos):
    +    return f"{x:.2f}".replace("0.", ".").replace("1.00", "")
    +
    +annotate_heatmap(im, valfmt=matplotlib.ticker.FuncFormatter(func), size=7)
    +
    +
    +plt.tight_layout()
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    diff --git a/galleries/examples/images_contours_and_fields/image_antialiasing.py b/galleries/examples/images_contours_and_fields/image_antialiasing.py
    new file mode 100644
    index 000000000000..10f563875767
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_antialiasing.py
    @@ -0,0 +1,258 @@
    +"""
    +================
    +Image resampling
    +================
    +
    +Images are represented by discrete pixels assigned color values, either on the
    +screen or in an image file.  When a user calls `~.Axes.imshow` with a data
    +array, it is rare that the size of the data array exactly matches the number of
    +pixels allotted to the image in the figure, so Matplotlib resamples or `scales
    +`_ the data or image to fit.  If
    +the data array is larger than the number of pixels allotted in the rendered figure,
    +then the image will be "down-sampled" and image information will be lost.
    +Conversely, if the data array is smaller than the number of output pixels then each
    +data point will get multiple pixels, and the image is "up-sampled".
    +
    +In the following figure, the first data array has size (450, 450), but is
    +represented by far fewer pixels in the figure, and hence is down-sampled.  The
    +second data array has size (4, 4), and is represented by far more pixels, and
    +hence is up-sampled.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, axs = plt.subplots(1, 2, figsize=(4, 2))
    +
    +# First we generate a 450x450 pixel image with varying frequency content:
    +N = 450
    +x = np.arange(N) / N - 0.5
    +y = np.arange(N) / N - 0.5
    +aa = np.ones((N, N))
    +aa[::2, :] = -1
    +
    +X, Y = np.meshgrid(x, y)
    +R = np.sqrt(X**2 + Y**2)
    +f0 = 5
    +k = 100
    +a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2))
    +# make the left hand side of this
    +a[:int(N / 2), :][R[:int(N / 2), :] < 0.4] = -1
    +a[:int(N / 2), :][R[:int(N / 2), :] < 0.3] = 1
    +aa[:, int(N / 3):] = a[:, int(N / 3):]
    +alarge = aa
    +
    +axs[0].imshow(alarge, cmap='RdBu_r')
    +axs[0].set_title('(450, 450) Down-sampled', fontsize='medium')
    +
    +np.random.seed(19680801+9)
    +asmall = np.random.rand(4, 4)
    +axs[1].imshow(asmall, cmap='viridis')
    +axs[1].set_title('(4, 4) Up-sampled', fontsize='medium')
    +
    +# %%
    +# Matplotlib's `~.Axes.imshow` method has two keyword arguments to allow the user
    +# to control how resampling is done.  The *interpolation* keyword argument allows
    +# a choice of the kernel that is used for resampling, allowing either `anti-alias
    +# `_ filtering if
    +# down-sampling, or smoothing of pixels if up-sampling.  The
    +# *interpolation_stage* keyword argument, determines if this smoothing kernel is
    +# applied to the underlying data, or if the kernel is applied to the RGBA pixels.
    +#
    +# ``interpolation_stage='rgba'``: Data -> Normalize -> RGBA -> Interpolate/Resample
    +#
    +# ``interpolation_stage='data'``: Data -> Interpolate/Resample -> Normalize -> RGBA
    +#
    +# For both keyword arguments, Matplotlib has a default "antialiased", that is
    +# recommended for most situations, and is described below.  Note that this
    +# default behaves differently if the image is being down- or up-sampled, as
    +# described below.
    +#
    +# Down-sampling and modest up-sampling
    +# ====================================
    +#
    +# When down-sampling data, we usually want to remove aliasing by smoothing the
    +# image first and then sub-sampling it.  In Matplotlib, we can do that smoothing
    +# before mapping the data to colors, or we can do the smoothing on the RGB(A)
    +# image pixels.  The differences between these are shown below, and controlled
    +# with the *interpolation_stage* keyword argument.
    +#
    +# The following images are down-sampled from 450 data pixels to approximately
    +# 125 pixels or 250 pixels (depending on your display).
    +# The underlying image has alternating +1, -1 stripes on the left side, and
    +# a varying wavelength (`chirp `_) pattern
    +# in the rest of the image.  If we zoom, we can see this detail without any
    +# down-sampling:
    +
    +fig, ax = plt.subplots(figsize=(4, 4), layout='compressed')
    +ax.imshow(alarge, interpolation='nearest', cmap='RdBu_r')
    +ax.set_xlim(100, 200)
    +ax.set_ylim(275, 175)
    +ax.set_title('Zoom')
    +
    +# %%
    +# If we down-sample, the simplest algorithm is to decimate the data using
    +# `nearest-neighbor interpolation
    +# `_.  We can
    +# do this in either data space or RGBA space:
    +
    +fig, axs = plt.subplots(1, 2, figsize=(5, 2.7), layout='compressed')
    +for ax, interp, space in zip(axs.flat, ['nearest', 'nearest'],
    +                                       ['data', 'rgba']):
    +    ax.imshow(alarge, interpolation=interp, interpolation_stage=space,
    +              cmap='RdBu_r')
    +    ax.set_title(f"interpolation='{interp}'\nstage='{space}'")
    +
    +# %%
    +# Nearest interpolation is identical in data and RGBA space, and both exhibit
    +# `Moiré `_ patterns because the
    +# high-frequency data is being down-sampled and shows up as lower frequency
    +# patterns. We can reduce the Moiré patterns by applying an anti-aliasing filter
    +# to the image before rendering:
    +
    +fig, axs = plt.subplots(1, 2, figsize=(5, 2.7), layout='compressed')
    +for ax, interp, space in zip(axs.flat, ['hanning', 'hanning'],
    +                                       ['data', 'rgba']):
    +    ax.imshow(alarge, interpolation=interp, interpolation_stage=space,
    +              cmap='RdBu_r')
    +    ax.set_title(f"interpolation='{interp}'\nstage='{space}'")
    +plt.show()
    +
    +# %%
    +# The `Hanning `_ filter smooths
    +# the underlying data so that each new pixel is a weighted average of the
    +# original underlying pixels.  This greatly reduces the Moiré patterns.
    +# However, when the *interpolation_stage* is set to 'data', it also introduces
    +# white regions to the image that are not in the original data, both in the
    +# alternating bands on the left hand side of the image, and in the boundary
    +# between the red and blue of the large circles in the middle of the image.
    +# The interpolation at the 'rgba' stage has a different artifact, with the alternating
    +# bands coming out a shade of purple; even though purple is not in the original
    +# colormap, it is what we perceive when a blue and red stripe are close to each
    +# other.
    +#
    +# The default for the *interpolation* keyword argument is 'auto' which
    +# will choose a Hanning filter if the image is being down-sampled or up-sampled
    +# by less than a factor of three.  The default *interpolation_stage* keyword
    +# argument is also 'auto', and for images that are down-sampled or
    +# up-sampled by less than a factor of three it defaults to 'rgba'
    +# interpolation.
    +#
    +# Anti-aliasing filtering is needed, even when up-sampling. The following image
    +# up-samples 450 data pixels to 530 rendered pixels. You may note a grid of
    +# line-like artifacts which stem from the extra pixels that had to be made up.
    +# Since interpolation is 'nearest' they are the same as a neighboring line of
    +# pixels and thus stretch the image locally so that it looks distorted.
    +
    +fig, ax = plt.subplots(figsize=(6.8, 6.8))
    +ax.imshow(alarge, interpolation='nearest', cmap='grey')
    +ax.set_title("up-sampled by factor a 1.17, interpolation='nearest'")
    +
    +# %%
    +# Better anti-aliasing algorithms can reduce this effect:
    +fig, ax = plt.subplots(figsize=(6.8, 6.8))
    +ax.imshow(alarge, interpolation='auto', cmap='grey')
    +ax.set_title("up-sampled by factor a 1.17, interpolation='auto'")
    +
    +# %%
    +# Apart from the default 'hanning' anti-aliasing, `~.Axes.imshow` supports a
    +# number of different interpolation algorithms, which may work better or
    +# worse depending on the underlying data.
    +fig, axs = plt.subplots(1, 2, figsize=(7, 4), layout='constrained')
    +for ax, interp in zip(axs, ['hanning', 'lanczos']):
    +    ax.imshow(alarge, interpolation=interp, cmap='gray')
    +    ax.set_title(f"interpolation='{interp}'")
    +
    +# %%
    +# A final example shows the desirability of performing the anti-aliasing at the
    +# RGBA stage when using non-trivial interpolation kernels.  In the following,
    +# the data in the upper 100 rows is exactly 0.0, and data in the inner circle
    +# is exactly 2.0. If we perform the *interpolation_stage* in 'data' space and
    +# use an anti-aliasing filter (first panel), then floating point imprecision
    +# makes some of the data values just a bit less than zero or a bit more than
    +# 2.0, and they get assigned the under- or over- colors. This can be avoided if
    +# you do not use an anti-aliasing filter (*interpolation* set set to
    +# 'nearest'), however, that makes the part of the data susceptible to Moiré
    +# patterns much worse (second panel).  Therefore, we recommend the default
    +# *interpolation* of 'hanning'/'auto', and *interpolation_stage* of
    +# 'rgba'/'auto' for most down-sampling situations (last panel).
    +
    +a = alarge + 1
    +cmap = plt.get_cmap('RdBu_r')
    +cmap.set_under('yellow')
    +cmap.set_over('limegreen')
    +
    +fig, axs = plt.subplots(1, 3, figsize=(7, 3), layout='constrained')
    +for ax, interp, space in zip(axs.flat,
    +                             ['hanning', 'nearest', 'hanning', ],
    +                             ['data', 'data', 'rgba']):
    +    im = ax.imshow(a, interpolation=interp, interpolation_stage=space,
    +                   cmap=cmap, vmin=0, vmax=2)
    +    title = f"interpolation='{interp}'\nstage='{space}'"
    +    if ax == axs[2]:
    +        title += '\nDefault'
    +    ax.set_title(title, fontsize='medium')
    +fig.colorbar(im, ax=axs, extend='both', shrink=0.8)
    +
    +# %%
    +# Up-sampling
    +# ===========
    +#
    +# If we up-sample, then we can represent a data pixel by many image or screen pixels.
    +# In the following example, we greatly over-sample the small data matrix.
    +
    +np.random.seed(19680801+9)
    +a = np.random.rand(4, 4)
    +
    +fig, axs = plt.subplots(1, 2, figsize=(6.5, 4), layout='compressed')
    +axs[0].imshow(asmall, cmap='viridis')
    +axs[0].set_title("interpolation='auto'\nstage='auto'")
    +axs[1].imshow(asmall, cmap='viridis', interpolation="nearest",
    +              interpolation_stage="data")
    +axs[1].set_title("interpolation='nearest'\nstage='data'")
    +plt.show()
    +
    +# %%
    +# The *interpolation* keyword argument can be used to smooth the pixels if desired.
    +# However, that almost always is better done in data space, rather than in RGBA space
    +# where the filters can cause colors that are not in the colormap to be the result of
    +# the interpolation.  In the following example, note that when the interpolation is
    +# 'rgba' there are red colors as interpolation artifacts.  Therefore, the default
    +# 'auto' choice for *interpolation_stage* is set to be the same as 'data'
    +# when up-sampling is greater than a factor of three:
    +
    +fig, axs = plt.subplots(1, 2, figsize=(6.5, 4), layout='compressed')
    +im = axs[0].imshow(a, cmap='viridis', interpolation='sinc', interpolation_stage='data')
    +axs[0].set_title("interpolation='sinc'\nstage='data'\n(default for upsampling)")
    +axs[1].imshow(a, cmap='viridis', interpolation='sinc', interpolation_stage='rgba')
    +axs[1].set_title("interpolation='sinc'\nstage='rgba'")
    +fig.colorbar(im, ax=axs, shrink=0.7, extend='both')
    +
    +# %%
    +# Avoiding resampling
    +# ===================
    +#
    +# It is possible to avoid resampling data when making an image.  One method is
    +# to simply save to a vector backend (pdf, eps, svg) and use
    +# ``interpolation='none'``.  Vector backends allow embedded images, however be
    +# aware that some vector image viewers may smooth image pixels.
    +#
    +# The second method is to exactly match the size of your axes to the size of
    +# your data. The following figure is exactly 2 inches by 2 inches, and
    +# if the dpi is 200, then the 400x400 data is not resampled at all. If you download
    +# this image and zoom in an image viewer you should see the individual stripes
    +# on the left hand side (note that if you have a non hiDPI or "retina" screen, the html
    +# may serve a 100x100 version of the image, which will be downsampled.)
    +
    +fig = plt.figure(figsize=(2, 2))
    +ax = fig.add_axes((0, 0, 1, 1))
    +ax.imshow(aa[:400, :400], cmap='RdBu_r', interpolation='nearest')
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/image_clip_path.py b/galleries/examples/images_contours_and_fields/image_clip_path.py
    new file mode 100644
    index 000000000000..d66ec535b1c3
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_clip_path.py
    @@ -0,0 +1,32 @@
    +"""
    +============================
    +Clipping images with patches
    +============================
    +
    +Demo of image that's been clipped by a circular patch.
    +"""
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.cbook as cbook
    +import matplotlib.patches as patches
    +
    +with cbook.get_sample_data('grace_hopper.jpg') as image_file:
    +    image = plt.imread(image_file)
    +
    +fig, ax = plt.subplots()
    +im = ax.imshow(image)
    +patch = patches.Circle((260, 200), radius=200, transform=ax.transData)
    +im.set_clip_path(patch)
    +
    +ax.axis('off')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.artist.Artist.set_clip_path`
    diff --git a/galleries/examples/images_contours_and_fields/image_demo.py b/galleries/examples/images_contours_and_fields/image_demo.py
    new file mode 100644
    index 000000000000..4111adfa2c4e
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_demo.py
    @@ -0,0 +1,181 @@
    +"""
    +========================
    +Many ways to plot images
    +========================
    +
    +The most common way to plot images in Matplotlib is with
    +`~.axes.Axes.imshow`. The following examples demonstrate much of the
    +functionality of imshow and the many images you can create.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +from matplotlib.patches import PathPatch
    +from matplotlib.path import Path
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# %%
    +# First we'll generate a simple bivariate normal distribution.
    +
    +delta = 0.025
    +x = y = np.arange(-3.0, 3.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +fig, ax = plt.subplots()
    +im = ax.imshow(Z, interpolation='bilinear', cmap="RdYlBu",
    +               origin='lower', extent=[-3, 3, -3, 3],
    +               vmax=abs(Z).max(), vmin=-abs(Z).max())
    +
    +plt.show()
    +
    +
    +# %%
    +# It is also possible to show images of pictures.
    +
    +# A sample image
    +with cbook.get_sample_data('grace_hopper.jpg') as image_file:
    +    image = plt.imread(image_file)
    +
    +# And another image, using 256x256 16-bit integers.
    +w, h = 256, 256
    +with cbook.get_sample_data('s1045.ima.gz') as datafile:
    +    s = datafile.read()
    +A = np.frombuffer(s, np.uint16).astype(float).reshape((w, h))
    +extent = (0, 25, 0, 25)
    +
    +fig, ax = plt.subplot_mosaic([
    +    ['hopper', 'mri']
    +], figsize=(7, 3.5))
    +
    +ax['hopper'].imshow(image)
    +ax['hopper'].axis('off')  # clear x-axis and y-axis
    +
    +im = ax['mri'].imshow(A, cmap="hot", origin='upper', extent=extent)
    +
    +markers = [(15.9, 14.5), (16.8, 15)]
    +x, y = zip(*markers)
    +ax['mri'].plot(x, y, 'o')
    +
    +ax['mri'].set_title('MRI')
    +
    +plt.show()
    +
    +
    +# %%
    +# Interpolating images
    +# --------------------
    +#
    +# It is also possible to interpolate images before displaying them. Be careful,
    +# as this may manipulate the way your data looks, but it can be helpful for
    +# achieving the look you want. Below we'll display the same (small) array,
    +# interpolated with three different interpolation methods.
    +#
    +# The center of the pixel at A[i, j] is plotted at (i+0.5, i+0.5).  If you
    +# are using interpolation='nearest', the region bounded by (i, j) and
    +# (i+1, j+1) will have the same color.  If you are using interpolation,
    +# the pixel center will have the same color as it does with nearest, but
    +# other pixels will be interpolated between the neighboring pixels.
    +#
    +# To prevent edge effects when doing interpolation, Matplotlib pads the input
    +# array with identical pixels around the edge: if you have a 5x5 array with
    +# colors a-y as below::
    +#
    +#   a b c d e
    +#   f g h i j
    +#   k l m n o
    +#   p q r s t
    +#   u v w x y
    +#
    +# Matplotlib computes the interpolation and resizing on the padded array ::
    +#
    +#   a a b c d e e
    +#   a a b c d e e
    +#   f f g h i j j
    +#   k k l m n o o
    +#   p p q r s t t
    +#   o u v w x y y
    +#   o u v w x y y
    +#
    +# and then extracts the central region of the result.  (Extremely old versions
    +# of Matplotlib (<0.63) did not pad the array, but instead adjusted the view
    +# limits to hide the affected edge areas.)
    +#
    +# This approach allows plotting the full extent of an array without
    +# edge effects, and for example to layer multiple images of different
    +# sizes over one another with different interpolation methods -- see
    +# :doc:`/gallery/images_contours_and_fields/layer_images`.  It also implies
    +# a performance hit, as this new temporary, padded array must be created.
    +# Sophisticated interpolation also implies a performance hit; for maximal
    +# performance or very large images, interpolation='nearest' is suggested.
    +
    +A = np.random.rand(5, 5)
    +
    +fig, axs = plt.subplots(1, 3, figsize=(10, 3))
    +for ax, interp in zip(axs, ['nearest', 'bilinear', 'bicubic']):
    +    ax.imshow(A, interpolation=interp)
    +    ax.set_title(interp.capitalize())
    +    ax.grid(True)
    +
    +plt.show()
    +
    +
    +# %%
    +# You can specify whether images should be plotted with the array origin
    +# x[0, 0] in the upper left or lower right by using the origin parameter.
    +# You can also control the default setting image.origin in your
    +# :ref:`matplotlibrc file `. For more on
    +# this topic see the :ref:`complete guide on origin and extent
    +# `.
    +
    +x = np.arange(120).reshape((10, 12))
    +
    +interp = 'bilinear'
    +fig, axs = plt.subplots(nrows=2, sharex=True, figsize=(3, 5))
    +axs[0].set_title('blue should be up')
    +axs[0].imshow(x, origin='upper', interpolation=interp)
    +
    +axs[1].set_title('blue should be down')
    +axs[1].imshow(x, origin='lower', interpolation=interp)
    +plt.show()
    +
    +
    +# %%
    +# Finally, we'll show an image using a clip path.
    +
    +delta = 0.025
    +x = y = np.arange(-3.0, 3.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +path = Path([[0, 1], [1, 0], [0, -1], [-1, 0], [0, 1]])
    +patch = PathPatch(path, facecolor='none')
    +
    +fig, ax = plt.subplots()
    +ax.add_patch(patch)
    +
    +im = ax.imshow(Z, interpolation='bilinear', cmap="gray",
    +               origin='lower', extent=[-3, 3, -3, 3],
    +               clip_path=patch, clip_on=True)
    +im.set_clip_path(patch)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.artist.Artist.set_clip_path`
    +#    - `matplotlib.patches.PathPatch`
    diff --git a/galleries/examples/images_contours_and_fields/image_exact_placement.py b/galleries/examples/images_contours_and_fields/image_exact_placement.py
    new file mode 100644
    index 000000000000..7c667dfed1af
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_exact_placement.py
    @@ -0,0 +1,165 @@
    +"""
    +=========================================
    +Placing images, preserving relative sizes
    +=========================================
    +
    +By default Matplotlib resamples images created with `~.Axes.imshow` to
    +fit inside the parent `~.axes.Axes`.  This can mean that images that have very
    +different original sizes can end up appearing similar in size.
    +
    +This example shows how to keep the images the same relative size, or
    +how to make the images keep exactly the same pixels as the original data.
    +
    +Preserving relative sizes
    +=========================
    +
    +By default the two images are made a similar size, despite one being 1.5 times the width
    +of the other:
    +"""
    +
    +# sphinx_gallery_thumbnail_number = -1
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.patches as mpatches
    +
    +# make the data:
    +N = 450
    +x = np.arange(N) / N
    +y = np.arange(N) / N
    +
    +X, Y = np.meshgrid(x, y)
    +R = np.sqrt(X**2 + Y**2)
    +f0 = 5
    +k = 100
    +a = np.sin(np.pi * 2 * (f0 * R + k * R**2 / 2))
    +A = a[:100, :300]
    +B = A[:40, :200]
    +
    +# default layout: both axes have the same size
    +fig, axs = plt.subplots(1, 2, facecolor='aliceblue')
    +
    +axs[0].imshow(A, vmin=-1, vmax=1)
    +axs[1].imshow(B, vmin=-1, vmax=1)
    +
    +
    +def annotate_rect(ax):
    +    # add a rectangle that is the size of the B matrix
    +    rect = mpatches.Rectangle((0, 0), 200, 40, linewidth=1,
    +                              edgecolor='r', facecolor='none')
    +    ax.add_patch(rect)
    +    return rect
    +
    +annotate_rect(axs[0])
    +
    +# %%
    +# Note that both images have an aspect ratio of 1 (i.e. pixels are square), but
    +# pixels sizes differ because the images are scaled to the same width.
    +#
    +# If the size of the images are amenable, we can preserve the relative sizes of two
    +# images by using either the *width_ratio* or *height_ratio* of the subplots.  Which
    +# one you use depends on the shape of the image and the size of the figure.
    +# We can control the relative sizes using the *width_ratios* argument *if* the images
    +# are wider than they are tall and shown side by side, as is the case here.
    +#
    +# While we are making changes, let us also make the aspect ratio of the figure closer
    +# to the aspect ratio of the axes using *figsize* so that the figure does not have so
    +# much white space. Note that you could alternatively trim extra blank space when
    +# saving a figure by passing ``bbox_inches="tight"`` to `~.Figure.savefig`.
    +
    +fig, axs = plt.subplots(1, 2, width_ratios=[300/200, 1],
    +                        figsize=(6.4, 2), facecolor='aliceblue')
    +
    +axs[0].imshow(A, vmin=-1, vmax=1)
    +annotate_rect(axs[0])
    +
    +axs[1].imshow(B, vmin=-1, vmax=1)
    +# %%
    +# Given that the data subsample is in the upper left of the larger image,
    +# it might make sense if the top of the smaller Axes aligned with the top of the larger.
    +# This can be done manually by using `~.Axes.set_anchor`, and using "NW" (for
    +# northwest).
    +
    +fig, axs = plt.subplots(1, 2, width_ratios=[300/200, 1],
    +                        figsize=(6.4, 2), facecolor='aliceblue')
    +
    +axs[0].imshow(A, vmin=-1, vmax=1)
    +annotate_rect(axs[0])
    +
    +axs[0].set_anchor('NW')
    +axs[1].imshow(B, vmin=-1, vmax=1)
    +axs[1].set_anchor('NW')
    +
    +# %%
    +# Explicit placement
    +# ==================
    +# The above approach with adjusting ``figsize`` and ``width_ratios`` does
    +# not generalize well, because it needs manual parameter tuning, and
    +# possibly even code changes to using ``height_ratios`` instead of
    +# ``width_ratios`` depending on the aspects and layout of the images.
    +#
    +# We can alternative calculate positions explicitly and place Axes at absolute
    +# coordinates using `~.Figure.add_axes`. This takes the position in the form
    +# ``[left bottom width height]`` and is in
    +# :ref:`figure coordinates `. In the following, we
    +# determine figure size and Axes positions so that one image data point
    +# is rendered exactly to one figure pixel.
    +
    +dpi = 100  # 100 pixels is one inch
    +
    +# All variables from here are in pixels:
    +buffer = 0.35 * dpi  # pixels
    +
    +# Get the position of A axes
    +left = buffer
    +bottom = buffer
    +ny, nx = np.shape(A)
    +posA = [left, bottom, nx, ny]
    +# we know this is tallest, so we can already get the fig height (in pixels)
    +fig_height = bottom + ny + buffer
    +
    +# place the B axes to the right of the A axes
    +left = left + nx + buffer
    +
    +ny, nx = np.shape(B)
    +# align the bottom so that the top lines up with the top of the A axes:
    +bottom = fig_height - buffer - ny
    +posB = [left, bottom, nx, ny]
    +
    +# now we can get the fig width (in pixels)
    +fig_width = left + nx + buffer
    +
    +# figsize must be in inches:
    +fig = plt.figure(figsize=(fig_width / dpi, fig_height / dpi), facecolor='aliceblue')
    +
    +# the position posA must be normalized by the figure width and height:
    +ax = fig.add_axes((posA[0] / fig_width, posA[1] / fig_height,
    +                   posA[2] / fig_width, posA[3] / fig_height))
    +ax.imshow(A, vmin=-1, vmax=1)
    +annotate_rect(ax)
    +
    +ax = fig.add_axes((posB[0] / fig_width, posB[1] / fig_height,
    +                   posB[2] / fig_width, posB[3] / fig_height))
    +ax.imshow(B, vmin=-1, vmax=1)
    +plt.show()
    +# %%
    +# Inspection of the image will show that it is exactly 3* 35 + 300 + 200 = 605
    +# pixels wide, and 2 * 35 + 100 = 170 pixels high (or twice that if the 2x
    +# version is used by the browser instead).  The images should be rendered with
    +# exactly 1 pixel per data point (or four, if 2x).
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow`
    +#    - `matplotlib.figure.Figure.add_axes`
    +#
    +# .. tags::
    +#
    +#    component: figure
    +#    component: axes
    +#    styling: position
    +#    plot-type: image
    diff --git a/galleries/examples/images_contours_and_fields/image_masked.py b/galleries/examples/images_contours_and_fields/image_masked.py
    new file mode 100644
    index 000000000000..876e32dc0e93
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_masked.py
    @@ -0,0 +1,82 @@
    +"""
    +========================
    +Image with masked values
    +========================
    +
    +imshow with masked array input and out-of-range colors.
    +
    +The second subplot illustrates the use of BoundaryNorm to
    +get a filled contour effect.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors as colors
    +
    +# compute some interesting data
    +x0, x1 = -5, 5
    +y0, y1 = -3, 3
    +x = np.linspace(x0, x1, 500)
    +y = np.linspace(y0, y1, 500)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +# Set up a colormap:
    +palette = plt.colormaps["gray"].with_extremes(over='r', under='g', bad='b')
    +# Alternatively, we could use
    +# palette.set_bad(alpha = 0.0)
    +# to make the bad region transparent.  This is the default.
    +# If you comment out all the palette.set* lines, you will see
    +# all the defaults; under and over will be colored with the
    +# first and last colors in the palette, respectively.
    +Zm = np.ma.masked_where(Z > 1.2, Z)
    +
    +# By setting vmin and vmax in the norm, we establish the
    +# range to which the regular palette color scale is applied.
    +# Anything above that range is colored based on palette.set_over, etc.
    +
    +# set up the Axes objects
    +fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6, 5.4))
    +
    +# plot using 'continuous' colormap
    +im = ax1.imshow(Zm, interpolation='bilinear',
    +                cmap=palette,
    +                norm=colors.Normalize(vmin=-1.0, vmax=1.0),
    +                aspect='auto',
    +                origin='lower',
    +                extent=[x0, x1, y0, y1])
    +ax1.set_title('Green=low, Red=high, Blue=masked')
    +cbar = fig.colorbar(im, extend='both', shrink=0.9, ax=ax1)
    +cbar.set_label('uniform')
    +ax1.tick_params(axis='x', labelbottom=False)
    +
    +# Plot using a small number of colors, with unevenly spaced boundaries.
    +im = ax2.imshow(Zm, interpolation='nearest',
    +                cmap=palette,
    +                norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1],
    +                                         ncolors=palette.N),
    +                aspect='auto',
    +                origin='lower',
    +                extent=[x0, x1, y0, y1])
    +ax2.set_title('With BoundaryNorm')
    +cbar = fig.colorbar(im, extend='both', spacing='proportional',
    +                    shrink=0.9, ax=ax2)
    +cbar.set_label('proportional')
    +
    +fig.suptitle('imshow, with out-of-range and masked data')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors.BoundaryNorm`
    +#    - `matplotlib.colorbar.Colorbar.set_label`
    diff --git a/galleries/examples/images_contours_and_fields/image_nonuniform.py b/galleries/examples/images_contours_and_fields/image_nonuniform.py
    new file mode 100644
    index 000000000000..0901b1a7b89c
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_nonuniform.py
    @@ -0,0 +1,71 @@
    +"""
    +================
    +Image nonuniform
    +================
    +
    +`.NonUniformImage` is a generalized image with pixels on a rectilinear grid,
    +i.e. it allows rows and columns with individual heights / widths.
    +
    +There is no high-level plotting method on `~.axes.Axes` or `.pyplot` to
    +create a NonUniformImage. Instead, you have to instantiate the image
    +explicitly add it to the Axes using `.Axes.add_image`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.image import NonUniformImage
    +
    +interp = 'nearest'
    +
    +# Linear x array for cell centers:
    +x = np.linspace(-4, 4, 9)
    +
    +# Highly nonlinear x array:
    +x2 = x**3
    +
    +y = np.linspace(-4, 4, 9)
    +
    +z = np.sqrt(x[np.newaxis, :]**2 + y[:, np.newaxis]**2)
    +
    +fig, axs = plt.subplots(nrows=2, ncols=2, layout='constrained')
    +fig.suptitle('NonUniformImage class', fontsize='large')
    +ax = axs[0, 0]
    +im = NonUniformImage(ax, interpolation=interp, extent=(-4, 4, -4, 4),
    +                     cmap="Purples")
    +im.set_data(x, y, z)
    +ax.add_image(im)
    +ax.set_xlim(-4, 4)
    +ax.set_ylim(-4, 4)
    +ax.set_title(interp)
    +
    +ax = axs[0, 1]
    +im = NonUniformImage(ax, interpolation=interp, extent=(-64, 64, -4, 4),
    +                     cmap="Purples")
    +im.set_data(x2, y, z)
    +ax.add_image(im)
    +ax.set_xlim(-64, 64)
    +ax.set_ylim(-4, 4)
    +ax.set_title(interp)
    +
    +interp = 'bilinear'
    +
    +ax = axs[1, 0]
    +im = NonUniformImage(ax, interpolation=interp, extent=(-4, 4, -4, 4),
    +                     cmap="Purples")
    +im.set_data(x, y, z)
    +ax.add_image(im)
    +ax.set_xlim(-4, 4)
    +ax.set_ylim(-4, 4)
    +ax.set_title(interp)
    +
    +ax = axs[1, 1]
    +im = NonUniformImage(ax, interpolation=interp, extent=(-64, 64, -4, 4),
    +                     cmap="Purples")
    +im.set_data(x2, y, z)
    +ax.add_image(im)
    +ax.set_xlim(-64, 64)
    +ax.set_ylim(-4, 4)
    +ax.set_title(interp)
    +
    +plt.show()
    diff --git a/galleries/examples/images_contours_and_fields/image_transparency_blend.py b/galleries/examples/images_contours_and_fields/image_transparency_blend.py
    new file mode 100644
    index 000000000000..31d1d50f757c
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_transparency_blend.py
    @@ -0,0 +1,125 @@
    +"""
    +==========================================
    +Blend transparency with color in 2D images
    +==========================================
    +
    +Blend transparency with color to highlight parts of data with imshow.
    +
    +A common use for `matplotlib.pyplot.imshow` is to plot a 2D statistical
    +map. The function makes it easy to visualize a 2D matrix as an image and add
    +transparency to the output. For example, one can plot a statistic (such as a
    +t-statistic) and color the transparency of each pixel according to its p-value.
    +This example demonstrates how you can achieve this effect.
    +
    +First we will generate some data, in this case, we'll create two 2D "blobs"
    +in a 2D grid. One blob will be positive, and the other negative.
    +"""
    +
    +import matplotlib.pyplot as plt
    +# sphinx_gallery_thumbnail_number = 3
    +import numpy as np
    +
    +from matplotlib.colors import Normalize
    +
    +
    +def normal_pdf(x, mean, var):
    +    return np.exp(-(x - mean)**2 / (2*var))
    +
    +
    +# Generate the space in which the blobs will live
    +xmin, xmax, ymin, ymax = (0, 100, 0, 100)
    +n_bins = 100
    +xx = np.linspace(xmin, xmax, n_bins)
    +yy = np.linspace(ymin, ymax, n_bins)
    +
    +# Generate the blobs. The range of the values is roughly -.0002 to .0002
    +means_high = [20, 50]
    +means_low = [50, 60]
    +var = [150, 200]
    +
    +gauss_x_high = normal_pdf(xx, means_high[0], var[0])
    +gauss_y_high = normal_pdf(yy, means_high[1], var[0])
    +
    +gauss_x_low = normal_pdf(xx, means_low[0], var[1])
    +gauss_y_low = normal_pdf(yy, means_low[1], var[1])
    +
    +weights = (np.outer(gauss_y_high, gauss_x_high)
    +           - np.outer(gauss_y_low, gauss_x_low))
    +
    +# We'll also create a grey background into which the pixels will fade
    +greys = np.full((*weights.shape, 3), 70, dtype=np.uint8)
    +
    +# First we'll plot these blobs using ``imshow`` without transparency.
    +vmax = np.abs(weights).max()
    +imshow_kwargs = {
    +    'vmax': vmax,
    +    'vmin': -vmax,
    +    'cmap': 'RdYlBu',
    +    'extent': (xmin, xmax, ymin, ymax),
    +}
    +
    +fig, ax = plt.subplots()
    +ax.imshow(greys)
    +ax.imshow(weights, **imshow_kwargs)
    +ax.set_axis_off()
    +
    +# %%
    +# Blending in transparency
    +# ========================
    +#
    +# The simplest way to include transparency when plotting data with
    +# `matplotlib.pyplot.imshow` is to pass an array matching the shape of
    +# the data to the ``alpha`` argument. For example, we'll create a gradient
    +# moving from left to right below.
    +
    +# Create an alpha channel of linearly increasing values moving to the right.
    +alphas = np.ones(weights.shape)
    +alphas[:, 30:] = np.linspace(1, 0, 70)
    +
    +# Create the figure and image
    +# Note that the absolute values may be slightly different
    +fig, ax = plt.subplots()
    +ax.imshow(greys)
    +ax.imshow(weights, alpha=alphas, **imshow_kwargs)
    +ax.set_axis_off()
    +
    +# %%
    +# Using transparency to highlight values with high amplitude
    +# ==========================================================
    +#
    +# Finally, we'll recreate the same plot, but this time we'll use transparency
    +# to highlight the extreme values in the data. This is often used to highlight
    +# data points with smaller p-values. We'll also add in contour lines to
    +# highlight the image values.
    +
    +# Create an alpha channel based on weight values
    +# Any value whose absolute value is > .0001 will have zero transparency
    +alphas = Normalize(0, .3, clip=True)(np.abs(weights))
    +alphas = np.clip(alphas, .4, 1)  # alpha value clipped at the bottom at .4
    +
    +# Create the figure and image
    +# Note that the absolute values may be slightly different
    +fig, ax = plt.subplots()
    +ax.imshow(greys)
    +ax.imshow(weights, alpha=alphas, **imshow_kwargs)
    +
    +# Add contour lines to further highlight different levels.
    +ax.contour(weights[::-1], levels=[-.1, .1], colors='k', linestyles='-')
    +ax.set_axis_off()
    +plt.show()
    +
    +ax.contour(weights[::-1], levels=[-.0001, .0001], colors='k', linestyles='-')
    +ax.set_axis_off()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.colors.Normalize`
    +#    - `matplotlib.axes.Axes.set_axis_off`
    diff --git a/galleries/examples/images_contours_and_fields/image_zcoord.py b/galleries/examples/images_contours_and_fields/image_zcoord.py
    new file mode 100644
    index 000000000000..ffc336a653cc
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/image_zcoord.py
    @@ -0,0 +1,46 @@
    +"""
    +==================================
    +Modifying the coordinate formatter
    +==================================
    +
    +Modify the coordinate formatter to report the image "z" value of the nearest
    +pixel given x and y.  This functionality is built in by default; this example
    +just showcases how to customize the `~.axes.Axes.format_coord` function.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +X = 10*np.random.rand(5, 3)
    +
    +fig, ax = plt.subplots()
    +ax.imshow(X)
    +
    +
    +def format_coord(x, y):
    +    col = round(x)
    +    row = round(y)
    +    nrows, ncols = X.shape
    +    if 0 <= col < ncols and 0 <= row < nrows:
    +        z = X[row, col]
    +        return f'x={x:1.4f}, y={y:1.4f}, z={z:1.4f}'
    +    else:
    +        return f'x={x:1.4f}, y={y:1.4f}'
    +
    +
    +ax.format_coord = format_coord
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.format_coord`
    +#    - `matplotlib.axes.Axes.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/interpolation_methods.py b/galleries/examples/images_contours_and_fields/interpolation_methods.py
    new file mode 100644
    index 000000000000..dea1b474801c
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/interpolation_methods.py
    @@ -0,0 +1,50 @@
    +"""
    +=========================
    +Interpolations for imshow
    +=========================
    +
    +This example displays the difference between interpolation methods for
    +`~.axes.Axes.imshow`.
    +
    +If *interpolation* is None, it defaults to the :rc:`image.interpolation`.
    +If the interpolation is ``'none'``, then no interpolation is performed for the
    +Agg, ps and pdf backends. Other backends will default to ``'auto'``.
    +
    +For the Agg, ps and pdf backends, ``interpolation='none'`` works well when a
    +big image is scaled down, while ``interpolation='nearest'`` works well when
    +a small image is scaled up.
    +
    +See :doc:`/gallery/images_contours_and_fields/image_antialiasing` for a
    +discussion on the default ``interpolation='auto'`` option.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
    +           'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
    +           'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +grid = np.random.rand(4, 4)
    +
    +fig, axs = plt.subplots(nrows=3, ncols=6, figsize=(9, 6),
    +                        subplot_kw={'xticks': [], 'yticks': []})
    +
    +for ax, interp_method in zip(axs.flat, methods):
    +    ax.imshow(grid, interpolation=interp_method, cmap='viridis')
    +    ax.set_title(str(interp_method))
    +
    +plt.tight_layout()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/irregulardatagrid.py b/galleries/examples/images_contours_and_fields/irregulardatagrid.py
    new file mode 100644
    index 000000000000..d1c3e9b5e8a0
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/irregulardatagrid.py
    @@ -0,0 +1,95 @@
    +"""
    +=======================================
    +Contour plot of irregularly spaced data
    +=======================================
    +
    +Comparison of a contour plot of irregularly spaced data interpolated
    +on a regular grid versus a tricontour plot for an unstructured triangular grid.
    +
    +Since `~.axes.Axes.contour` and `~.axes.Axes.contourf` expect the data to live
    +on a regular grid, plotting a contour plot of irregularly spaced data requires
    +different methods. The two options are:
    +
    +* Interpolate the data to a regular grid first. This can be done with on-board
    +  means, e.g. via `~.tri.LinearTriInterpolator` or using external functionality
    +  e.g. via `scipy.interpolate.griddata`. Then plot the interpolated data with
    +  the usual `~.axes.Axes.contour`.
    +* Directly use `~.axes.Axes.tricontour` or `~.axes.Axes.tricontourf` which will
    +  perform a triangulation internally.
    +
    +This example shows both methods in action.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +np.random.seed(19680801)
    +npts = 200
    +ngridx = 100
    +ngridy = 200
    +x = np.random.uniform(-2, 2, npts)
    +y = np.random.uniform(-2, 2, npts)
    +z = x * np.exp(-x**2 - y**2)
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2)
    +
    +# -----------------------
    +# Interpolation on a grid
    +# -----------------------
    +# A contour plot of irregularly spaced data coordinates
    +# via interpolation on a grid.
    +
    +# Create grid values first.
    +xi = np.linspace(-2.1, 2.1, ngridx)
    +yi = np.linspace(-2.1, 2.1, ngridy)
    +
    +# Linearly interpolate the data (x, y) on a grid defined by (xi, yi).
    +triang = tri.Triangulation(x, y)
    +interpolator = tri.LinearTriInterpolator(triang, z)
    +Xi, Yi = np.meshgrid(xi, yi)
    +zi = interpolator(Xi, Yi)
    +
    +# Note that scipy.interpolate provides means to interpolate data on a grid
    +# as well. The following would be an alternative to the four lines above:
    +# from scipy.interpolate import griddata
    +# zi = griddata((x, y), z, (xi[None, :], yi[:, None]), method='linear')
    +
    +ax1.contour(xi, yi, zi, levels=14, linewidths=0.5, colors='k')
    +cntr1 = ax1.contourf(xi, yi, zi, levels=14, cmap="RdBu_r")
    +
    +fig.colorbar(cntr1, ax=ax1)
    +ax1.plot(x, y, 'ko', ms=3)
    +ax1.set(xlim=(-2, 2), ylim=(-2, 2))
    +ax1.set_title('grid and contour (%d points, %d grid points)' %
    +              (npts, ngridx * ngridy))
    +
    +# ----------
    +# Tricontour
    +# ----------
    +# Directly supply the unordered, irregularly spaced coordinates
    +# to tricontour.
    +
    +ax2.tricontour(x, y, z, levels=14, linewidths=0.5, colors='k')
    +cntr2 = ax2.tricontourf(x, y, z, levels=14, cmap="RdBu_r")
    +
    +fig.colorbar(cntr2, ax=ax2)
    +ax2.plot(x, y, 'ko', ms=3)
    +ax2.set(xlim=(-2, 2), ylim=(-2, 2))
    +ax2.set_title('tricontour (%d points)' % npts)
    +
    +plt.subplots_adjust(hspace=0.5)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.contour` / `matplotlib.pyplot.contour`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour`
    +#    - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf`
    diff --git a/galleries/examples/images_contours_and_fields/layer_images.py b/galleries/examples/images_contours_and_fields/layer_images.py
    new file mode 100644
    index 000000000000..dd1062f0a881
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/layer_images.py
    @@ -0,0 +1,51 @@
    +"""
    +================================
    +Layer images with alpha blending
    +================================
    +
    +Layer images above one another using alpha blending
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def func3(x, y):
    +    return (1 - x / 2 + x**5 + y**3) * np.exp(-(x**2 + y**2))
    +
    +
    +# make these smaller to increase the resolution
    +dx, dy = 0.05, 0.05
    +
    +x = np.arange(-3.0, 3.0, dx)
    +y = np.arange(-3.0, 3.0, dy)
    +X, Y = np.meshgrid(x, y)
    +
    +# when layering multiple images, the images need to have the same
    +# extent.  This does not mean they need to have the same shape, but
    +# they both need to render to the same coordinate system determined by
    +# xmin, xmax, ymin, ymax.  Note if you use different interpolations
    +# for the images their apparent extent could be different due to
    +# interpolation edge effects
    +
    +extent = np.min(x), np.max(x), np.min(y), np.max(y)
    +fig = plt.figure(frameon=False)
    +
    +Z1 = np.add.outer(range(8), range(8)) % 2  # chessboard
    +im1 = plt.imshow(Z1, cmap="gray", interpolation='nearest',
    +                 extent=extent)
    +
    +Z2 = func3(X, Y)
    +
    +im2 = plt.imshow(Z2, cmap="viridis", alpha=.9, interpolation='bilinear',
    +                 extent=extent)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/matshow.py b/galleries/examples/images_contours_and_fields/matshow.py
    new file mode 100644
    index 000000000000..4d2debf1f6b0
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/matshow.py
    @@ -0,0 +1,25 @@
    +"""
    +===============================
    +Visualize matrices with matshow
    +===============================
    +
    +`~.axes.Axes.matshow` visualizes a 2D matrix or array as color-coded image.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# a 2D array with linearly increasing values on the diagonal
    +a = np.diag(range(15))
    +
    +plt.matshow(a)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/multi_image.py b/galleries/examples/images_contours_and_fields/multi_image.py
    new file mode 100644
    index 000000000000..11be73f3a267
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/multi_image.py
    @@ -0,0 +1,64 @@
    +"""
    +=================================
    +Multiple images with one colorbar
    +=================================
    +
    +Use a single colorbar for multiple images.
    +
    +Currently, a colorbar can only be connected to one image. The connection
    +guarantees that the data coloring is consistent with the colormap scale
    +(i.e. the color of value *x* in the colormap is used for coloring a data
    +value *x* in the image).
    +
    +If we want one colorbar to be representative for multiple images, we have
    +to explicitly ensure consistent data coloring by using the same
    +data-to-color pipeline for all the images. We ensure this by explicitly
    +creating a `matplotlib.colorizer.Colorizer` object that we pass to all
    +the image plotting methods.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colorizer as mcolorizer
    +import matplotlib.colors as mcolors
    +
    +np.random.seed(19680801)
    +
    +datasets = [
    +    (i+1)/10 * np.random.rand(10, 20)
    +    for i in range(4)
    +]
    +
    +fig, axs = plt.subplots(2, 2)
    +fig.suptitle('Multiple images')
    +
    +# create a colorizer with a predefined norm to be shared across all images
    +norm = mcolors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
    +colorizer = mcolorizer.Colorizer(norm=norm)
    +
    +images = []
    +for ax, data in zip(axs.flat, datasets):
    +    images.append(ax.imshow(data, colorizer=colorizer))
    +
    +fig.colorbar(images[0], ax=axs, orientation='horizontal', fraction=.1)
    +
    +plt.show()
    +
    +# %%
    +# The colors are now kept consistent across all images when changing the
    +# scaling, e.g. through zooming in the colorbar or via the "edit axis,
    +# curves and images parameters" GUI of the Qt backend. Additionally,
    +# if the colormap of the colorizer is changed, (e.g. through the "edit
    +# axis, curves and images parameters" GUI of the Qt backend) this change
    +# propagates to the other plots and the colorbar.
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colorizer.Colorizer`
    +#    - `matplotlib.colors.Normalize`
    diff --git a/galleries/examples/images_contours_and_fields/pcolor_demo.py b/galleries/examples/images_contours_and_fields/pcolor_demo.py
    new file mode 100644
    index 000000000000..7a8ef35caf96
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/pcolor_demo.py
    @@ -0,0 +1,122 @@
    +"""
    +=============
    +pcolor images
    +=============
    +
    +`~.Axes.pcolor` generates 2D image-style plots, as illustrated below.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.colors import LogNorm
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# %%
    +# A simple pcolor demo
    +# --------------------
    +
    +Z = np.random.rand(6, 10)
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1)
    +
    +c = ax0.pcolor(Z)
    +ax0.set_title('default: no edges')
    +
    +c = ax1.pcolor(Z, edgecolors='k', linewidths=4)
    +ax1.set_title('thick edges')
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +# Comparing pcolor with similar functions
    +# ---------------------------------------
    +#
    +# Demonstrates similarities between `~.axes.Axes.pcolor`,
    +# `~.axes.Axes.pcolormesh`, `~.axes.Axes.imshow` and
    +# `~.axes.Axes.pcolorfast` for drawing quadrilateral grids.
    +# Note that we call ``imshow`` with ``aspect="auto"`` so that it doesn't force
    +# the data pixels to be square (the default is ``aspect="equal"``).
    +
    +# make these smaller to increase the resolution
    +dx, dy = 0.15, 0.05
    +
    +# generate 2 2d grids for the x & y bounds
    +y, x = np.mgrid[-3:3+dy:dy, -3:3+dx:dx]
    +z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2)
    +# x and y are bounds, so z should be the value *inside* those bounds.
    +# Therefore, remove the last value from the z array.
    +z = z[:-1, :-1]
    +z_min, z_max = -abs(z).max(), abs(z).max()
    +
    +fig, axs = plt.subplots(2, 2)
    +
    +ax = axs[0, 0]
    +c = ax.pcolor(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max)
    +ax.set_title('pcolor')
    +fig.colorbar(c, ax=ax)
    +
    +ax = axs[0, 1]
    +c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max)
    +ax.set_title('pcolormesh')
    +fig.colorbar(c, ax=ax)
    +
    +ax = axs[1, 0]
    +c = ax.imshow(z, cmap='RdBu', vmin=z_min, vmax=z_max,
    +              extent=[x.min(), x.max(), y.min(), y.max()],
    +              interpolation='nearest', origin='lower', aspect='auto')
    +ax.set_title('image (nearest, aspect="auto")')
    +fig.colorbar(c, ax=ax)
    +
    +ax = axs[1, 1]
    +c = ax.pcolorfast(x, y, z, cmap='RdBu', vmin=z_min, vmax=z_max)
    +ax.set_title('pcolorfast')
    +fig.colorbar(c, ax=ax)
    +
    +fig.tight_layout()
    +plt.show()
    +
    +
    +# %%
    +# Pcolor with a log scale
    +# -----------------------
    +#
    +# The following shows pcolor plots with a log scale.
    +
    +N = 100
    +X, Y = np.meshgrid(np.linspace(-3, 3, N), np.linspace(-2, 2, N))
    +
    +# A low hump with a spike coming out.
    +# Needs to have z/colour axis on a log scale, so we see both hump and spike.
    +# A linear scale only shows the spike.
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2)
    +Z = Z1 + 50 * Z2
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1)
    +
    +c = ax0.pcolor(X, Y, Z, shading='auto',
    +               norm=LogNorm(vmin=Z.min(), vmax=Z.max()), cmap='PuBu_r')
    +fig.colorbar(c, ax=ax0)
    +
    +c = ax1.pcolor(X, Y, Z, cmap='PuBu_r', shading='auto')
    +fig.colorbar(c, ax=ax1)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pcolor` / `matplotlib.pyplot.pcolor`
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    +#    - `matplotlib.axes.Axes.pcolorfast`
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors.LogNorm`
    diff --git a/galleries/examples/images_contours_and_fields/pcolormesh_grids.py b/galleries/examples/images_contours_and_fields/pcolormesh_grids.py
    new file mode 100644
    index 000000000000..212b807dbf90
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/pcolormesh_grids.py
    @@ -0,0 +1,127 @@
    +"""
    +============================
    +pcolormesh grids and shading
    +============================
    +
    +`.axes.Axes.pcolormesh` and `~.axes.Axes.pcolor` have a few options for
    +how grids are laid out and the shading between the grid points.
    +
    +Generally, if *Z* has shape *(M, N)* then the grid *X* and *Y* can be
    +specified with either shape *(M+1, N+1)* or *(M, N)*, depending on the
    +argument for the ``shading`` keyword argument.  Note that below we specify
    +vectors *x* as either length N or N+1 and *y* as length M or M+1, and
    +`~.axes.Axes.pcolormesh` internally makes the mesh matrices *X* and *Y* from
    +the input vectors.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +# Flat Shading
    +# ------------
    +#
    +# The grid specification with the least assumptions is ``shading='flat'``
    +# and if the grid is one larger than the data in each dimension, i.e. has shape
    +# *(M+1, N+1)*.  In that case *X* and *Y* specify the corners of quadrilaterals
    +# that are colored with the values in *Z*. Here we specify the edges of the
    +# *(3, 5)* quadrilaterals with *X* and *Y* that are *(4, 6)*.
    +
    +nrows = 3
    +ncols = 5
    +Z = np.arange(nrows * ncols).reshape(nrows, ncols)
    +x = np.arange(ncols + 1)
    +y = np.arange(nrows + 1)
    +
    +fig, ax = plt.subplots()
    +ax.pcolormesh(x, y, Z, shading='flat', vmin=Z.min(), vmax=Z.max())
    +
    +
    +def _annotate(ax, x, y, title):
    +    # this all gets repeated below:
    +    X, Y = np.meshgrid(x, y)
    +    ax.plot(X.flat, Y.flat, 'o', color='m')
    +    ax.set_xlim(-0.7, 5.2)
    +    ax.set_ylim(-0.7, 3.2)
    +    ax.set_title(title)
    +
    +_annotate(ax, x, y, "shading='flat'")
    +
    +
    +# %%
    +# Flat Shading, same shape grid
    +# -----------------------------
    +#
    +# Often, however, data is provided where *X* and *Y* match the shape of *Z*.
    +# While this makes sense for other ``shading`` types, it is not permitted
    +# when ``shading='flat'``. Historically, Matplotlib silently dropped the last
    +# row and column of *Z* in this case, to match Matlab's behavior. If this
    +# behavior is still desired, simply drop the last row and column manually:
    +
    +x = np.arange(ncols)  # note *not* ncols + 1 as before
    +y = np.arange(nrows)
    +fig, ax = plt.subplots()
    +ax.pcolormesh(x, y, Z[:-1, :-1], shading='flat', vmin=Z.min(), vmax=Z.max())
    +_annotate(ax, x, y, "shading='flat': X, Y, C same shape")
    +
    +# %%
    +# Nearest Shading, same shape grid
    +# --------------------------------
    +#
    +# Usually, dropping a row and column of data is not what the user means when
    +# they make *X*, *Y* and *Z* all the same shape.  For this case, Matplotlib
    +# allows ``shading='nearest'`` and centers the colored quadrilaterals on the
    +# grid points.
    +#
    +# If a grid that is not the correct shape is passed with ``shading='nearest'``
    +# an error is raised.
    +
    +fig, ax = plt.subplots()
    +ax.pcolormesh(x, y, Z, shading='nearest', vmin=Z.min(), vmax=Z.max())
    +_annotate(ax, x, y, "shading='nearest'")
    +
    +# %%
    +# Auto Shading
    +# ------------
    +#
    +# It's possible that the user would like the code to automatically choose which
    +# to use, in this case ``shading='auto'`` will decide whether to use 'flat' or
    +# 'nearest' shading based on the shapes of *X*, *Y* and *Z*.
    +
    +fig, axs = plt.subplots(2, 1, layout='constrained')
    +ax = axs[0]
    +x = np.arange(ncols)
    +y = np.arange(nrows)
    +ax.pcolormesh(x, y, Z, shading='auto', vmin=Z.min(), vmax=Z.max())
    +_annotate(ax, x, y, "shading='auto'; X, Y, Z: same shape (nearest)")
    +
    +ax = axs[1]
    +x = np.arange(ncols + 1)
    +y = np.arange(nrows + 1)
    +ax.pcolormesh(x, y, Z, shading='auto', vmin=Z.min(), vmax=Z.max())
    +_annotate(ax, x, y, "shading='auto'; X, Y one larger than Z (flat)")
    +
    +# %%
    +# Gouraud Shading
    +# ---------------
    +#
    +# `Gouraud shading `_ can also
    +# be specified, where the color in the quadrilaterals is linearly interpolated
    +# between the grid points.  The shapes of *X*, *Y*, *Z* must be the same.
    +
    +fig, ax = plt.subplots(layout='constrained')
    +x = np.arange(ncols)
    +y = np.arange(nrows)
    +ax.pcolormesh(x, y, Z, shading='gouraud', vmin=Z.min(), vmax=Z.max())
    +_annotate(ax, x, y, "shading='gouraud'; X, Y same shape as Z")
    +
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    diff --git a/galleries/examples/images_contours_and_fields/pcolormesh_levels.py b/galleries/examples/images_contours_and_fields/pcolormesh_levels.py
    new file mode 100644
    index 000000000000..23d47e5230d2
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/pcolormesh_levels.py
    @@ -0,0 +1,132 @@
    +"""
    +==========
    +pcolormesh
    +==========
    +
    +`.axes.Axes.pcolormesh` allows you to generate 2D image-style plots.
    +Note that it is faster than the similar `~.axes.Axes.pcolor`.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.colors import BoundaryNorm
    +from matplotlib.ticker import MaxNLocator
    +
    +# %%
    +# Basic pcolormesh
    +# ----------------
    +#
    +# We usually specify a pcolormesh by defining the edge of quadrilaterals and
    +# the value of the quadrilateral.  Note that here *x* and *y* each have one
    +# extra element than Z in the respective dimension.
    +
    +np.random.seed(19680801)
    +Z = np.random.rand(6, 10)
    +x = np.arange(-0.5, 10, 1)  # len = 11
    +y = np.arange(4.5, 11, 1)  # len = 7
    +
    +fig, ax = plt.subplots()
    +ax.pcolormesh(x, y, Z)
    +
    +# %%
    +# Non-rectilinear pcolormesh
    +# --------------------------
    +#
    +# Note that we can also specify matrices for *X* and *Y* and have
    +# non-rectilinear quadrilaterals.
    +
    +x = np.arange(-0.5, 10, 1)  # len = 11
    +y = np.arange(4.5, 11, 1)  # len = 7
    +X, Y = np.meshgrid(x, y)
    +X = X + 0.2 * Y  # tilt the coordinates.
    +Y = Y + 0.3 * X
    +
    +fig, ax = plt.subplots()
    +ax.pcolormesh(X, Y, Z)
    +
    +# %%
    +# Centered Coordinates
    +# ---------------------
    +#
    +# Often a user wants to pass *X* and *Y* with the same sizes as *Z* to
    +# `.axes.Axes.pcolormesh`. This is also allowed if ``shading='auto'`` is
    +# passed (default set by :rc:`pcolor.shading`). Pre Matplotlib 3.3,
    +# ``shading='flat'`` would drop the last column and row of *Z*, but now gives
    +# an error. If this is really what you want, then simply drop the last row and
    +# column of Z manually:
    +
    +x = np.arange(10)  # len = 10
    +y = np.arange(6)  # len = 6
    +X, Y = np.meshgrid(x, y)
    +
    +fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)
    +axs[0].pcolormesh(X, Y, Z, vmin=np.min(Z), vmax=np.max(Z), shading='auto')
    +axs[0].set_title("shading='auto' = 'nearest'")
    +axs[1].pcolormesh(X, Y, Z[:-1, :-1], vmin=np.min(Z), vmax=np.max(Z),
    +                  shading='flat')
    +axs[1].set_title("shading='flat'")
    +
    +# %%
    +# Making levels using Norms
    +# -------------------------
    +#
    +# Shows how to combine Normalization and Colormap instances to draw
    +# "levels" in `.axes.Axes.pcolor`, `.axes.Axes.pcolormesh`
    +# and `.axes.Axes.imshow` type plots in a similar
    +# way to the levels keyword argument to contour/contourf.
    +
    +# make these smaller to increase the resolution
    +dx, dy = 0.05, 0.05
    +
    +# generate 2 2d grids for the x & y bounds
    +y, x = np.mgrid[slice(1, 5 + dy, dy),
    +                slice(1, 5 + dx, dx)]
    +
    +z = np.sin(x)**10 + np.cos(10 + y*x) * np.cos(x)
    +
    +# x and y are bounds, so z should be the value *inside* those bounds.
    +# Therefore, remove the last value from the z array.
    +z = z[:-1, :-1]
    +levels = MaxNLocator(nbins=15).tick_values(z.min(), z.max())
    +
    +
    +# pick the desired colormap, sensible levels, and define a normalization
    +# instance which takes data values and translates those into levels.
    +cmap = plt.colormaps['PiYG']
    +norm = BoundaryNorm(levels, ncolors=cmap.N, clip=True)
    +
    +fig, (ax0, ax1) = plt.subplots(nrows=2)
    +
    +im = ax0.pcolormesh(x, y, z, cmap=cmap, norm=norm)
    +fig.colorbar(im, ax=ax0)
    +ax0.set_title('pcolormesh with levels')
    +
    +
    +# contours are *point* based plots, so convert our bound into point
    +# centers
    +cf = ax1.contourf(x[:-1, :-1] + dx/2.,
    +                  y[:-1, :-1] + dy/2., z, levels=levels,
    +                  cmap=cmap)
    +fig.colorbar(cf, ax=ax1)
    +ax1.set_title('contourf with levels')
    +
    +# adjust spacing between subplots so `ax1` title and `ax0` tick labels
    +# don't overlap
    +fig.tight_layout()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.colors.BoundaryNorm`
    +#    - `matplotlib.ticker.MaxNLocator`
    diff --git a/galleries/examples/images_contours_and_fields/plot_streamplot.py b/galleries/examples/images_contours_and_fields/plot_streamplot.py
    new file mode 100644
    index 000000000000..0128b6369b4a
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/plot_streamplot.py
    @@ -0,0 +1,174 @@
    +"""
    +==========
    +Streamplot
    +==========
    +
    +A stream plot, or streamline plot, is used to display 2D vector fields. This
    +example shows a few features of the `~.axes.Axes.streamplot` function:
    +
    +* Varying the color along a streamline.
    +* Varying the density of streamlines.
    +* Varying the line width along a streamline.
    +* Controlling the starting points of streamlines.
    +* Streamlines skipping masked regions and NaN values.
    +* Unbroken streamlines even when exceeding the limit of lines within a single
    +  grid cell.
    +"""
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +w = 3
    +Y, X = np.mgrid[-w:w:100j, -w:w:100j]
    +U = -1 - X**2 + Y
    +V = 1 + X - Y**2
    +speed = np.sqrt(U**2 + V**2)
    +
    +fig, axs = plt.subplots(4, 2, figsize=(7, 12), height_ratios=[1, 1, 1, 2])
    +axs = axs.flat
    +
    +#  Varying density along a streamline
    +axs[0].streamplot(X, Y, U, V, density=[0.5, 1])
    +axs[0].set_title('Varying Density')
    +
    +# Varying color along a streamline
    +strm = axs[1].streamplot(X, Y, U, V, color=U, linewidth=2, cmap='autumn')
    +fig.colorbar(strm.lines)
    +axs[1].set_title('Varying Color')
    +
    +#  Varying line width along a streamline
    +lw = 5*speed / speed.max()
    +axs[2].streamplot(X, Y, U, V, density=0.6, color='k', linewidth=lw, num_arrows=5)
    +axs[2].set_title('Varying Line Width')
    +
    +# Controlling the starting points of the streamlines
    +seed_points = np.array([[-2, -1, 0, 1, 2, -1], [-2, -1,  0, 1, 2, 2]])
    +
    +strm = axs[3].streamplot(X, Y, U, V, color=U, linewidth=2,
    +                         cmap='autumn', start_points=seed_points.T)
    +fig.colorbar(strm.lines)
    +axs[3].set_title('Controlling Starting Points')
    +
    +# Displaying the starting points with blue symbols.
    +axs[3].plot(seed_points[0], seed_points[1], 'bo')
    +axs[3].set(xlim=(-w, w), ylim=(-w, w))
    +
    +# Adding more than one arrow to each streamline
    +axs[4].streamplot(X, Y, U, V, num_arrows=3)
    +axs[4].set_title('Multiple arrows')
    +
    +axs[5].axis("off")
    +
    +# Create a mask
    +mask = np.zeros(U.shape, dtype=bool)
    +mask[40:60, 40:60] = True
    +U[:20, :20] = np.nan
    +U = np.ma.array(U, mask=mask)
    +
    +axs[6].streamplot(X, Y, U, V, color='r')
    +axs[6].set_title('Streamplot with Masking')
    +
    +axs[6].imshow(~mask, extent=(-w, w, -w, w), alpha=0.5, cmap='gray',
    +              aspect='auto')
    +axs[6].set_aspect('equal')
    +
    +axs[7].streamplot(X, Y, U, V, broken_streamlines=False)
    +axs[7].set_title('Streamplot with unbroken streamlines')
    +
    +plt.tight_layout()
    +# plt.show()
    +
    +# %%
    +# Streamline computation
    +# ----------------------
    +#
    +# The streamlines are computed by integrating along the provided vector field
    +# from the seed points, which are either automatically generated or manually
    +# specified. The accuracy and smoothness of the streamlines can be adjusted using
    +# the ``integration_max_step_scale`` and ``integration_max_error_scale`` optional
    +# parameters. See the `~.axes.Axes.streamplot` function documentation for more
    +# details.
    +#
    +# This example shows how adjusting the maximum allowed step size and error for
    +# the integrator changes the appearance of the streamline. The differences can
    +# be subtle, but can be observed particularly where the streamlines have
    +# high curvature (as shown in the zoomed in region).
    +
    +# Linear potential flow over a lifting cylinder
    +n = 50
    +x, y = np.meshgrid(np.linspace(-2, 2, n), np.linspace(-3, 3, n))
    +th = np.arctan2(y, x)
    +r = np.sqrt(x**2 + y**2)
    +vr = -np.cos(th) / r**2
    +vt = -np.sin(th) / r**2 - 1 / r
    +vx = vr * np.cos(th) - vt * np.sin(th) + 1.0
    +vy = vr * np.sin(th) + vt * np.cos(th)
    +
    +# Seed points
    +n_seed = 50
    +seed_pts = np.column_stack((np.full(n_seed, -1.75), np.linspace(-2, 2, n_seed)))
    +
    +_, axs = plt.subplots(3, 1, figsize=(6, 14))
    +th_circ = np.linspace(0, 2 * np.pi, 100)
    +for ax, max_val in zip(axs, [0.05, 1, 5]):
    +    ax_ins = ax.inset_axes([0.0, 0.7, 0.3, 0.35])
    +    for ax_curr, is_inset in zip([ax, ax_ins], [False, True]):
    +        t_start = time.time()
    +        ax_curr.streamplot(
    +            x,
    +            y,
    +            vx,
    +            vy,
    +            start_points=seed_pts,
    +            broken_streamlines=False,
    +            arrowsize=1e-10,
    +            linewidth=2 if is_inset else 0.6,
    +            color="k",
    +            integration_max_step_scale=max_val,
    +            integration_max_error_scale=max_val,
    +        )
    +        if is_inset:
    +            t_total = time.time() - t_start
    +
    +        # Draw the cylinder
    +        ax_curr.fill(
    +            np.cos(th_circ),
    +            np.sin(th_circ),
    +            color="w",
    +            ec="k",
    +            lw=6 if is_inset else 2,
    +        )
    +
    +        # Set axis properties
    +        ax_curr.set_aspect("equal")
    +
    +    # Label properties of each circle
    +    text = f"integration_max_step_scale: {max_val}\n" \
    +        f"integration_max_error_scale: {max_val}\n" \
    +        f"streamplot time: {t_total:.2f} sec"
    +    if max_val == 1:
    +        text += "\n(default)"
    +    ax.text(0.0, 0.0, text, ha="center", va="center")
    +
    +    # Set axis limits and show zoomed region
    +    ax_ins.set_xlim(-1.2, -0.7)
    +    ax_ins.set_ylim(-0.8, -0.4)
    +    ax_ins.set_yticks(())
    +    ax_ins.set_xticks(())
    +
    +    ax.set_ylim(-1.5, 1.5)
    +    ax.axis("off")
    +    ax.indicate_inset_zoom(ax_ins, ec="k")
    +
    +plt.tight_layout()
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.streamplot` / `matplotlib.pyplot.streamplot`
    +#    - `matplotlib.gridspec.GridSpec`
    diff --git a/galleries/examples/images_contours_and_fields/quadmesh_demo.py b/galleries/examples/images_contours_and_fields/quadmesh_demo.py
    new file mode 100644
    index 000000000000..85876765834d
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/quadmesh_demo.py
    @@ -0,0 +1,51 @@
    +"""
    +=============
    +QuadMesh Demo
    +=============
    +
    +`~.axes.Axes.pcolormesh` uses a `~matplotlib.collections.QuadMesh`,
    +a faster generalization of `~.axes.Axes.pcolor`, but with some restrictions.
    +
    +This demo illustrates a bug in quadmesh with masked data.
    +"""
    +
    +import numpy as np
    +
    +from matplotlib import pyplot as plt
    +
    +n = 12
    +x = np.linspace(-1.5, 1.5, n)
    +y = np.linspace(-1.5, 1.5, n * 2)
    +X, Y = np.meshgrid(x, y)
    +Qx = np.cos(Y) - np.cos(X)
    +Qz = np.sin(Y) + np.sin(X)
    +Z = np.sqrt(X**2 + Y**2) / 5
    +Z = (Z - Z.min()) / (Z.max() - Z.min())
    +
    +# The color array can include masked values.
    +Zm = np.ma.masked_where(np.abs(Qz) < 0.5 * np.max(Qz), Z)
    +
    +fig, axs = plt.subplots(nrows=1, ncols=3)
    +axs[0].pcolormesh(Qx, Qz, Z, shading='gouraud')
    +axs[0].set_title('Without masked values')
    +
    +# You can control the color of the masked region.
    +cmap = plt.colormaps[plt.rcParams['image.cmap']].with_extremes(bad='y')
    +axs[1].pcolormesh(Qx, Qz, Zm, shading='gouraud', cmap=cmap)
    +axs[1].set_title('With masked values')
    +
    +# Or use the default, which is transparent.
    +axs[2].pcolormesh(Qx, Qz, Zm, shading='gouraud')
    +axs[2].set_title('With masked values')
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    diff --git a/galleries/examples/images_contours_and_fields/quiver_demo.py b/galleries/examples/images_contours_and_fields/quiver_demo.py
    new file mode 100644
    index 000000000000..784cfb0dbc79
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/quiver_demo.py
    @@ -0,0 +1,63 @@
    +"""
    +=======================================
    +Advanced quiver and quiverkey functions
    +=======================================
    +
    +Demonstrates some more advanced options for `~.axes.Axes.quiver`.  For a simple
    +example refer to :doc:`/gallery/images_contours_and_fields/quiver_simple_demo`.
    +
    +Note: The plot autoscaling does not take into account the arrows, so
    +those on the boundaries may reach out of the picture.  This is not an easy
    +problem to solve in a perfectly general way.  The recommended workaround is to
    +manually set the Axes limits in such a case.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +X, Y = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2))
    +U = np.cos(X)
    +V = np.sin(Y)
    +
    +# %%
    +
    +fig1, ax1 = plt.subplots()
    +ax1.set_title('Arrows scale with plot width, not view')
    +Q = ax1.quiver(X, Y, U, V, units='width')
    +qk = ax1.quiverkey(Q, 0.9, 0.9, 2, r'$2 \frac{m}{s}$', labelpos='E',
    +                   coordinates='figure')
    +
    +# %%
    +
    +fig2, ax2 = plt.subplots()
    +ax2.set_title("pivot='mid'; every third arrow; units='inches'")
    +Q = ax2.quiver(X[::3, ::3], Y[::3, ::3], U[::3, ::3], V[::3, ::3],
    +               pivot='mid', units='inches')
    +qk = ax2.quiverkey(Q, 0.9, 0.9, 1, r'$1 \frac{m}{s}$', labelpos='E',
    +                   coordinates='figure')
    +ax2.scatter(X[::3, ::3], Y[::3, ::3], color='r', s=5)
    +
    +# %%
    +
    +# sphinx_gallery_thumbnail_number = 3
    +
    +fig3, ax3 = plt.subplots()
    +ax3.set_title("pivot='tip'; scales with x view")
    +M = np.hypot(U, V)
    +Q = ax3.quiver(X, Y, U, V, M, units='x', pivot='tip', width=0.022,
    +               scale=1 / 0.15)
    +qk = ax3.quiverkey(Q, 0.9, 0.9, 1, r'$1 \frac{m}{s}$', labelpos='E',
    +                   coordinates='figure')
    +ax3.scatter(X, Y, color='0.5', s=1)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver`
    +#    - `matplotlib.axes.Axes.quiverkey` / `matplotlib.pyplot.quiverkey`
    diff --git a/galleries/examples/images_contours_and_fields/quiver_simple_demo.py b/galleries/examples/images_contours_and_fields/quiver_simple_demo.py
    new file mode 100644
    index 000000000000..a17a7f2e87f7
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/quiver_simple_demo.py
    @@ -0,0 +1,33 @@
    +"""
    +==================
    +Quiver Simple Demo
    +==================
    +
    +A simple example of a `~.axes.Axes.quiver` plot with a `~.axes.Axes.quiverkey`.
    +
    +For more advanced options refer to
    +:doc:`/gallery/images_contours_and_fields/quiver_demo`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +X = np.arange(-10, 10, 1)
    +Y = np.arange(-10, 10, 1)
    +U, V = np.meshgrid(X, Y)
    +
    +fig, ax = plt.subplots()
    +q = ax.quiver(X, Y, U, V)
    +ax.quiverkey(q, X=0.3, Y=1.1, U=10,
    +             label='Quiver key, length = 10', labelpos='E')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver`
    +#    - `matplotlib.axes.Axes.quiverkey` / `matplotlib.pyplot.quiverkey`
    diff --git a/galleries/examples/images_contours_and_fields/shading_example.py b/galleries/examples/images_contours_and_fields/shading_example.py
    new file mode 100644
    index 000000000000..cb3e5393e1c1
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/shading_example.py
    @@ -0,0 +1,74 @@
    +"""
    +===============
    +Shading example
    +===============
    +
    +Example showing how to make shaded relief plots like Mathematica_ or
    +`Generic Mapping Tools`_.
    +
    +.. _Mathematica: http://reference.wolfram.com/mathematica/ref/ReliefPlot.html
    +.. _Generic Mapping Tools: https://www.generic-mapping-tools.org/
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from matplotlib.colors import LightSource
    +
    +
    +def main():
    +    # Test data
    +    x, y = np.mgrid[-5:5:0.05, -5:5:0.05]
    +    z = 5 * (np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2))
    +
    +    dem = cbook.get_sample_data('jacksboro_fault_dem.npz')
    +    elev = dem['elevation']
    +
    +    fig = compare(z, plt.colormaps["copper"])
    +    fig.suptitle('HSV Blending Looks Best with Smooth Surfaces', y=0.95)
    +
    +    fig = compare(elev, plt.colormaps["gist_earth"], ve=0.05)
    +    fig.suptitle('Overlay Blending Looks Best with Rough Surfaces', y=0.95)
    +
    +    plt.show()
    +
    +
    +def compare(z, cmap, ve=1):
    +    # Create subplots and hide ticks
    +    fig, axs = plt.subplots(ncols=2, nrows=2)
    +    for ax in axs.flat:
    +        ax.set(xticks=[], yticks=[])
    +
    +    # Illuminate the scene from the northwest
    +    ls = LightSource(azdeg=315, altdeg=45)
    +
    +    axs[0, 0].imshow(z, cmap=cmap)
    +    axs[0, 0].set(xlabel='Colormapped Data')
    +
    +    axs[0, 1].imshow(ls.hillshade(z, vert_exag=ve), cmap='gray')
    +    axs[0, 1].set(xlabel='Illumination Intensity')
    +
    +    rgb = ls.shade(z, cmap=cmap, vert_exag=ve, blend_mode='hsv')
    +    axs[1, 0].imshow(rgb)
    +    axs[1, 0].set(xlabel='Blend Mode: "hsv" (default)')
    +
    +    rgb = ls.shade(z, cmap=cmap, vert_exag=ve, blend_mode='overlay')
    +    axs[1, 1].imshow(rgb)
    +    axs[1, 1].set(xlabel='Blend Mode: "overlay"')
    +
    +    return fig
    +
    +
    +if __name__ == '__main__':
    +    main()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors.LightSource`
    +#    - `matplotlib.axes.Axes.imshow` / `matplotlib.pyplot.imshow`
    diff --git a/galleries/examples/images_contours_and_fields/specgram_demo.py b/galleries/examples/images_contours_and_fields/specgram_demo.py
    new file mode 100644
    index 000000000000..5add9172cf2a
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/specgram_demo.py
    @@ -0,0 +1,52 @@
    +"""
    +===========
    +Spectrogram
    +===========
    +
    +Plotting a spectrogram using `~.Axes.specgram`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +dt = 0.0005
    +t = np.arange(0.0, 20.5, dt)
    +s1 = np.sin(2 * np.pi * 100 * t)
    +s2 = 2 * np.sin(2 * np.pi * 400 * t)
    +
    +# create a transient "chirp"
    +s2[t <= 10] = s2[12 <= t] = 0
    +
    +# add some noise into the mix
    +nse = 0.01 * np.random.random(size=len(t))
    +
    +x = s1 + s2 + nse  # the signal
    +NFFT = 1024  # the length of the windowing segments
    +Fs = 1/dt  # the sampling frequency
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
    +ax1.plot(t, x)
    +ax1.set_ylabel('Signal')
    +
    +Pxx, freqs, bins, im = ax2.specgram(x, NFFT=NFFT, Fs=Fs)
    +# The `specgram` method returns 4 objects. They are:
    +# - Pxx: the periodogram
    +# - freqs: the frequency vector
    +# - bins: the centers of the time bins
    +# - im: the .image.AxesImage instance representing the data in the plot
    +ax2.set_xlabel('Time (s)')
    +ax2.set_ylabel('Frequency (Hz)')
    +ax2.set_xlim(0, 20)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.specgram` / `matplotlib.pyplot.specgram`
    diff --git a/galleries/examples/images_contours_and_fields/spy_demos.py b/galleries/examples/images_contours_and_fields/spy_demos.py
    new file mode 100644
    index 000000000000..ceb4b3d63e26
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/spy_demos.py
    @@ -0,0 +1,40 @@
    +"""
    +=========
    +Spy Demos
    +=========
    +
    +Plot the sparsity pattern of arrays.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, axs = plt.subplots(2, 2)
    +ax1 = axs[0, 0]
    +ax2 = axs[0, 1]
    +ax3 = axs[1, 0]
    +ax4 = axs[1, 1]
    +
    +x = np.random.randn(20, 20)
    +x[5, :] = 0.
    +x[:, 12] = 0.
    +
    +ax1.spy(x, markersize=5)
    +ax2.spy(x, precision=0.1, markersize=5)
    +
    +ax3.spy(x)
    +ax4.spy(x, precision=0.1)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.spy` / `matplotlib.pyplot.spy`
    diff --git a/galleries/examples/images_contours_and_fields/tricontour_demo.py b/galleries/examples/images_contours_and_fields/tricontour_demo.py
    new file mode 100644
    index 000000000000..3459382caad6
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/tricontour_demo.py
    @@ -0,0 +1,161 @@
    +"""
    +===============
    +Tricontour Demo
    +===============
    +
    +Contour plots of unstructured triangular grids.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +# %%
    +# Creating a Triangulation without specifying the triangles results in the
    +# Delaunay triangulation of the points.
    +
    +# First create the x and y coordinates of the points.
    +n_angles = 48
    +n_radii = 8
    +min_radius = 0.25
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi / n_angles
    +
    +x = (radii * np.cos(angles)).flatten()
    +y = (radii * np.sin(angles)).flatten()
    +z = (np.cos(radii) * np.cos(3 * angles)).flatten()
    +
    +# Create the Triangulation; no triangles so Delaunay triangulation created.
    +triang = tri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +# %%
    +# pcolor plot.
    +
    +fig1, ax1 = plt.subplots()
    +ax1.set_aspect('equal')
    +tcf = ax1.tricontourf(triang, z)
    +fig1.colorbar(tcf)
    +ax1.tricontour(triang, z, colors='k')
    +ax1.set_title('Contour plot of Delaunay triangulation')
    +
    +
    +# %%
    +# You could also specify hatching patterns along with different cmaps.
    +
    +fig2, ax2 = plt.subplots()
    +ax2.set_aspect("equal")
    +tcf = ax2.tricontourf(
    +    triang,
    +    z,
    +    hatches=["*", "-", "/", "//", "\\", None],
    +    cmap="cividis"
    +)
    +fig2.colorbar(tcf)
    +ax2.tricontour(triang, z, linestyles="solid", colors="k", linewidths=2.0)
    +ax2.set_title("Hatched Contour plot of Delaunay triangulation")
    +
    +# %%
    +# You could also generate hatching patterns labeled with no color.
    +
    +fig3, ax3 = plt.subplots()
    +n_levels = 7
    +tcf = ax3.tricontourf(
    +    triang,
    +    z,
    +    n_levels,
    +    colors="none",
    +    hatches=[".", "/", "\\", None, "\\\\", "*"],
    +)
    +ax3.tricontour(triang, z, n_levels, colors="black", linestyles="-")
    +
    +
    +# create a legend for the contour set
    +artists, labels = tcf.legend_elements(str_format="{:2.1f}".format)
    +ax3.legend(artists, labels, handleheight=2, framealpha=1)
    +
    +# %%
    +# You can specify your own triangulation rather than perform a Delaunay
    +# triangulation of the points, where each triangle is given by the indices of
    +# the three points that make up the triangle, ordered in either a clockwise or
    +# anticlockwise manner.
    +
    +xy = np.asarray([
    +    [-0.101, 0.872], [-0.080, 0.883], [-0.069, 0.888], [-0.054, 0.890],
    +    [-0.045, 0.897], [-0.057, 0.895], [-0.073, 0.900], [-0.087, 0.898],
    +    [-0.090, 0.904], [-0.069, 0.907], [-0.069, 0.921], [-0.080, 0.919],
    +    [-0.073, 0.928], [-0.052, 0.930], [-0.048, 0.942], [-0.062, 0.949],
    +    [-0.054, 0.958], [-0.069, 0.954], [-0.087, 0.952], [-0.087, 0.959],
    +    [-0.080, 0.966], [-0.085, 0.973], [-0.087, 0.965], [-0.097, 0.965],
    +    [-0.097, 0.975], [-0.092, 0.984], [-0.101, 0.980], [-0.108, 0.980],
    +    [-0.104, 0.987], [-0.102, 0.993], [-0.115, 1.001], [-0.099, 0.996],
    +    [-0.101, 1.007], [-0.090, 1.010], [-0.087, 1.021], [-0.069, 1.021],
    +    [-0.052, 1.022], [-0.052, 1.017], [-0.069, 1.010], [-0.064, 1.005],
    +    [-0.048, 1.005], [-0.031, 1.005], [-0.031, 0.996], [-0.040, 0.987],
    +    [-0.045, 0.980], [-0.052, 0.975], [-0.040, 0.973], [-0.026, 0.968],
    +    [-0.020, 0.954], [-0.006, 0.947], [ 0.003, 0.935], [ 0.006, 0.926],
    +    [ 0.005, 0.921], [ 0.022, 0.923], [ 0.033, 0.912], [ 0.029, 0.905],
    +    [ 0.017, 0.900], [ 0.012, 0.895], [ 0.027, 0.893], [ 0.019, 0.886],
    +    [ 0.001, 0.883], [-0.012, 0.884], [-0.029, 0.883], [-0.038, 0.879],
    +    [-0.057, 0.881], [-0.062, 0.876], [-0.078, 0.876], [-0.087, 0.872],
    +    [-0.030, 0.907], [-0.007, 0.905], [-0.057, 0.916], [-0.025, 0.933],
    +    [-0.077, 0.990], [-0.059, 0.993]])
    +x = np.degrees(xy[:, 0])
    +y = np.degrees(xy[:, 1])
    +x0 = -5
    +y0 = 52
    +z = np.exp(-0.01 * ((x - x0) ** 2 + (y - y0) ** 2))
    +
    +triangles = np.asarray([
    +    [67, 66,  1], [65,  2, 66], [ 1, 66,  2], [64,  2, 65], [63,  3, 64],
    +    [60, 59, 57], [ 2, 64,  3], [ 3, 63,  4], [ 0, 67,  1], [62,  4, 63],
    +    [57, 59, 56], [59, 58, 56], [61, 60, 69], [57, 69, 60], [ 4, 62, 68],
    +    [ 6,  5,  9], [61, 68, 62], [69, 68, 61], [ 9,  5, 70], [ 6,  8,  7],
    +    [ 4, 70,  5], [ 8,  6,  9], [56, 69, 57], [69, 56, 52], [70, 10,  9],
    +    [54, 53, 55], [56, 55, 53], [68, 70,  4], [52, 56, 53], [11, 10, 12],
    +    [69, 71, 68], [68, 13, 70], [10, 70, 13], [51, 50, 52], [13, 68, 71],
    +    [52, 71, 69], [12, 10, 13], [71, 52, 50], [71, 14, 13], [50, 49, 71],
    +    [49, 48, 71], [14, 16, 15], [14, 71, 48], [17, 19, 18], [17, 20, 19],
    +    [48, 16, 14], [48, 47, 16], [47, 46, 16], [16, 46, 45], [23, 22, 24],
    +    [21, 24, 22], [17, 16, 45], [20, 17, 45], [21, 25, 24], [27, 26, 28],
    +    [20, 72, 21], [25, 21, 72], [45, 72, 20], [25, 28, 26], [44, 73, 45],
    +    [72, 45, 73], [28, 25, 29], [29, 25, 31], [43, 73, 44], [73, 43, 40],
    +    [72, 73, 39], [72, 31, 25], [42, 40, 43], [31, 30, 29], [39, 73, 40],
    +    [42, 41, 40], [72, 33, 31], [32, 31, 33], [39, 38, 72], [33, 72, 38],
    +    [33, 38, 34], [37, 35, 38], [34, 38, 35], [35, 37, 36]])
    +
    +# %%
    +# Rather than create a Triangulation object, can simply pass x, y and triangles
    +# arrays to tripcolor directly.  It would be better to use a Triangulation
    +# object if the same triangulation was to be used more than once to save
    +# duplicated calculations.
    +
    +fig4, ax4 = plt.subplots()
    +ax4.set_aspect('equal')
    +tcf = ax4.tricontourf(x, y, triangles, z)
    +fig4.colorbar(tcf)
    +ax4.set_title('Contour plot of user-specified triangulation')
    +ax4.set_xlabel('Longitude (degrees)')
    +ax4.set_ylabel('Latitude (degrees)')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf`
    +#    - `matplotlib.tri.Triangulation`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#    - `matplotlib.contour.ContourSet.legend_elements`
    diff --git a/galleries/examples/images_contours_and_fields/tricontour_smooth_delaunay.py b/galleries/examples/images_contours_and_fields/tricontour_smooth_delaunay.py
    new file mode 100644
    index 000000000000..0f1ee4938f8d
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/tricontour_smooth_delaunay.py
    @@ -0,0 +1,155 @@
    +"""
    +==========================
    +Tricontour Smooth Delaunay
    +==========================
    +
    +Demonstrates high-resolution tricontouring of a random set of points;
    +a `matplotlib.tri.TriAnalyzer` is used to improve the plot quality.
    +
    +The initial data points and triangular grid for this demo are:
    +
    +- a set of random points is instantiated, inside [-1, 1] x [-1, 1] square
    +- A Delaunay triangulation of these points is then computed, of which a
    +  random subset of triangles is masked out by the user (based on
    +  *init_mask_frac* parameter). This simulates invalidated data.
    +
    +The proposed generic procedure to obtain a high resolution contouring of such
    +a data set is the following:
    +
    +1. Compute an extended mask with a `matplotlib.tri.TriAnalyzer`, which will
    +   exclude badly shaped (flat) triangles from the border of the
    +   triangulation. Apply the mask to the triangulation (using set_mask).
    +2. Refine and interpolate the data using a `matplotlib.tri.UniformTriRefiner`.
    +3. Plot the refined data with `~.axes.Axes.tricontour`.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.tri import TriAnalyzer, Triangulation, UniformTriRefiner
    +
    +
    +# ----------------------------------------------------------------------------
    +# Analytical test function
    +# ----------------------------------------------------------------------------
    +def experiment_res(x, y):
    +    """An analytic function representing experiment results."""
    +    x = 2 * x
    +    r1 = np.sqrt((0.5 - x)**2 + (0.5 - y)**2)
    +    theta1 = np.arctan2(0.5 - x, 0.5 - y)
    +    r2 = np.sqrt((-x - 0.2)**2 + (-y - 0.2)**2)
    +    theta2 = np.arctan2(-x - 0.2, -y - 0.2)
    +    z = (4 * (np.exp((r1/10)**2) - 1) * 30 * np.cos(3 * theta1) +
    +         (np.exp((r2/10)**2) - 1) * 30 * np.cos(5 * theta2) +
    +         2 * (x**2 + y**2))
    +    return (np.max(z) - z) / (np.max(z) - np.min(z))
    +
    +# ----------------------------------------------------------------------------
    +# Generating the initial data test points and triangulation for the demo
    +# ----------------------------------------------------------------------------
    +# User parameters for data test points
    +
    +# Number of test data points, tested from 3 to 5000 for subdiv=3
    +n_test = 200
    +
    +# Number of recursive subdivisions of the initial mesh for smooth plots.
    +# Values >3 might result in a very high number of triangles for the refine
    +# mesh: new triangles numbering = (4**subdiv)*ntri
    +subdiv = 3
    +
    +# Float > 0. adjusting the proportion of (invalid) initial triangles which will
    +# be masked out. Enter 0 for no mask.
    +init_mask_frac = 0.0
    +
    +# Minimum circle ratio - border triangles with circle ratio below this will be
    +# masked if they touch a border. Suggested value 0.01; use -1 to keep all
    +# triangles.
    +min_circle_ratio = .01
    +
    +# Random points
    +random_gen = np.random.RandomState(seed=19680801)
    +x_test = random_gen.uniform(-1., 1., size=n_test)
    +y_test = random_gen.uniform(-1., 1., size=n_test)
    +z_test = experiment_res(x_test, y_test)
    +
    +# meshing with Delaunay triangulation
    +tri = Triangulation(x_test, y_test)
    +ntri = tri.triangles.shape[0]
    +
    +# Some invalid data are masked out
    +mask_init = np.zeros(ntri, dtype=bool)
    +masked_tri = random_gen.randint(0, ntri, int(ntri * init_mask_frac))
    +mask_init[masked_tri] = True
    +tri.set_mask(mask_init)
    +
    +
    +# ----------------------------------------------------------------------------
    +# Improving the triangulation before high-res plots: removing flat triangles
    +# ----------------------------------------------------------------------------
    +# masking badly shaped triangles at the border of the triangular mesh.
    +mask = TriAnalyzer(tri).get_flat_tri_mask(min_circle_ratio)
    +tri.set_mask(mask)
    +
    +# refining the data
    +refiner = UniformTriRefiner(tri)
    +tri_refi, z_test_refi = refiner.refine_field(z_test, subdiv=subdiv)
    +
    +# analytical 'results' for comparison
    +z_expected = experiment_res(tri_refi.x, tri_refi.y)
    +
    +# for the demo: loading the 'flat' triangles for plot
    +flat_tri = Triangulation(x_test, y_test)
    +flat_tri.set_mask(~mask)
    +
    +
    +# ----------------------------------------------------------------------------
    +# Now the plots
    +# ----------------------------------------------------------------------------
    +# User options for plots
    +plot_tri = True          # plot of base triangulation
    +plot_masked_tri = True   # plot of excessively flat excluded triangles
    +plot_refi_tri = False    # plot of refined triangulation
    +plot_expected = False    # plot of analytical function values for comparison
    +
    +
    +# Graphical options for tricontouring
    +levels = np.arange(0., 1., 0.025)
    +
    +fig, ax = plt.subplots()
    +ax.set_aspect('equal')
    +ax.set_title("Filtering a Delaunay mesh\n"
    +             "(application to high-resolution tricontouring)")
    +
    +# 1) plot of the refined (computed) data contours:
    +ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap='Blues',
    +              linewidths=[2.0, 0.5, 1.0, 0.5])
    +# 2) plot of the expected (analytical) data contours (dashed):
    +if plot_expected:
    +    ax.tricontour(tri_refi, z_expected, levels=levels, cmap='Blues',
    +                  linestyles='--')
    +# 3) plot of the fine mesh on which interpolation was done:
    +if plot_refi_tri:
    +    ax.triplot(tri_refi, color='0.97')
    +# 4) plot of the initial 'coarse' mesh:
    +if plot_tri:
    +    ax.triplot(tri, color='0.7')
    +# 4) plot of the unvalidated triangles from naive Delaunay Triangulation:
    +if plot_masked_tri:
    +    ax.triplot(flat_tri, color='red')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour`
    +#    - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf`
    +#    - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.Triangulation`
    +#    - `matplotlib.tri.TriAnalyzer`
    +#    - `matplotlib.tri.UniformTriRefiner`
    diff --git a/galleries/examples/images_contours_and_fields/tricontour_smooth_user.py b/galleries/examples/images_contours_and_fields/tricontour_smooth_user.py
    new file mode 100644
    index 000000000000..2d973c0de108
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/tricontour_smooth_user.py
    @@ -0,0 +1,89 @@
    +"""
    +======================
    +Tricontour Smooth User
    +======================
    +
    +Demonstrates high-resolution tricontouring on user-defined triangular grids
    +with `matplotlib.tri.UniformTriRefiner`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +
    +# ----------------------------------------------------------------------------
    +# Analytical test function
    +# ----------------------------------------------------------------------------
    +def function_z(x, y):
    +    r1 = np.sqrt((0.5 - x)**2 + (0.5 - y)**2)
    +    theta1 = np.arctan2(0.5 - x, 0.5 - y)
    +    r2 = np.sqrt((-x - 0.2)**2 + (-y - 0.2)**2)
    +    theta2 = np.arctan2(-x - 0.2, -y - 0.2)
    +    z = -(2 * (np.exp((r1 / 10)**2) - 1) * 30. * np.cos(7. * theta1) +
    +          (np.exp((r2 / 10)**2) - 1) * 30. * np.cos(11. * theta2) +
    +          0.7 * (x**2 + y**2))
    +    return (np.max(z) - z) / (np.max(z) - np.min(z))
    +
    +# ----------------------------------------------------------------------------
    +# Creating a Triangulation
    +# ----------------------------------------------------------------------------
    +# First create the x and y coordinates of the points.
    +n_angles = 20
    +n_radii = 10
    +min_radius = 0.15
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi / n_angles
    +
    +x = (radii * np.cos(angles)).flatten()
    +y = (radii * np.sin(angles)).flatten()
    +z = function_z(x, y)
    +
    +# Now create the Triangulation.
    +# (Creating a Triangulation without specifying the triangles results in the
    +# Delaunay triangulation of the points.)
    +triang = tri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +# ----------------------------------------------------------------------------
    +# Refine data
    +# ----------------------------------------------------------------------------
    +refiner = tri.UniformTriRefiner(triang)
    +tri_refi, z_test_refi = refiner.refine_field(z, subdiv=3)
    +
    +# ----------------------------------------------------------------------------
    +# Plot the triangulation and the high-res iso-contours
    +# ----------------------------------------------------------------------------
    +fig, ax = plt.subplots()
    +ax.set_aspect('equal')
    +ax.triplot(triang, lw=0.5, color='white')
    +
    +levels = np.arange(0., 1., 0.025)
    +ax.tricontourf(tri_refi, z_test_refi, levels=levels, cmap='terrain')
    +ax.tricontour(tri_refi, z_test_refi, levels=levels,
    +              colors=['0.25', '0.5', '0.5', '0.5', '0.5'],
    +              linewidths=[1.0, 0.5, 0.5, 0.5, 0.5])
    +
    +ax.set_title("High-resolution tricontouring")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour`
    +#    - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.Triangulation`
    +#    - `matplotlib.tri.UniformTriRefiner`
    diff --git a/galleries/examples/images_contours_and_fields/trigradient_demo.py b/galleries/examples/images_contours_and_fields/trigradient_demo.py
    new file mode 100644
    index 000000000000..dcfd23ada73b
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/trigradient_demo.py
    @@ -0,0 +1,102 @@
    +"""
    +================
    +Trigradient Demo
    +================
    +
    +Demonstrates computation of gradient with
    +`matplotlib.tri.CubicTriInterpolator`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.tri import CubicTriInterpolator, Triangulation, UniformTriRefiner
    +
    +
    +# ----------------------------------------------------------------------------
    +# Electrical potential of a dipole
    +# ----------------------------------------------------------------------------
    +def dipole_potential(x, y):
    +    """The electric dipole potential V, at position *x*, *y*."""
    +    r_sq = x**2 + y**2
    +    theta = np.arctan2(y, x)
    +    z = np.cos(theta)/r_sq
    +    return (np.max(z) - z) / (np.max(z) - np.min(z))
    +
    +
    +# ----------------------------------------------------------------------------
    +# Creating a Triangulation
    +# ----------------------------------------------------------------------------
    +# First create the x and y coordinates of the points.
    +n_angles = 30
    +n_radii = 10
    +min_radius = 0.2
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi / n_angles
    +
    +x = (radii*np.cos(angles)).flatten()
    +y = (radii*np.sin(angles)).flatten()
    +V = dipole_potential(x, y)
    +
    +# Create the Triangulation; no triangles specified so Delaunay triangulation
    +# created.
    +triang = Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +# ----------------------------------------------------------------------------
    +# Refine data - interpolates the electrical potential V
    +# ----------------------------------------------------------------------------
    +refiner = UniformTriRefiner(triang)
    +tri_refi, z_test_refi = refiner.refine_field(V, subdiv=3)
    +
    +# ----------------------------------------------------------------------------
    +# Computes the electrical field (Ex, Ey) as gradient of electrical potential
    +# ----------------------------------------------------------------------------
    +tci = CubicTriInterpolator(triang, -V)
    +# Gradient requested here at the mesh nodes but could be anywhere else:
    +(Ex, Ey) = tci.gradient(triang.x, triang.y)
    +E_norm = np.sqrt(Ex**2 + Ey**2)
    +
    +# ----------------------------------------------------------------------------
    +# Plot the triangulation, the potential iso-contours and the vector field
    +# ----------------------------------------------------------------------------
    +fig, ax = plt.subplots()
    +ax.set_aspect('equal')
    +# Enforce the margins, and enlarge them to give room for the vectors.
    +ax.use_sticky_edges = False
    +ax.margins(0.07)
    +
    +ax.triplot(triang, color='0.8')
    +
    +levels = np.arange(0., 1., 0.01)
    +ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap='hot',
    +              linewidths=[2.0, 1.0, 1.0, 1.0])
    +# Plots direction of the electrical vector field
    +ax.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm,
    +          units='xy', scale=10., zorder=3, color='blue',
    +          width=0.007, headwidth=3., headlength=4.)
    +
    +ax.set_title('Gradient plot: an electrical dipole')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tricontour` / `matplotlib.pyplot.tricontour`
    +#    - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.Triangulation`
    +#    - `matplotlib.tri.CubicTriInterpolator`
    +#    - `matplotlib.tri.CubicTriInterpolator.gradient`
    +#    - `matplotlib.tri.UniformTriRefiner`
    +#    - `matplotlib.axes.Axes.quiver` / `matplotlib.pyplot.quiver`
    diff --git a/galleries/examples/images_contours_and_fields/triinterp_demo.py b/galleries/examples/images_contours_and_fields/triinterp_demo.py
    new file mode 100644
    index 000000000000..a5bd31f775cd
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/triinterp_demo.py
    @@ -0,0 +1,77 @@
    +"""
    +==============
    +Triinterp Demo
    +==============
    +
    +Interpolation from triangular grid to quad grid.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as mtri
    +
    +# Create triangulation.
    +x = np.asarray([0, 1, 2, 3, 0.5, 1.5, 2.5, 1, 2, 1.5])
    +y = np.asarray([0, 0, 0, 0, 1.0, 1.0, 1.0, 2, 2, 3.0])
    +triangles = [[0, 1, 4], [1, 2, 5], [2, 3, 6], [1, 5, 4], [2, 6, 5], [4, 5, 7],
    +             [5, 6, 8], [5, 8, 7], [7, 8, 9]]
    +triang = mtri.Triangulation(x, y, triangles)
    +
    +# Interpolate to regularly-spaced quad grid.
    +z = np.cos(1.5 * x) * np.cos(1.5 * y)
    +xi, yi = np.meshgrid(np.linspace(0, 3, 20), np.linspace(0, 3, 20))
    +
    +interp_lin = mtri.LinearTriInterpolator(triang, z)
    +zi_lin = interp_lin(xi, yi)
    +
    +interp_cubic_geom = mtri.CubicTriInterpolator(triang, z, kind='geom')
    +zi_cubic_geom = interp_cubic_geom(xi, yi)
    +
    +interp_cubic_min_E = mtri.CubicTriInterpolator(triang, z, kind='min_E')
    +zi_cubic_min_E = interp_cubic_min_E(xi, yi)
    +
    +# Set up the figure
    +fig, axs = plt.subplots(nrows=2, ncols=2)
    +axs = axs.flatten()
    +
    +# Plot the triangulation.
    +axs[0].tricontourf(triang, z)
    +axs[0].triplot(triang, 'ko-')
    +axs[0].set_title('Triangular grid')
    +
    +# Plot linear interpolation to quad grid.
    +axs[1].contourf(xi, yi, zi_lin)
    +axs[1].plot(xi, yi, 'k-', lw=0.5, alpha=0.5)
    +axs[1].plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5)
    +axs[1].set_title("Linear interpolation")
    +
    +# Plot cubic interpolation to quad grid, kind=geom
    +axs[2].contourf(xi, yi, zi_cubic_geom)
    +axs[2].plot(xi, yi, 'k-', lw=0.5, alpha=0.5)
    +axs[2].plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5)
    +axs[2].set_title("Cubic interpolation,\nkind='geom'")
    +
    +# Plot cubic interpolation to quad grid, kind=min_E
    +axs[3].contourf(xi, yi, zi_cubic_min_E)
    +axs[3].plot(xi, yi, 'k-', lw=0.5, alpha=0.5)
    +axs[3].plot(xi.T, yi.T, 'k-', lw=0.5, alpha=0.5)
    +axs[3].set_title("Cubic interpolation,\nkind='min_E'")
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tricontourf` / `matplotlib.pyplot.tricontourf`
    +#    - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot`
    +#    - `matplotlib.axes.Axes.contourf` / `matplotlib.pyplot.contourf`
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.LinearTriInterpolator`
    +#    - `matplotlib.tri.CubicTriInterpolator`
    +#    - `matplotlib.tri.Triangulation`
    diff --git a/galleries/examples/images_contours_and_fields/tripcolor_demo.py b/galleries/examples/images_contours_and_fields/tripcolor_demo.py
    new file mode 100644
    index 000000000000..a1c011a1224c
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/tripcolor_demo.py
    @@ -0,0 +1,138 @@
    +"""
    +==============
    +Tripcolor Demo
    +==============
    +
    +Pseudocolor plots of unstructured triangular grids.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +# %%
    +# Creating a Triangulation without specifying the triangles results in the
    +# Delaunay triangulation of the points.
    +
    +# First create the x and y coordinates of the points.
    +n_angles = 36
    +n_radii = 8
    +min_radius = 0.25
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi / n_angles
    +
    +x = (radii * np.cos(angles)).flatten()
    +y = (radii * np.sin(angles)).flatten()
    +z = (np.cos(radii) * np.cos(3 * angles)).flatten()
    +
    +# Create the Triangulation; no triangles so Delaunay triangulation created.
    +triang = tri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +# %%
    +# tripcolor plot.
    +
    +fig1, ax1 = plt.subplots()
    +ax1.set_aspect('equal')
    +tpc = ax1.tripcolor(triang, z, shading='flat')
    +fig1.colorbar(tpc)
    +ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
    +
    +# %%
    +# Illustrate Gouraud shading.
    +
    +fig2, ax2 = plt.subplots()
    +ax2.set_aspect('equal')
    +tpc = ax2.tripcolor(triang, z, shading='gouraud')
    +fig2.colorbar(tpc)
    +ax2.set_title('tripcolor of Delaunay triangulation, gouraud shading')
    +
    +
    +# %%
    +# You can specify your own triangulation rather than perform a Delaunay
    +# triangulation of the points, where each triangle is given by the indices of
    +# the three points that make up the triangle, ordered in either a clockwise or
    +# anticlockwise manner.
    +
    +xy = np.asarray([
    +    [-0.101, 0.872], [-0.080, 0.883], [-0.069, 0.888], [-0.054, 0.890],
    +    [-0.045, 0.897], [-0.057, 0.895], [-0.073, 0.900], [-0.087, 0.898],
    +    [-0.090, 0.904], [-0.069, 0.907], [-0.069, 0.921], [-0.080, 0.919],
    +    [-0.073, 0.928], [-0.052, 0.930], [-0.048, 0.942], [-0.062, 0.949],
    +    [-0.054, 0.958], [-0.069, 0.954], [-0.087, 0.952], [-0.087, 0.959],
    +    [-0.080, 0.966], [-0.085, 0.973], [-0.087, 0.965], [-0.097, 0.965],
    +    [-0.097, 0.975], [-0.092, 0.984], [-0.101, 0.980], [-0.108, 0.980],
    +    [-0.104, 0.987], [-0.102, 0.993], [-0.115, 1.001], [-0.099, 0.996],
    +    [-0.101, 1.007], [-0.090, 1.010], [-0.087, 1.021], [-0.069, 1.021],
    +    [-0.052, 1.022], [-0.052, 1.017], [-0.069, 1.010], [-0.064, 1.005],
    +    [-0.048, 1.005], [-0.031, 1.005], [-0.031, 0.996], [-0.040, 0.987],
    +    [-0.045, 0.980], [-0.052, 0.975], [-0.040, 0.973], [-0.026, 0.968],
    +    [-0.020, 0.954], [-0.006, 0.947], [ 0.003, 0.935], [ 0.006, 0.926],
    +    [ 0.005, 0.921], [ 0.022, 0.923], [ 0.033, 0.912], [ 0.029, 0.905],
    +    [ 0.017, 0.900], [ 0.012, 0.895], [ 0.027, 0.893], [ 0.019, 0.886],
    +    [ 0.001, 0.883], [-0.012, 0.884], [-0.029, 0.883], [-0.038, 0.879],
    +    [-0.057, 0.881], [-0.062, 0.876], [-0.078, 0.876], [-0.087, 0.872],
    +    [-0.030, 0.907], [-0.007, 0.905], [-0.057, 0.916], [-0.025, 0.933],
    +    [-0.077, 0.990], [-0.059, 0.993]])
    +x, y = np.rad2deg(xy).T
    +
    +triangles = np.asarray([
    +    [67, 66,  1], [65,  2, 66], [ 1, 66,  2], [64,  2, 65], [63,  3, 64],
    +    [60, 59, 57], [ 2, 64,  3], [ 3, 63,  4], [ 0, 67,  1], [62,  4, 63],
    +    [57, 59, 56], [59, 58, 56], [61, 60, 69], [57, 69, 60], [ 4, 62, 68],
    +    [ 6,  5,  9], [61, 68, 62], [69, 68, 61], [ 9,  5, 70], [ 6,  8,  7],
    +    [ 4, 70,  5], [ 8,  6,  9], [56, 69, 57], [69, 56, 52], [70, 10,  9],
    +    [54, 53, 55], [56, 55, 53], [68, 70,  4], [52, 56, 53], [11, 10, 12],
    +    [69, 71, 68], [68, 13, 70], [10, 70, 13], [51, 50, 52], [13, 68, 71],
    +    [52, 71, 69], [12, 10, 13], [71, 52, 50], [71, 14, 13], [50, 49, 71],
    +    [49, 48, 71], [14, 16, 15], [14, 71, 48], [17, 19, 18], [17, 20, 19],
    +    [48, 16, 14], [48, 47, 16], [47, 46, 16], [16, 46, 45], [23, 22, 24],
    +    [21, 24, 22], [17, 16, 45], [20, 17, 45], [21, 25, 24], [27, 26, 28],
    +    [20, 72, 21], [25, 21, 72], [45, 72, 20], [25, 28, 26], [44, 73, 45],
    +    [72, 45, 73], [28, 25, 29], [29, 25, 31], [43, 73, 44], [73, 43, 40],
    +    [72, 73, 39], [72, 31, 25], [42, 40, 43], [31, 30, 29], [39, 73, 40],
    +    [42, 41, 40], [72, 33, 31], [32, 31, 33], [39, 38, 72], [33, 72, 38],
    +    [33, 38, 34], [37, 35, 38], [34, 38, 35], [35, 37, 36]])
    +
    +xmid = x[triangles].mean(axis=1)
    +ymid = y[triangles].mean(axis=1)
    +x0 = -5
    +y0 = 52
    +zfaces = np.exp(-0.01 * ((xmid - x0) * (xmid - x0) +
    +                         (ymid - y0) * (ymid - y0)))
    +
    +# %%
    +# Rather than create a Triangulation object, can simply pass x, y and triangles
    +# arrays to tripcolor directly.  It would be better to use a Triangulation
    +# object if the same triangulation was to be used more than once to save
    +# duplicated calculations.
    +# Can specify one color value per face rather than one per point by using the
    +# *facecolors* keyword argument.
    +
    +fig3, ax3 = plt.subplots()
    +ax3.set_aspect('equal')
    +tpc = ax3.tripcolor(x, y, triangles, facecolors=zfaces, edgecolors='k')
    +fig3.colorbar(tpc)
    +ax3.set_title('tripcolor of user-specified triangulation')
    +ax3.set_xlabel('Longitude (degrees)')
    +ax3.set_ylabel('Latitude (degrees)')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tripcolor` / `matplotlib.pyplot.tripcolor`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.Triangulation`
    diff --git a/examples/pylab_examples/triplot_demo.py b/galleries/examples/images_contours_and_fields/triplot_demo.py
    similarity index 78%
    rename from examples/pylab_examples/triplot_demo.py
    rename to galleries/examples/images_contours_and_fields/triplot_demo.py
    index 7b3cee6cdd18..e1151b37ac4a 100644
    --- a/examples/pylab_examples/triplot_demo.py
    +++ b/galleries/examples/images_contours_and_fields/triplot_demo.py
    @@ -1,11 +1,16 @@
     """
    +============
    +Triplot Demo
    +============
    +
     Creating and plotting unstructured triangular grids.
     """
     import matplotlib.pyplot as plt
    -import matplotlib.tri as tri
     import numpy as np
    -import math
     
    +import matplotlib.tri as tri
    +
    +# %%
     # Creating a Triangulation without specifying the triangles results in the
     # Delaunay triangulation of the points.
     
    @@ -15,29 +20,31 @@
     min_radius = 0.25
     radii = np.linspace(min_radius, 0.95, n_radii)
     
    -angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
    +angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
     angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    -angles[:, 1::2] += math.pi/n_angles
    +angles[:, 1::2] += np.pi / n_angles
     
    -x = (radii*np.cos(angles)).flatten()
    -y = (radii*np.sin(angles)).flatten()
    +x = (radii * np.cos(angles)).flatten()
    +y = (radii * np.sin(angles)).flatten()
     
     # Create the Triangulation; no triangles so Delaunay triangulation created.
     triang = tri.Triangulation(x, y)
     
     # Mask off unwanted triangles.
    -xmid = x[triang.triangles].mean(axis=1)
    -ymid = y[triang.triangles].mean(axis=1)
    -mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
    -triang.set_mask(mask)
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
     
    +# %%
     # Plot the triangulation.
    -plt.figure()
    -plt.gca().set_aspect('equal')
    -plt.triplot(triang, 'bo-', lw=1)
    -plt.title('triplot of Delaunay triangulation')
     
    +fig1, ax1 = plt.subplots()
    +ax1.set_aspect('equal')
    +ax1.triplot(triang, 'bo-', lw=1)
    +ax1.set_title('triplot of Delaunay triangulation')
     
    +
    +# %%
     # You can specify your own triangulation rather than perform a Delaunay
     # triangulation of the points, where each triangle is given by the indices of
     # the three points that make up the triangle, ordered in either a clockwise or
    @@ -84,15 +91,28 @@
         [42, 41, 40], [72, 33, 31], [32, 31, 33], [39, 38, 72], [33, 72, 38],
         [33, 38, 34], [37, 35, 38], [34, 38, 35], [35, 37, 36]])
     
    +# %%
     # Rather than create a Triangulation object, can simply pass x, y and triangles
     # arrays to triplot directly.  It would be better to use a Triangulation object
     # if the same triangulation was to be used more than once to save duplicated
     # calculations.
    -plt.figure()
    -plt.gca().set_aspect('equal')
    -plt.triplot(x, y, triangles, 'go-', lw=1.0)
    -plt.title('triplot of user-specified triangulation')
    -plt.xlabel('Longitude (degrees)')
    -plt.ylabel('Latitude (degrees)')
    +
    +fig2, ax2 = plt.subplots()
    +ax2.set_aspect('equal')
    +ax2.triplot(x, y, triangles, 'go-', lw=1.0)
    +ax2.set_title('triplot of user-specified triangulation')
    +ax2.set_xlabel('Longitude (degrees)')
    +ax2.set_ylabel('Latitude (degrees)')
     
     plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.triplot` / `matplotlib.pyplot.triplot`
    +#    - `matplotlib.tri`
    +#    - `matplotlib.tri.Triangulation`
    diff --git a/galleries/examples/images_contours_and_fields/watermark_image.py b/galleries/examples/images_contours_and_fields/watermark_image.py
    new file mode 100644
    index 000000000000..f2903ad28d49
    --- /dev/null
    +++ b/galleries/examples/images_contours_and_fields/watermark_image.py
    @@ -0,0 +1,40 @@
    +"""
    +===============
    +Watermark image
    +===============
    +
    +Overlay an image on a plot by moving it to the front (``zorder=3``) and making it
    +semi-transparent (``alpha=0.7``).
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +import matplotlib.image as image
    +
    +with cbook.get_sample_data('logo2.png') as file:
    +    im = image.imread(file)
    +
    +fig, ax = plt.subplots()
    +
    +np.random.seed(19680801)
    +x = np.arange(30)
    +y = x + np.random.randn(30)
    +ax.bar(x, y, color='#6bbc6b')
    +ax.grid()
    +
    +fig.figimage(im, 25, 25, zorder=3, alpha=.7)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.image`
    +#    - `matplotlib.image.imread` / `matplotlib.pyplot.imread`
    +#    - `matplotlib.figure.Figure.figimage`
    diff --git a/galleries/examples/lines_bars_and_markers/README.txt b/galleries/examples/lines_bars_and_markers/README.txt
    new file mode 100644
    index 000000000000..a842344008fa
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/README.txt
    @@ -0,0 +1,4 @@
    +.. _lines_bars_and_markers_example:
    +
    +Lines, bars and markers
    +=======================
    diff --git a/galleries/examples/lines_bars_and_markers/axline.py b/galleries/examples/lines_bars_and_markers/axline.py
    new file mode 100644
    index 000000000000..11e8c646fe41
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/axline.py
    @@ -0,0 +1,63 @@
    +"""
    +==============
    +Infinite lines
    +==============
    +
    +`~.axes.Axes.axvline` and `~.axes.Axes.axhline` draw infinite vertical /
    +horizontal lines, at given *x* / *y* positions. They are usually used to mark
    +special data values, e.g. in this example the center and limit values of the
    +sigmoid function.
    +
    +`~.axes.Axes.axline` draws infinite straight lines in arbitrary directions.
    +
    +.. redirect-from:: /gallery/pyplot/axline
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.linspace(-10, 10, 100)
    +sig = 1 / (1 + np.exp(-t))
    +
    +fig, ax = plt.subplots()
    +ax.axhline(y=0, color="black", linestyle="--")
    +ax.axhline(y=0.5, color="black", linestyle=":")
    +ax.axhline(y=1.0, color="black", linestyle="--")
    +ax.axvline(color="grey")
    +ax.axline((0, 0.5), slope=0.25, color="black", linestyle=(0, (5, 5)))
    +ax.plot(t, sig, linewidth=2, label=r"$\sigma(t) = \frac{1}{1 + e^{-t}}$")
    +ax.set(xlim=(-10, 10), xlabel="t")
    +ax.legend(fontsize=14)
    +plt.show()
    +
    +# %%
    +# `~.axes.Axes.axline` can also be used with a *transform* parameter, which
    +# applies to the point, but not to the slope. This can be useful for drawing
    +# diagonal grid lines with a fixed slope, which stay in place when the
    +# plot limits are moved.
    +
    +fig, ax = plt.subplots()
    +for pos in np.linspace(-2, 1, 10):
    +    ax.axline((pos, 0), slope=0.5, color='k', transform=ax.transAxes)
    +
    +ax.set(xlim=(0, 1), ylim=(0, 1))
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.axhline` / `matplotlib.pyplot.axhline`
    +#    - `matplotlib.axes.Axes.axvline` / `matplotlib.pyplot.axvline`
    +#    - `matplotlib.axes.Axes.axline` / `matplotlib.pyplot.axline`
    +#
    +#
    +# .. seealso::
    +#
    +#    `~.Axes.axhspan`, `~.Axes.axvspan` draw rectangles that span the Axes in one
    +#    direction and are bounded in the other direction.
    +#
    +# .. tags:: component: annotation
    diff --git a/galleries/examples/lines_bars_and_markers/bar_colors.py b/galleries/examples/lines_bars_and_markers/bar_colors.py
    new file mode 100644
    index 000000000000..18af2b3ee9a7
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/bar_colors.py
    @@ -0,0 +1,33 @@
    +"""
    +====================================
    +Bar chart with individual bar colors
    +====================================
    +
    +This is an example showing how to control bar color and legend entries
    +using the *color* and *label* parameters of `~matplotlib.pyplot.bar`.
    +Note that labels with a preceding underscore won't show up in the legend.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +fruits = ['apple', 'blueberry', 'cherry', 'orange']
    +counts = [40, 100, 30, 55]
    +bar_labels = ['red', 'blue', '_red', 'orange']
    +bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange']
    +
    +ax.bar(fruits, counts, label=bar_labels, color=bar_colors)
    +
    +ax.set_ylabel('fruit supply')
    +ax.set_title('Fruit supply by kind and color')
    +ax.legend(title='Fruit color')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: color
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/bar_label_demo.py b/galleries/examples/lines_bars_and_markers/bar_label_demo.py
    new file mode 100644
    index 000000000000..2e43dbb18013
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/bar_label_demo.py
    @@ -0,0 +1,126 @@
    +"""
    +=====================
    +Bar chart with labels
    +=====================
    +
    +This example shows how to use the `~.Axes.bar_label` helper function
    +to create bar chart labels.
    +
    +See also the :doc:`grouped bar
    +`,
    +:doc:`stacked bar
    +` and
    +:doc:`horizontal bar chart
    +` examples.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +# data from https://allisonhorst.github.io/palmerpenguins/
    +
    +species = ('Adelie', 'Chinstrap', 'Gentoo')
    +sex_counts = {
    +    'Male': np.array([73, 34, 61]),
    +    'Female': np.array([73, 34, 58]),
    +}
    +width = 0.6  # the width of the bars: can also be len(x) sequence
    +
    +
    +fig, ax = plt.subplots()
    +bottom = np.zeros(3)
    +
    +for sex, sex_count in sex_counts.items():
    +    p = ax.bar(species, sex_count, width, label=sex, bottom=bottom)
    +    bottom += sex_count
    +
    +    ax.bar_label(p, label_type='center')
    +
    +ax.set_title('Number of penguins by sex')
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# Horizontal bar chart
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# Example data
    +people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
    +y_pos = np.arange(len(people))
    +performance = 3 + 10 * np.random.rand(len(people))
    +error = np.random.rand(len(people))
    +
    +fig, ax = plt.subplots()
    +
    +hbars = ax.barh(y_pos, performance, xerr=error, align='center')
    +ax.set_yticks(y_pos, labels=people)
    +ax.invert_yaxis()  # labels read top-to-bottom
    +ax.set_xlabel('Performance')
    +ax.set_title('How fast do you want to go today?')
    +
    +# Label with specially formatted floats
    +ax.bar_label(hbars, fmt='%.2f')
    +ax.set_xlim(right=15)  # adjust xlim to fit labels
    +
    +plt.show()
    +
    +# %%
    +# Some of the more advanced things that one can do with bar labels
    +
    +fig, ax = plt.subplots()
    +
    +hbars = ax.barh(y_pos, performance, xerr=error, align='center')
    +ax.set_yticks(y_pos, labels=people)
    +ax.invert_yaxis()  # labels read top-to-bottom
    +ax.set_xlabel('Performance')
    +ax.set_title('How fast do you want to go today?')
    +
    +# Label with given captions, custom padding and annotate options
    +ax.bar_label(hbars, labels=[f'±{e:.2f}' for e in error],
    +             padding=8, color='b', fontsize=14)
    +ax.set_xlim(right=16)
    +
    +plt.show()
    +
    +# %%
    +# Bar labels using {}-style format string
    +
    +fruit_names = ['Coffee', 'Salted Caramel', 'Pistachio']
    +fruit_counts = [4000, 2000, 7000]
    +
    +fig, ax = plt.subplots()
    +bar_container = ax.bar(fruit_names, fruit_counts)
    +ax.set(ylabel='pints sold', title='Gelato sales by flavor', ylim=(0, 8000))
    +ax.bar_label(bar_container, fmt='{:,.0f}')
    +
    +# %%
    +# Bar labels using a callable
    +
    +animal_names = ['Lion', 'Gazelle', 'Cheetah']
    +mph_speed = [50, 60, 75]
    +
    +fig, ax = plt.subplots()
    +bar_container = ax.bar(animal_names, mph_speed)
    +ax.set(ylabel='speed in MPH', title='Running speeds', ylim=(0, 80))
    +ax.bar_label(bar_container, fmt=lambda x: f'{x * 1.61:.1f} km/h')
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh`
    +#    - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label`
    +#
    +# .. tags::
    +#
    +#    component: label
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/bar_stacked.py b/galleries/examples/lines_bars_and_markers/bar_stacked.py
    new file mode 100644
    index 000000000000..f1f97e89da13
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/bar_stacked.py
    @@ -0,0 +1,42 @@
    +"""
    +=================
    +Stacked bar chart
    +=================
    +
    +This is an example of creating a stacked bar plot
    +using `~matplotlib.pyplot.bar`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# data from https://allisonhorst.github.io/palmerpenguins/
    +
    +species = (
    +    "Adelie\n $\\mu=$3700.66g",
    +    "Chinstrap\n $\\mu=$3733.09g",
    +    "Gentoo\n $\\mu=5076.02g$",
    +)
    +weight_counts = {
    +    "Below": np.array([70, 31, 58]),
    +    "Above": np.array([82, 37, 66]),
    +}
    +width = 0.5
    +
    +fig, ax = plt.subplots()
    +bottom = np.zeros(3)
    +
    +for boolean, weight_count in weight_counts.items():
    +    p = ax.bar(species, weight_count, width, label=boolean, bottom=bottom)
    +    bottom += weight_count
    +
    +ax.set_title("Number of penguins with above average body mass")
    +ax.legend(loc="upper right")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/barchart.py b/galleries/examples/lines_bars_and_markers/barchart.py
    new file mode 100644
    index 000000000000..dbb0f5bbbadd
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/barchart.py
    @@ -0,0 +1,49 @@
    +"""
    +=============================
    +Grouped bar chart with labels
    +=============================
    +
    +This example shows a how to create a grouped bar chart and how to annotate
    +bars with labels.
    +"""
    +
    +# data from https://allisonhorst.github.io/palmerpenguins/
    +
    +import matplotlib.pyplot as plt
    +
    +species = ("Adelie", "Chinstrap", "Gentoo")
    +penguin_means = {
    +    'Bill Depth': (18.35, 18.43, 14.98),
    +    'Bill Length': (38.79, 48.83, 47.50),
    +    'Flipper Length': (189.95, 195.82, 217.19),
    +}
    +
    +fig, ax = plt.subplots(layout='constrained')
    +
    +res = ax.grouped_bar(penguin_means, tick_labels=species, group_spacing=1)
    +for container in res.bar_containers:
    +    ax.bar_label(container, padding=3)
    +
    +# Add some text for labels, title, etc.
    +ax.set_ylabel('Length (mm)')
    +ax.set_title('Penguin attributes by species')
    +ax.legend(loc='upper left', ncols=3)
    +ax.set_ylim(0, 250)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label`
    +#
    +# .. tags::
    +#
    +#    component: label
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/barh.py b/galleries/examples/lines_bars_and_markers/barh.py
    new file mode 100644
    index 000000000000..8529698c1ddb
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/barh.py
    @@ -0,0 +1,28 @@
    +"""
    +====================
    +Horizontal bar chart
    +====================
    +
    +This example showcases a simple horizontal bar chart.
    +"""
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +# Example data
    +people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
    +performance = [5, 7, 6, 4, 9]
    +error = [0.2, 0.4, 0.3, 0.6, 0.2]
    +
    +ax.barh(people, performance, xerr=error, align='center')
    +ax.yaxis.set_inverted(True)  # arrange data from top to bottom
    +ax.set_xlabel('Performance')
    +ax.set_title('How fast do you want to go today?')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/broken_barh.py b/galleries/examples/lines_bars_and_markers/broken_barh.py
    new file mode 100644
    index 000000000000..a709e911773d
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/broken_barh.py
    @@ -0,0 +1,50 @@
    +"""
    +======================
    +Broken horizontal bars
    +======================
    +
    +`~.Axes.broken_barh` creates sequences of horizontal bars. This example shows
    +a timing diagram.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# data is a sequence of (start, duration) tuples
    +cpu_1 = [(0, 3), (3.5, 1), (5, 5)]
    +cpu_2 = np.column_stack([np.linspace(0, 9, 10), np.full(10, 0.5)])
    +cpu_3 = np.column_stack([10*np.random.random(61), np.full(61, 0.05)])
    +cpu_4 = [(2, 1.7), (7, 1.2)]
    +disk = [(1, 1.5)]
    +network = np.column_stack([10*np.random.random(10), np.full(10, 0.05)])
    +
    +fig, ax = plt.subplots()
    +# broken_barh(xranges, (ypos, height))
    +ax.broken_barh(cpu_1, (0, 0.4), align="center")
    +ax.broken_barh(cpu_2, (1, 0.4), align="center")
    +ax.broken_barh(cpu_3, (2, 0.4), align="center")
    +ax.broken_barh(cpu_4, (3, 0.4), align="center")
    +ax.broken_barh(disk, (4, 0.4), align="center", color="tab:orange")
    +ax.broken_barh(network, (5, 0.4), align="center", color="tab:green")
    +ax.set_xlim(0, 10)
    +ax.set_yticks(range(6),
    +              labels=["CPU 1", "CPU 2", "CPU 3", "CPU 4", "disk", "network"])
    +ax.invert_yaxis()
    +ax.set_title("Resource usage")
    +
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.broken_barh` / `matplotlib.pyplot.broken_barh`
    +#    - `matplotlib.axes.Axes.invert_yaxis`
    +#    - `matplotlib.axes.Axes.set_yticks`
    +#
    +# .. tags::
    +#
    +#    component: annotation
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/capstyle.py b/galleries/examples/lines_bars_and_markers/capstyle.py
    new file mode 100644
    index 000000000000..4927c904caa7
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/capstyle.py
    @@ -0,0 +1,21 @@
    +"""
    +=========
    +CapStyle
    +=========
    +
    +The `matplotlib._enums.CapStyle` controls how Matplotlib draws the two
    +endpoints (caps) of an unclosed line. For more details, see the
    +`~matplotlib._enums.CapStyle` docs.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib._enums import CapStyle
    +
    +CapStyle.demo()
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    purpose: reference
    diff --git a/galleries/examples/lines_bars_and_markers/categorical_variables.py b/galleries/examples/lines_bars_and_markers/categorical_variables.py
    new file mode 100644
    index 000000000000..a19a30eda2b2
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/categorical_variables.py
    @@ -0,0 +1,43 @@
    +"""
    +==============================
    +Plotting categorical variables
    +==============================
    +
    +You can pass categorical values (i.e. strings) directly as x- or y-values to
    +many plotting functions:
    +"""
    +import matplotlib.pyplot as plt
    +
    +data = {'apple': 10, 'orange': 15, 'lemon': 5, 'lime': 20}
    +names = list(data.keys())
    +values = list(data.values())
    +
    +fig, axs = plt.subplots(1, 3, figsize=(9, 3), sharey=True)
    +axs[0].bar(names, values)
    +axs[1].scatter(names, values)
    +axs[2].plot(names, values)
    +fig.suptitle('Categorical Plotting')
    +
    +
    +# %%
    +# Categorical values are a mapping from names to positions. This means that
    +# values that occur multiple times are mapped to the same position. See the
    +# ``cat`` and ``dog`` values "happy" and "bored" on the y-axis in the following
    +# example.
    +
    +cat = ["bored", "happy", "bored", "bored", "happy", "bored"]
    +dog = ["happy", "happy", "happy", "happy", "bored", "bored"]
    +activity = ["combing", "drinking", "feeding", "napping", "playing", "washing"]
    +
    +fig, ax = plt.subplots()
    +ax.plot(activity, dog, label="dog")
    +ax.plot(activity, cat, label="cat")
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: specialty
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/curve_error_band.py b/galleries/examples/lines_bars_and_markers/curve_error_band.py
    new file mode 100644
    index 000000000000..320d2e710be6
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/curve_error_band.py
    @@ -0,0 +1,93 @@
    +"""
    +=====================
    +Curve with error band
    +=====================
    +
    +This example illustrates how to draw an error band around a parametrized curve.
    +
    +A parametrized curve x(t), y(t) can directly be drawn using `~.Axes.plot`.
    +"""
    +# sphinx_gallery_thumbnail_number = 2
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import PathPatch
    +from matplotlib.path import Path
    +
    +N = 400
    +t = np.linspace(0, 2 * np.pi, N)
    +r = 0.5 + np.cos(t)
    +x, y = r * np.cos(t), r * np.sin(t)
    +
    +fig, ax = plt.subplots()
    +ax.plot(x, y, "k")
    +ax.set(aspect=1)
    +
    +# %%
    +# An error band can be used to indicate the uncertainty of the curve.
    +# In this example we assume that the error can be given as a scalar *err*
    +# that describes the uncertainty perpendicular to the curve in every point.
    +#
    +# We visualize this error as a colored band around the path using a
    +# `.PathPatch`. The patch is created from two path segments *(xp, yp)*, and
    +# *(xn, yn)* that are shifted by +/- *err* perpendicular to the curve *(x, y)*.
    +#
    +# Note: This method of using a `.PathPatch` is suited to arbitrary curves in
    +# 2D. If you just have a standard y-vs.-x plot, you can use the simpler
    +# `~.Axes.fill_between` method (see also
    +# :doc:`/gallery/lines_bars_and_markers/fill_between_demo`).
    +
    +
    +def draw_error_band(ax, x, y, err, **kwargs):
    +    # Calculate normals via centered finite differences (except the first point
    +    # which uses a forward difference and the last point which uses a backward
    +    # difference).
    +    dx = np.concatenate([[x[1] - x[0]], x[2:] - x[:-2], [x[-1] - x[-2]]])
    +    dy = np.concatenate([[y[1] - y[0]], y[2:] - y[:-2], [y[-1] - y[-2]]])
    +    l = np.hypot(dx, dy)
    +    nx = dy / l
    +    ny = -dx / l
    +
    +    # end points of errors
    +    xp = x + nx * err
    +    yp = y + ny * err
    +    xn = x - nx * err
    +    yn = y - ny * err
    +
    +    vertices = np.block([[xp, xn[::-1]],
    +                         [yp, yn[::-1]]]).T
    +    codes = np.full(len(vertices), Path.LINETO)
    +    codes[0] = codes[len(xp)] = Path.MOVETO
    +    path = Path(vertices, codes)
    +    ax.add_patch(PathPatch(path, **kwargs))
    +
    +
    +_, axs = plt.subplots(1, 2, layout='constrained', sharex=True, sharey=True)
    +errs = [
    +    (axs[0], "constant error", 0.05),
    +    (axs[1], "variable error", 0.05 * np.sin(2 * t) ** 2 + 0.04),
    +]
    +for i, (ax, title, err) in enumerate(errs):
    +    ax.set(title=title, aspect=1, xticks=[], yticks=[])
    +    ax.plot(x, y, "k")
    +    draw_error_band(ax, x, y, err=err,
    +                    facecolor=f"C{i}", edgecolor="none", alpha=.3)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.path.Path`
    +#
    +# .. tags::
    +#
    +#    component: error
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/errorbar_limits_simple.py b/galleries/examples/lines_bars_and_markers/errorbar_limits_simple.py
    new file mode 100644
    index 000000000000..d9c8375c61fb
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/errorbar_limits_simple.py
    @@ -0,0 +1,68 @@
    +"""
    +========================
    +Errorbar limit selection
    +========================
    +
    +Illustration of selectively drawing lower and/or upper limit symbols on
    +errorbars using the parameters ``uplims``, ``lolims`` of `~.pyplot.errorbar`.
    +
    +Alternatively, you can use 2xN values to draw errorbars in only one direction.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +x = np.arange(10)
    +y = 2.5 * np.sin(x / 20 * np.pi)
    +yerr = np.linspace(0.05, 0.2, 10)
    +
    +plt.errorbar(x, y + 3, yerr=yerr, label='both limits (default)')
    +
    +plt.errorbar(x, y + 2, yerr=yerr, uplims=True, label='uplims=True')
    +
    +plt.errorbar(x, y + 1, yerr=yerr, uplims=True, lolims=True,
    +             label='uplims=True, lolims=True')
    +
    +upperlimits = [True, False] * 5
    +lowerlimits = [False, True] * 5
    +plt.errorbar(x, y, yerr=yerr, uplims=upperlimits, lolims=lowerlimits,
    +             label='subsets of uplims and lolims')
    +
    +plt.legend(loc='lower right')
    +
    +
    +# %%
    +# Similarly ``xuplims`` and ``xlolims`` can be used on the horizontal ``xerr``
    +# errorbars.
    +
    +fig = plt.figure()
    +x = np.arange(10) / 10
    +y = (x + 0.1)**2
    +
    +plt.errorbar(x, y, xerr=0.1, xlolims=True, label='xlolims=True')
    +y = (x + 0.1)**3
    +
    +plt.errorbar(x + 0.6, y, xerr=0.1, xuplims=upperlimits, xlolims=lowerlimits,
    +             label='subsets of xuplims and xlolims')
    +
    +y = (x + 0.1)**4
    +plt.errorbar(x + 1.2, y, xerr=0.1, xuplims=True, label='xuplims=True')
    +
    +plt.legend()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    +#
    +# .. tags::
    +#
    +#    component: error
    +#    plot-type: errorbar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/errorbar_subsample.py b/galleries/examples/lines_bars_and_markers/errorbar_subsample.py
    new file mode 100644
    index 000000000000..009286e28ea9
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/errorbar_subsample.py
    @@ -0,0 +1,47 @@
    +"""
    +====================
    +Errorbar subsampling
    +====================
    +
    +The parameter *errorevery* of `.Axes.errorbar` can be used to draw error bars
    +only on a subset of data points. This is particularly useful if there are many
    +data points with similar errors.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# example data
    +x = np.arange(0.1, 4, 0.1)
    +y1 = np.exp(-1.0 * x)
    +y2 = np.exp(-0.5 * x)
    +
    +# example variable error bar values
    +y1err = 0.1 + 0.1 * np.sqrt(x)
    +y2err = 0.1 + 0.1 * np.sqrt(x/2)
    +
    +
    +fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, sharex=True,
    +                                    figsize=(12, 6))
    +
    +ax0.set_title('all errorbars')
    +ax0.errorbar(x, y1, yerr=y1err)
    +ax0.errorbar(x, y2, yerr=y2err)
    +
    +ax1.set_title('only every 6th errorbar')
    +ax1.errorbar(x, y1, yerr=y1err, errorevery=6)
    +ax1.errorbar(x, y2, yerr=y2err, errorevery=6)
    +
    +ax2.set_title('second series shifted by 3')
    +ax2.errorbar(x, y1, yerr=y1err, errorevery=(0, 6))
    +ax2.errorbar(x, y2, yerr=y2err, errorevery=(3, 6))
    +
    +fig.suptitle('Errorbar subsampling')
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: error
    +#    plot-type: errorbar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/eventcollection_demo.py b/galleries/examples/lines_bars_and_markers/eventcollection_demo.py
    new file mode 100644
    index 000000000000..6854a13e0974
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/eventcollection_demo.py
    @@ -0,0 +1,68 @@
    +r"""
    +====================
    +EventCollection Demo
    +====================
    +
    +Plot two curves, then use `.EventCollection`\s to mark the locations of the x
    +and y data points on the respective Axes for each curve.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import EventCollection
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# create random data
    +xdata = np.random.random([2, 10])
    +
    +# split the data into two parts
    +xdata1 = xdata[0, :]
    +xdata2 = xdata[1, :]
    +
    +# sort the data so it makes clean curves
    +xdata1.sort()
    +xdata2.sort()
    +
    +# create some y data points
    +ydata1 = xdata1 ** 2
    +ydata2 = 1 - xdata2 ** 3
    +
    +# plot the data
    +fig = plt.figure()
    +ax = fig.add_subplot(1, 1, 1)
    +ax.plot(xdata1, ydata1, color='tab:blue')
    +ax.plot(xdata2, ydata2, color='tab:orange')
    +
    +# create the events marking the x data points
    +xevents1 = EventCollection(xdata1, color='tab:blue', linelength=0.05)
    +xevents2 = EventCollection(xdata2, color='tab:orange', linelength=0.05)
    +
    +# create the events marking the y data points
    +yevents1 = EventCollection(ydata1, color='tab:blue', linelength=0.05,
    +                           orientation='vertical')
    +yevents2 = EventCollection(ydata2, color='tab:orange', linelength=0.05,
    +                           orientation='vertical')
    +
    +# add the events to the axis
    +ax.add_collection(xevents1)
    +ax.add_collection(xevents2)
    +ax.add_collection(yevents1)
    +ax.add_collection(yevents2)
    +
    +# set the limits
    +ax.set_xlim(0, 1)
    +ax.set_ylim(0, 1)
    +
    +ax.set_title('line plot with data points')
    +
    +# display the plot
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: eventplot
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/eventplot_demo.py b/galleries/examples/lines_bars_and_markers/eventplot_demo.py
    new file mode 100644
    index 000000000000..17797c2f697a
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/eventplot_demo.py
    @@ -0,0 +1,69 @@
    +"""
    +==============
    +Eventplot demo
    +==============
    +
    +An `~.axes.Axes.eventplot` showing sequences of events with various line
    +properties. The plot is shown in both horizontal and vertical orientations.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib
    +
    +matplotlib.rcParams['font.size'] = 8.0
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +# create random data
    +data1 = np.random.random([6, 50])
    +
    +# set different colors for each set of positions
    +colors1 = [f'C{i}' for i in range(6)]
    +
    +# set different line properties for each set of positions
    +# note that some overlap
    +lineoffsets1 = [-15, -3, 1, 1.5, 6, 10]
    +linelengths1 = [5, 2, 1, 1, 3, 1.5]
    +
    +fig, axs = plt.subplots(2, 2)
    +
    +# create a horizontal plot
    +axs[0, 0].eventplot(data1, colors=colors1, lineoffsets=lineoffsets1,
    +                    linelengths=linelengths1)
    +
    +# create a vertical plot
    +axs[1, 0].eventplot(data1, colors=colors1, lineoffsets=lineoffsets1,
    +                    linelengths=linelengths1, orientation='vertical')
    +
    +# create another set of random data.
    +# the gamma distribution is only used for aesthetic purposes
    +data2 = np.random.gamma(4, size=[60, 50])
    +
    +# use individual values for the parameters this time
    +# these values will be used for all data sets (except lineoffsets2, which
    +# sets the increment between each data set in this usage)
    +colors2 = 'black'
    +lineoffsets2 = 1
    +linelengths2 = 1
    +
    +# create a horizontal plot
    +axs[0, 1].eventplot(data2, colors=colors2, lineoffsets=lineoffsets2,
    +                    linelengths=linelengths2)
    +
    +
    +# create a vertical plot
    +axs[1, 1].eventplot(data2, colors=colors2, lineoffsets=lineoffsets2,
    +                    linelengths=linelengths2, orientation='vertical')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: eventplot
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/lines_bars_and_markers/fill.py b/galleries/examples/lines_bars_and_markers/fill.py
    new file mode 100644
    index 000000000000..4eba083fa825
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/fill.py
    @@ -0,0 +1,93 @@
    +"""
    +==============
    +Filled polygon
    +==============
    +
    +`~.Axes.fill()` draws a filled polygon based on lists of point
    +coordinates *x*, *y*.
    +
    +This example uses the `Koch snowflake`_ as an example polygon.
    +
    +.. _Koch snowflake: https://en.wikipedia.org/wiki/Koch_snowflake
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def koch_snowflake(order, scale=10):
    +    """
    +    Return two lists x, y of point coordinates of the Koch snowflake.
    +
    +    Parameters
    +    ----------
    +    order : int
    +        The recursion depth.
    +    scale : float
    +        The extent of the snowflake (edge length of the base triangle).
    +    """
    +    def _koch_snowflake_complex(order):
    +        if order == 0:
    +            # initial triangle
    +            angles = np.array([0, 120, 240]) + 90
    +            return scale / np.sqrt(3) * np.exp(np.deg2rad(angles) * 1j)
    +        else:
    +            ZR = 0.5 - 0.5j * np.sqrt(3) / 3
    +
    +            p1 = _koch_snowflake_complex(order - 1)  # start points
    +            p2 = np.roll(p1, shift=-1)  # end points
    +            dp = p2 - p1  # connection vectors
    +
    +            new_points = np.empty(len(p1) * 4, dtype=np.complex128)
    +            new_points[::4] = p1
    +            new_points[1::4] = p1 + dp / 3
    +            new_points[2::4] = p1 + dp * ZR
    +            new_points[3::4] = p1 + dp / 3 * 2
    +            return new_points
    +
    +    points = _koch_snowflake_complex(order)
    +    x, y = points.real, points.imag
    +    return x, y
    +
    +
    +# %%
    +# Basic usage:
    +
    +x, y = koch_snowflake(order=5)
    +
    +plt.figure(figsize=(8, 8))
    +plt.axis('equal')
    +plt.fill(x, y)
    +plt.show()
    +
    +# %%
    +# Use keyword arguments *facecolor* and *edgecolor* to modify the colors
    +# of the polygon. Since the *linewidth* of the edge is 0 in the default
    +# Matplotlib style, we have to set it as well for the edge to become visible.
    +
    +x, y = koch_snowflake(order=2)
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(9, 3),
    +                                    subplot_kw={'aspect': 'equal'})
    +ax1.fill(x, y)
    +ax2.fill(x, y, facecolor='lightsalmon', edgecolor='orangered', linewidth=3)
    +ax3.fill(x, y, facecolor='none', edgecolor='purple', linewidth=3)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.fill` / `matplotlib.pyplot.fill`
    +#    - `matplotlib.axes.Axes.axis` / `matplotlib.pyplot.axis`
    +#
    +# .. tags::
    +#
    +#    styling: shape
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/lines_bars_and_markers/fill_between_alpha.py b/galleries/examples/lines_bars_and_markers/fill_between_alpha.py
    new file mode 100644
    index 000000000000..487ae9aaf62d
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/fill_between_alpha.py
    @@ -0,0 +1,147 @@
    +"""
    +==================================
    +``fill_between`` with transparency
    +==================================
    +
    +The `~matplotlib.axes.Axes.fill_between` function generates a shaded
    +region between a min and max boundary that is useful for illustrating ranges.
    +It has a very handy ``where`` argument to combine filling with logical ranges,
    +e.g., to just fill in a curve over some threshold value.
    +
    +At its most basic level, ``fill_between`` can be used to enhance a graph's
    +visual appearance. Let's compare two graphs of financial data with a simple
    +line plot on the left and a filled line on the right.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +
    +# load up some sample financial data
    +r = cbook.get_sample_data('goog.npz')['price_data']
    +# create two subplots with the shared x and y axes
    +fig, (ax1, ax2) = plt.subplots(1, 2, sharex=True, sharey=True)
    +
    +pricemin = r["close"].min()
    +
    +ax1.plot(r["date"], r["close"], lw=2)
    +ax2.fill_between(r["date"], pricemin, r["close"], alpha=0.7)
    +
    +for ax in ax1, ax2:
    +    ax.grid(True)
    +    ax.label_outer()
    +
    +ax1.set_ylabel('price')
    +
    +fig.suptitle('Google (GOOG) daily closing price')
    +fig.autofmt_xdate()
    +
    +# %%
    +# The alpha channel is not necessary here, but it can be used to soften
    +# colors for more visually appealing plots.  In other examples, as we'll
    +# see below, the alpha channel is functionally useful as the shaded
    +# regions can overlap and alpha allows you to see both.  Note that the
    +# postscript format does not support alpha (this is a postscript
    +# limitation, not a matplotlib limitation), so when using alpha save
    +# your figures in GIF, PNG, PDF or SVG.
    +#
    +# Our next example computes two populations of random walkers with a
    +# different mean and standard deviation of the normal distributions from
    +# which the steps are drawn.  We use filled regions to plot +/- one
    +# standard deviation of the mean position of the population.  Here the
    +# alpha channel is useful, not just aesthetic.
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +Nsteps, Nwalkers = 100, 250
    +t = np.arange(Nsteps)
    +
    +# an (Nsteps x Nwalkers) array of random walk steps
    +S1 = 0.004 + 0.02*np.random.randn(Nsteps, Nwalkers)
    +S2 = 0.002 + 0.01*np.random.randn(Nsteps, Nwalkers)
    +
    +# an (Nsteps x Nwalkers) array of random walker positions
    +X1 = S1.cumsum(axis=0)
    +X2 = S2.cumsum(axis=0)
    +
    +
    +# Nsteps length arrays empirical means and standard deviations of both
    +# populations over time
    +mu1 = X1.mean(axis=1)
    +sigma1 = X1.std(axis=1)
    +mu2 = X2.mean(axis=1)
    +sigma2 = X2.std(axis=1)
    +
    +# plot it!
    +fig, ax = plt.subplots(1)
    +ax.plot(t, mu1, lw=2, label='mean population 1')
    +ax.plot(t, mu2, lw=2, label='mean population 2')
    +ax.fill_between(t, mu1+sigma1, mu1-sigma1, facecolor='C0', alpha=0.4)
    +ax.fill_between(t, mu2+sigma2, mu2-sigma2, facecolor='C1', alpha=0.4)
    +ax.set_title(r'random walkers empirical $\mu$ and $\pm \sigma$ interval')
    +ax.legend(loc='upper left')
    +ax.set_xlabel('num steps')
    +ax.set_ylabel('position')
    +ax.grid()
    +
    +# %%
    +# The ``where`` keyword argument is very handy for highlighting certain
    +# regions of the graph.  ``where`` takes a boolean mask the same length
    +# as the x, ymin and ymax arguments, and only fills in the region where
    +# the boolean mask is True.  In the example below, we simulate a single
    +# random walker and compute the analytic mean and standard deviation of
    +# the population positions.  The population mean is shown as the dashed
    +# line, and the plus/minus one sigma deviation from the mean is shown
    +# as the filled region.  We use the where mask ``X > upper_bound`` to
    +# find the region where the walker is outside the one sigma boundary,
    +# and shade that region red.
    +
    +# Fixing random state for reproducibility
    +np.random.seed(1)
    +
    +Nsteps = 500
    +t = np.arange(Nsteps)
    +
    +mu = 0.002
    +sigma = 0.01
    +
    +# the steps and position
    +S = mu + sigma*np.random.randn(Nsteps)
    +X = S.cumsum()
    +
    +# the 1 sigma upper and lower analytic population bounds
    +lower_bound = mu*t - sigma*np.sqrt(t)
    +upper_bound = mu*t + sigma*np.sqrt(t)
    +
    +fig, ax = plt.subplots(1)
    +ax.plot(t, X, lw=2, label='walker position')
    +ax.plot(t, mu*t, lw=1, label='population mean', color='C0', ls='--')
    +ax.fill_between(t, lower_bound, upper_bound, facecolor='C0', alpha=0.4,
    +                label='1 sigma range')
    +ax.legend(loc='upper left')
    +
    +# here we use the where argument to only fill the region where the
    +# walker is above the population 1 sigma boundary
    +ax.fill_between(t, upper_bound, X, where=X > upper_bound, fc='red', alpha=0.4)
    +ax.fill_between(t, lower_bound, X, where=X < lower_bound, fc='red', alpha=0.4)
    +ax.set_xlabel('num steps')
    +ax.set_ylabel('position')
    +ax.grid()
    +
    +# %%
    +# Another handy use of filled regions is to highlight horizontal or vertical
    +# spans of an Axes -- for that Matplotlib has the helper functions
    +# `~matplotlib.axes.Axes.axhspan` and `~matplotlib.axes.Axes.axvspan`.  See
    +# :doc:`/gallery/subplots_axes_and_figures/axhspan_demo`.
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: alpha
    +#    plot-type: fill_between
    +#    level: intermediate
    +#    purpose: showcase
    diff --git a/galleries/examples/lines_bars_and_markers/fill_between_demo.py b/galleries/examples/lines_bars_and_markers/fill_between_demo.py
    new file mode 100644
    index 000000000000..feb325a3f9db
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/fill_between_demo.py
    @@ -0,0 +1,148 @@
    +"""
    +===============================
    +Fill the area between two lines
    +===============================
    +
    +This example shows how to use `~.axes.Axes.fill_between` to color the area
    +between two lines.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +#
    +# Basic usage
    +# -----------
    +# The parameters *y1* and *y2* can be scalars, indicating a horizontal
    +# boundary at the given y-values. If only *y1* is given, *y2* defaults to 0.
    +
    +x = np.arange(0.0, 2, 0.01)
    +y1 = np.sin(2 * np.pi * x)
    +y2 = 0.8 * np.sin(4 * np.pi * x)
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True, figsize=(6, 6))
    +
    +ax1.fill_between(x, y1)
    +ax1.set_title('fill between y1 and 0')
    +
    +ax2.fill_between(x, y1, 1)
    +ax2.set_title('fill between y1 and 1')
    +
    +ax3.fill_between(x, y1, y2)
    +ax3.set_title('fill between y1 and y2')
    +ax3.set_xlabel('x')
    +fig.tight_layout()
    +
    +# %%
    +#
    +# Example: Confidence bands
    +# -------------------------
    +# A common application for `~.axes.Axes.fill_between` is the indication of
    +# confidence bands.
    +#
    +# `~.axes.Axes.fill_between` uses the colors of the color cycle as the fill
    +# color. These may be a bit strong when applied to fill areas. It is
    +# therefore often a good practice to lighten the color by making the area
    +# semi-transparent using *alpha*.
    +
    +# sphinx_gallery_thumbnail_number = 2
    +
    +N = 21
    +x = np.linspace(0, 10, 11)
    +y = [3.9, 4.4, 10.8, 10.3, 11.2, 13.1, 14.1,  9.9, 13.9, 15.1, 12.5]
    +
    +# fit a linear curve and estimate its y-values and their error.
    +a, b = np.polyfit(x, y, deg=1)
    +y_est = a * x + b
    +y_err = x.std() * np.sqrt(1/len(x) +
    +                          (x - x.mean())**2 / np.sum((x - x.mean())**2))
    +
    +fig, ax = plt.subplots()
    +ax.plot(x, y_est, '-')
    +ax.fill_between(x, y_est - y_err, y_est + y_err, alpha=0.2)
    +ax.plot(x, y, 'o', color='tab:brown')
    +
    +# %%
    +#
    +# Selectively filling horizontal regions
    +# --------------------------------------
    +# The parameter *where* allows to specify the x-ranges to fill. It's a boolean
    +# array with the same size as *x*.
    +#
    +# Only x-ranges of contiguous *True* sequences are filled. As a result the
    +# range between neighboring *True* and *False* values is never filled. This
    +# often undesired when the data points should represent a contiguous quantity.
    +# It is therefore recommended to set ``interpolate=True`` unless the
    +# x-distance of the data points is fine enough so that the above effect is not
    +# noticeable. Interpolation approximates the actual x position at which the
    +# *where* condition will change and extends the filling up to there.
    +
    +x = np.array([0, 1, 2, 3])
    +y1 = np.array([0.8, 0.8, 0.2, 0.2])
    +y2 = np.array([0, 0, 1, 1])
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
    +
    +ax1.set_title('interpolation=False')
    +ax1.plot(x, y1, 'o--')
    +ax1.plot(x, y2, 'o--')
    +ax1.fill_between(x, y1, y2, where=(y1 > y2), color='C0', alpha=0.3)
    +ax1.fill_between(x, y1, y2, where=(y1 < y2), color='C1', alpha=0.3)
    +
    +ax2.set_title('interpolation=True')
    +ax2.plot(x, y1, 'o--')
    +ax2.plot(x, y2, 'o--')
    +ax2.fill_between(x, y1, y2, where=(y1 > y2), color='C0', alpha=0.3,
    +                 interpolate=True)
    +ax2.fill_between(x, y1, y2, where=(y1 <= y2), color='C1', alpha=0.3,
    +                 interpolate=True)
    +fig.tight_layout()
    +
    +# %%
    +#
    +# .. note::
    +#
    +#    Similar gaps will occur if *y1* or *y2* are masked arrays. Since missing
    +#    values cannot be approximated, *interpolate* has no effect in this case.
    +#    The gaps around masked values can only be reduced by adding more data
    +#    points close to the masked values.
    +
    +# %%
    +#
    +# Selectively marking horizontal regions across the whole Axes
    +# ------------------------------------------------------------
    +# The same selection mechanism can be applied to fill the full vertical height
    +# of the Axes. To be independent of y-limits, we add a transform that
    +# interprets the x-values in data coordinates and the y-values in Axes
    +# coordinates.
    +#
    +# The following example marks the regions in which the y-data are above a
    +# given threshold.
    +
    +fig, ax = plt.subplots()
    +x = np.arange(0, 4 * np.pi, 0.01)
    +y = np.sin(x)
    +ax.plot(x, y, color='black')
    +
    +threshold = 0.75
    +ax.axhline(threshold, color='green', lw=2, alpha=0.7)
    +ax.fill_between(x, 0, 1, where=y > threshold,
    +                color='green', alpha=0.5, transform=ax.get_xaxis_transform())
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.fill_between` / `matplotlib.pyplot.fill_between`
    +#    - `matplotlib.axes.Axes.get_xaxis_transform`
    +#
    +# .. tags::
    +#
    +#    styling: conditional
    +#    plot-type: fill_between
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/lines_bars_and_markers/fill_betweenx_demo.py b/galleries/examples/lines_bars_and_markers/fill_betweenx_demo.py
    new file mode 100644
    index 000000000000..ebd8d2a24a7b
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/fill_betweenx_demo.py
    @@ -0,0 +1,60 @@
    +"""
    +========================================
    +Fill the area between two vertical lines
    +========================================
    +
    +Using `~.Axes.fill_betweenx` to color along the horizontal direction between
    +two curves.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +y = np.arange(0.0, 2, 0.01)
    +x1 = np.sin(2 * np.pi * y)
    +x2 = 1.2 * np.sin(4 * np.pi * y)
    +
    +fig, [ax1, ax2, ax3] = plt.subplots(1, 3, sharey=True, figsize=(6, 6))
    +
    +ax1.fill_betweenx(y, 0, x1)
    +ax1.set_title('between (x1, 0)')
    +
    +ax2.fill_betweenx(y, x1, 1)
    +ax2.set_title('between (x1, 1)')
    +ax2.set_xlabel('x')
    +
    +ax3.fill_betweenx(y, x1, x2)
    +ax3.set_title('between (x1, x2)')
    +
    +# %%
    +# Now fill between x1 and x2 where a logical condition is met. Note this is
    +# different than calling::
    +#
    +#   fill_between(y[where], x1[where], x2[where])
    +#
    +# because of edge effects over multiple contiguous regions.
    +
    +fig, [ax, ax1] = plt.subplots(1, 2, sharey=True, figsize=(6, 6))
    +ax.plot(x1, y, x2, y, color='black')
    +ax.fill_betweenx(y, x1, x2, where=x2 >= x1, facecolor='green')
    +ax.fill_betweenx(y, x1, x2, where=x2 <= x1, facecolor='red')
    +ax.set_title('fill_betweenx where')
    +
    +# Test support for masked arrays.
    +x2 = np.ma.masked_greater(x2, 1.0)
    +ax1.plot(x1, y, x2, y, color='black')
    +ax1.fill_betweenx(y, x1, x2, where=x2 >= x1, facecolor='green')
    +ax1.fill_betweenx(y, x1, x2, where=x2 <= x1, facecolor='red')
    +ax1.set_title('regions with x2 > 1 are masked')
    +
    +# %%
    +# This example illustrates a problem; because of the data gridding, there are
    +# undesired unfilled triangles at the crossover points. A brute-force solution
    +# would be to interpolate all arrays to a very fine grid before plotting.
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: fill_between
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/gradient_bar.py b/galleries/examples/lines_bars_and_markers/gradient_bar.py
    new file mode 100644
    index 000000000000..66f497aec91c
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/gradient_bar.py
    @@ -0,0 +1,81 @@
    +"""
    +========================
    +Bar chart with gradients
    +========================
    +
    +Matplotlib does not natively support gradients. However, we can emulate a
    +gradient-filled rectangle by an `.AxesImage` of the right size and coloring.
    +
    +In particular, we use a colormap to generate the actual colors. It is then
    +sufficient to define the underlying values on the corners of the image and
    +let bicubic interpolation fill out the area. We define the gradient direction
    +by a unit vector *v*. The values at the corners are then obtained by the
    +lengths of the projections of the corner vectors on *v*.
    +
    +A similar approach can be used to create a gradient background for an Axes.
    +In that case, it is helpful to use Axes coordinates (``extent=(0, 1, 0, 1),
    +transform=ax.transAxes``) to be independent of the data coordinates.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +
    +def gradient_image(ax, direction=0.3, cmap_range=(0, 1), **kwargs):
    +    """
    +    Draw a gradient image based on a colormap.
    +
    +    Parameters
    +    ----------
    +    ax : Axes
    +        The Axes to draw on.
    +    direction : float
    +        The direction of the gradient. This is a number in
    +        range 0 (=vertical) to 1 (=horizontal).
    +    cmap_range : float, float
    +        The fraction (cmin, cmax) of the colormap that should be
    +        used for the gradient, where the complete colormap is (0, 1).
    +    **kwargs
    +        Other parameters are passed on to `.Axes.imshow()`.
    +        In particular, *cmap*, *extent*, and *transform* may be useful.
    +    """
    +    phi = direction * np.pi / 2
    +    v = np.array([np.cos(phi), np.sin(phi)])
    +    X = np.array([[v @ [1, 0], v @ [1, 1]],
    +                  [v @ [0, 0], v @ [0, 1]]])
    +    a, b = cmap_range
    +    X = a + (b - a) / X.max() * X
    +    im = ax.imshow(X, interpolation='bicubic', clim=(0, 1),
    +                   aspect='auto', **kwargs)
    +    return im
    +
    +
    +def gradient_bar(ax, x, y, width=0.5, bottom=0):
    +    for left, top in zip(x, y):
    +        right = left + width
    +        gradient_image(ax, extent=(left, right, bottom, top),
    +                       cmap="Blues_r", cmap_range=(0, 0.8))
    +
    +
    +fig, ax = plt.subplots()
    +ax.set(xlim=(0, 10), ylim=(0, 1))
    +
    +# background image
    +gradient_image(ax, direction=1, extent=(0, 1, 0, 1), transform=ax.transAxes,
    +               cmap="RdYlGn", cmap_range=(0.2, 0.8), alpha=0.5)
    +
    +N = 10
    +x = np.arange(N) + 0.15
    +y = np.random.rand(N)
    +gradient_bar(ax, x, y, width=0.7)
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: color
    +#    plot-type: imshow
    +#    level: intermediate
    +#    purpose: showcase
    diff --git a/galleries/examples/lines_bars_and_markers/hat_graph.py b/galleries/examples/lines_bars_and_markers/hat_graph.py
    new file mode 100644
    index 000000000000..25e5f0b1ead3
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/hat_graph.py
    @@ -0,0 +1,78 @@
    +"""
    +=========
    +Hat graph
    +=========
    +This example shows how to create a `hat graph`_ and how to annotate it with
    +labels.
    +
    +.. _hat graph: https://doi.org/10.1186/s41235-019-0182-3
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def hat_graph(ax, xlabels, values, group_labels):
    +    """
    +    Create a hat graph.
    +
    +    Parameters
    +    ----------
    +    ax : matplotlib.axes.Axes
    +        The Axes to plot into.
    +    xlabels : list of str
    +        The category names to be displayed on the x-axis.
    +    values : (M, N) array-like
    +        The data values.
    +        Rows are the groups (len(group_labels) == M).
    +        Columns are the categories (len(xlabels) == N).
    +    group_labels : list of str
    +        The group labels displayed in the legend.
    +    """
    +
    +    values = np.asarray(values)
    +    color_cycle_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
    +
    +    # Draw the hats
    +    bars = ax.grouped_bar(
    +        (values - values[0]).T, bottom=values[0], tick_labels=xlabels,
    +        labels=group_labels, edgecolor='black', group_spacing=0.8,
    +        colors=['none'] + color_cycle_colors)
    +
    +    # Attach a text label on top of each bar
    +    for bc, heights in zip(bars.bar_containers, values):
    +        ax.bar_label(bc, heights, padding=4)
    +
    +
    +# Initialise labels and a numpy array make sure you have
    +# N labels of N number of values in the array
    +xlabels = ['I', 'II', 'III', 'IV', 'V']
    +playerA = np.array([5, 15, 22, 20, 25])
    +playerB = np.array([25, 32, 34, 30, 27])
    +
    +fig, ax = plt.subplots(layout='constrained')
    +
    +hat_graph(ax, xlabels, [playerA, playerB], ['Player A', 'Player B'])
    +
    +# Add some text for labels, title and custom x-axis tick labels, etc.
    +ax.set_xlabel('Games')
    +ax.set_ylabel('Score')
    +ax.set_ylim(0, 60)
    +ax.set_title('Scores by number of game and players')
    +ax.legend()
    +
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.grouped_bar` / `matplotlib.pyplot.grouped_bar`
    +#    - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label`
    +#
    +# .. tags::
    +#
    +#    component: annotation
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/horizontal_barchart_distribution.py b/galleries/examples/lines_bars_and_markers/horizontal_barchart_distribution.py
    new file mode 100644
    index 000000000000..3f7e499f38e7
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/horizontal_barchart_distribution.py
    @@ -0,0 +1,87 @@
    +"""
    +=============================================
    +Discrete distribution as horizontal bar chart
    +=============================================
    +
    +Stacked bar charts can be used to visualize discrete distributions.
    +
    +This example visualizes the result of a survey in which people could rate
    +their agreement to questions on a five-element scale.
    +
    +The horizontal stacking is achieved by calling `~.Axes.barh()` for each
    +category and passing the starting point as the cumulative sum of the
    +already drawn bars via the parameter ``left``.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +category_names = ['Strongly disagree', 'Disagree',
    +                  'Neither agree nor disagree', 'Agree', 'Strongly agree']
    +results = {
    +    'Question 1': [10, 15, 17, 32, 26],
    +    'Question 2': [26, 22, 29, 10, 13],
    +    'Question 3': [35, 37, 7, 2, 19],
    +    'Question 4': [32, 11, 9, 15, 33],
    +    'Question 5': [21, 29, 5, 5, 40],
    +    'Question 6': [8, 19, 5, 30, 38]
    +}
    +
    +
    +def survey(results, category_names):
    +    """
    +    Parameters
    +    ----------
    +    results : dict
    +        A mapping from question labels to a list of answers per category.
    +        It is assumed all lists contain the same number of entries and that
    +        it matches the length of *category_names*.
    +    category_names : list of str
    +        The category labels.
    +    """
    +    labels = list(results.keys())
    +    data = np.array(list(results.values()))
    +    data_cum = data.cumsum(axis=1)
    +    category_colors = plt.colormaps['RdYlGn'](
    +        np.linspace(0.15, 0.85, data.shape[1]))
    +
    +    fig, ax = plt.subplots(figsize=(9.2, 5))
    +    ax.invert_yaxis()
    +    ax.xaxis.set_visible(False)
    +    ax.set_xlim(0, np.sum(data, axis=1).max())
    +
    +    for i, (colname, color) in enumerate(zip(category_names, category_colors)):
    +        widths = data[:, i]
    +        starts = data_cum[:, i] - widths
    +        rects = ax.barh(labels, widths, left=starts, height=0.5,
    +                        label=colname, color=color)
    +
    +        r, g, b, _ = color
    +        text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
    +        ax.bar_label(rects, label_type='center', color=text_color)
    +    ax.legend(ncols=len(category_names), bbox_to_anchor=(0, 1),
    +              loc='lower left', fontsize='small')
    +
    +    return fig, ax
    +
    +
    +survey(results, category_names)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh`
    +#    - `matplotlib.axes.Axes.bar_label` / `matplotlib.pyplot.bar_label`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#
    +# .. tags::
    +#
    +#    domain: statistics
    +#    component: label
    +#    plot-type: bar
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/joinstyle.py b/galleries/examples/lines_bars_and_markers/joinstyle.py
    new file mode 100644
    index 000000000000..09ae03e07692
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/joinstyle.py
    @@ -0,0 +1,19 @@
    +"""
    +=========
    +JoinStyle
    +=========
    +
    +The `matplotlib._enums.JoinStyle` controls how Matplotlib draws the corners
    +where two different line segments meet. For more details, see the
    +`~matplotlib._enums.JoinStyle` docs.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib._enums import JoinStyle
    +
    +JoinStyle.demo()
    +plt.show()
    +
    +# %%
    +# .. tags:: purpose: reference, styling: linestyle
    diff --git a/galleries/examples/lines_bars_and_markers/line_demo_dash_control.py b/galleries/examples/lines_bars_and_markers/line_demo_dash_control.py
    new file mode 100644
    index 000000000000..3b12cf0aa5b7
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/line_demo_dash_control.py
    @@ -0,0 +1,56 @@
    +"""
    +===============================
    +Dashed line style configuration
    +===============================
    +
    +The dashing of a line is controlled via a dash sequence. It can be modified
    +using `.Line2D.set_dashes`.
    +
    +The dash sequence is a series of on/off lengths in points, e.g.
    +``[3, 1]`` would be 3pt long lines separated by 1pt spaces.
    +
    +Some functions like `.Axes.plot` support passing Line properties as keyword
    +arguments. In such a case, you can already set the dashing when creating the
    +line.
    +
    +*Note*: The dash style can also be configured via a
    +:ref:`property_cycle `
    +by passing a list of dash sequences using the keyword *dashes* to the
    +cycler. This is not shown within this example.
    +
    +Other attributes of the dash may also be set either with the relevant method
    +(`~.Line2D.set_dash_capstyle`, `~.Line2D.set_dash_joinstyle`,
    +`~.Line2D.set_gapcolor`) or by passing the property through a plotting
    +function.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.linspace(0, 10, 500)
    +y = np.sin(x)
    +
    +plt.rc('lines', linewidth=2.5)
    +fig, ax = plt.subplots()
    +
    +# Using set_dashes() and set_capstyle() to modify dashing of an existing line.
    +line1, = ax.plot(x, y, label='Using set_dashes() and set_dash_capstyle()')
    +line1.set_dashes([2, 2, 10, 2])  # 2pt line, 2pt break, 10pt line, 2pt break.
    +line1.set_dash_capstyle('round')
    +
    +# Using plot(..., dashes=...) to set the dashing when creating a line.
    +line2, = ax.plot(x, y - 0.2, dashes=[6, 2], label='Using the dashes parameter')
    +
    +# Using plot(..., dashes=..., gapcolor=...) to set the dashing and
    +# alternating color when creating a line.
    +line3, = ax.plot(x, y - 0.4, dashes=[4, 4], gapcolor='tab:pink',
    +                 label='Using the dashes and gapcolor parameters')
    +
    +ax.legend(handlelength=4)
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: linestyle
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/lines_with_ticks_demo.py b/galleries/examples/lines_bars_and_markers/lines_with_ticks_demo.py
    new file mode 100644
    index 000000000000..fba7eb9f045e
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/lines_with_ticks_demo.py
    @@ -0,0 +1,39 @@
    +"""
    +==============================
    +Lines with a ticked patheffect
    +==============================
    +
    +Ticks can be added along a line to mark one side as a barrier using
    +`~matplotlib.patheffects.TickedStroke`.  You can control the angle,
    +spacing, and length of the ticks.
    +
    +The ticks will also appear appropriately in the legend.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import patheffects
    +
    +# Plot a straight diagonal line with ticked style path
    +fig, ax = plt.subplots(figsize=(6, 6))
    +ax.plot([0, 1], [0, 1], label="Line",
    +        path_effects=[patheffects.withTickedStroke(spacing=7, angle=135)])
    +
    +# Plot a curved line with ticked style path
    +nx = 101
    +x = np.linspace(0.0, 1.0, nx)
    +y = 0.3*np.sin(x*8) + 0.4
    +ax.plot(x, y, label="Curve", path_effects=[patheffects.withTickedStroke()])
    +
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: linestyle
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/linestyles.py b/galleries/examples/lines_bars_and_markers/linestyles.py
    new file mode 100644
    index 000000000000..25b053e912bd
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/linestyles.py
    @@ -0,0 +1,87 @@
    +"""
    +==========
    +Linestyles
    +==========
    +
    +Simple linestyles can be defined using the strings "solid", "dotted", "dashed"
    +or "dashdot". More refined control can be achieved by providing a dash tuple
    +``(offset, (on_off_seq))``. For example, ``(0, (3, 10, 1, 15))`` means
    +(3pt line, 10pt space, 1pt line, 15pt space) with no offset, while
    +``(5, (10, 3))``, means (10pt line, 3pt space), but skip the first 5pt line.
    +See also `.Line2D.set_linestyle`.  The specific on/off sequences of the
    +"dotted", "dashed" and "dashdot" styles are configurable:
    +
    +* :rc:`lines.dotted_pattern`
    +* :rc:`lines.dashed_pattern`
    +* :rc:`lines.dashdot_pattern`
    +
    +*Note*: The dash style can also be configured via `.Line2D.set_dashes`
    +as shown in :doc:`/gallery/lines_bars_and_markers/line_demo_dash_control`
    +and passing a list of dash sequences using the keyword *dashes* to the
    +cycler in :ref:`property_cycle `.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +linestyle_str = [
    +     ('solid', 'solid'),      # Same as (0, ()) or '-'
    +     ('dotted', 'dotted'),    # Same as ':'
    +     ('dashed', 'dashed'),    # Same as '--'
    +     ('dashdot', 'dashdot')]  # Same as '-.'
    +
    +linestyle_tuple = [
    +     ('loosely dotted',        (0, (1, 10))),
    +     ('dotted',                (0, (1, 5))),
    +     ('densely dotted',        (0, (1, 1))),
    +
    +     ('long dash with offset', (5, (10, 3))),
    +     ('loosely dashed',        (0, (5, 10))),
    +     ('dashed',                (0, (5, 5))),
    +     ('densely dashed',        (0, (5, 1))),
    +
    +     ('loosely dashdotted',    (0, (3, 10, 1, 10))),
    +     ('dashdotted',            (0, (3, 5, 1, 5))),
    +     ('densely dashdotted',    (0, (3, 1, 1, 1))),
    +
    +     ('dashdotdotted',         (0, (3, 5, 1, 5, 1, 5))),
    +     ('loosely dashdotdotted', (0, (3, 10, 1, 10, 1, 10))),
    +     ('densely dashdotdotted', (0, (3, 1, 1, 1, 1, 1)))]
    +
    +
    +def plot_linestyles(ax, linestyles, title):
    +    X, Y = np.linspace(0, 100, 10), np.zeros(10)
    +    yticklabels = []
    +
    +    for i, (name, linestyle) in enumerate(linestyles):
    +        ax.plot(X, Y+i, linestyle=linestyle, linewidth=1.5, color='black')
    +        yticklabels.append(name)
    +
    +    ax.set_title(title)
    +    ax.set(ylim=(-0.5, len(linestyles)-0.5),
    +           yticks=np.arange(len(linestyles)),
    +           yticklabels=yticklabels)
    +    ax.tick_params(left=False, bottom=False, labelbottom=False)
    +    ax.spines[:].set_visible(False)
    +
    +    # For each line style, add a text annotation with a small offset from
    +    # the reference point (0 in Axes coords, y tick value in Data coords).
    +    for i, (name, linestyle) in enumerate(linestyles):
    +        ax.annotate(repr(linestyle),
    +                    xy=(0.0, i), xycoords=ax.get_yaxis_transform(),
    +                    xytext=(-6, -12), textcoords='offset points',
    +                    color="blue", fontsize=8, ha="right", family="monospace")
    +
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1, figsize=(7, 8), height_ratios=[1, 3],
    +                               layout='constrained')
    +
    +plot_linestyles(ax0, linestyle_str[::-1], title='Named linestyles')
    +plot_linestyles(ax1, linestyle_tuple[::-1], title='Parametrized linestyles')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: linestyle
    +#    purpose: reference
    diff --git a/galleries/examples/lines_bars_and_markers/marker_reference.py b/galleries/examples/lines_bars_and_markers/marker_reference.py
    new file mode 100644
    index 000000000000..32b6291cbc76
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/marker_reference.py
    @@ -0,0 +1,248 @@
    +"""
    +================
    +Marker reference
    +================
    +
    +Matplotlib supports multiple categories of markers which are selected using
    +the ``marker`` parameter of plot commands:
    +
    +- `Unfilled markers`_
    +- `Filled markers`_
    +- `Markers created from TeX symbols`_
    +- `Markers created from Paths`_
    +
    +For a list of all markers see also the `matplotlib.markers` documentation.
    +
    +For example usages see
    +:doc:`/gallery/lines_bars_and_markers/scatter_star_poly`.
    +
    +.. redirect-from:: /gallery/shapes_and_collections/marker_path
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.lines import Line2D
    +from matplotlib.markers import MarkerStyle
    +from matplotlib.transforms import Affine2D
    +
    +text_style = dict(horizontalalignment='right', verticalalignment='center',
    +                  fontsize=12, fontfamily='monospace')
    +marker_style = dict(linestyle=':', color='0.8', markersize=10,
    +                    markerfacecolor="tab:blue", markeredgecolor="tab:blue")
    +
    +
    +def format_axes(ax):
    +    ax.margins(0.2)
    +    ax.set_axis_off()
    +    ax.invert_yaxis()
    +
    +
    +def split_list(a_list):
    +    i_half = len(a_list) // 2
    +    return a_list[:i_half], a_list[i_half:]
    +
    +
    +# %%
    +# Unfilled markers
    +# ================
    +# Unfilled markers are single-colored.
    +
    +fig, axs = plt.subplots(ncols=2)
    +fig.suptitle('Un-filled markers', fontsize=14)
    +
    +# Filter out filled markers and marker settings that do nothing.
    +unfilled_markers = [m for m, func in Line2D.markers.items()
    +                    if func != 'nothing' and m not in Line2D.filled_markers]
    +
    +for ax, markers in zip(axs, split_list(unfilled_markers)):
    +    for y, marker in enumerate(markers):
    +        ax.text(-0.5, y, repr(marker), **text_style)
    +        ax.plot([y] * 3, marker=marker, **marker_style)
    +    format_axes(ax)
    +
    +# %%
    +# Filled markers
    +# ==============
    +
    +fig, axs = plt.subplots(ncols=2)
    +fig.suptitle('Filled markers', fontsize=14)
    +for ax, markers in zip(axs, split_list(Line2D.filled_markers)):
    +    for y, marker in enumerate(markers):
    +        ax.text(-0.5, y, repr(marker), **text_style)
    +        ax.plot([y] * 3, marker=marker, **marker_style)
    +    format_axes(ax)
    +
    +# %%
    +# .. _marker_fill_styles:
    +#
    +# Marker fill styles
    +# ------------------
    +# The edge color and fill color of filled markers can be specified separately.
    +# Additionally, the ``fillstyle`` can be configured to be unfilled, fully
    +# filled, or half-filled in various directions. The half-filled styles use
    +# ``markerfacecoloralt`` as secondary fill color.
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Marker fillstyle', fontsize=14)
    +fig.subplots_adjust(left=0.4)
    +
    +filled_marker_style = dict(marker='o', linestyle=':', markersize=15,
    +                           color='darkgrey',
    +                           markerfacecolor='tab:blue',
    +                           markerfacecoloralt='lightsteelblue',
    +                           markeredgecolor='brown')
    +
    +for y, fill_style in enumerate(Line2D.fillStyles):
    +    ax.text(-0.5, y, repr(fill_style), **text_style)
    +    ax.plot([y] * 3, fillstyle=fill_style, **filled_marker_style)
    +format_axes(ax)
    +
    +# %%
    +# Markers created from TeX symbols
    +# ================================
    +#
    +# Use :ref:`MathText `, to use custom marker symbols,
    +# like e.g. ``"$\u266B$"``. For an overview over the STIX font symbols refer
    +# to the `STIX font table `_.
    +# Also see the :doc:`/gallery/text_labels_and_annotations/stix_fonts_demo`.
    +
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Mathtext markers', fontsize=14)
    +fig.subplots_adjust(left=0.4)
    +
    +marker_style.update(markeredgecolor="none", markersize=15)
    +markers = ["$1$", r"$\frac{1}{2}$", "$f$", "$\u266B$", r"$\mathcal{A}$"]
    +
    +for y, marker in enumerate(markers):
    +    # Escape dollars so that the text is written "as is", not as mathtext.
    +    ax.text(-0.5, y, repr(marker).replace("$", r"\$"), **text_style)
    +    ax.plot([y] * 3, marker=marker, **marker_style)
    +format_axes(ax)
    +
    +# %%
    +# Markers created from Paths
    +# ==========================
    +#
    +# Any `~.path.Path` can be used as a marker. The following example shows two
    +# simple paths *star* and *circle*, and a more elaborate path of a circle with
    +# a cut-out star.
    +
    +import numpy as np
    +
    +import matplotlib.path as mpath
    +
    +star = mpath.Path.unit_regular_star(6)
    +circle = mpath.Path.unit_circle()
    +# concatenate the circle with an internal cutout of the star
    +cut_star = mpath.Path(
    +    vertices=np.concatenate([circle.vertices, star.vertices[::-1, ...]]),
    +    codes=np.concatenate([circle.codes, star.codes]))
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Path markers', fontsize=14)
    +fig.subplots_adjust(left=0.4)
    +
    +markers = {'star': star, 'circle': circle, 'cut_star': cut_star}
    +
    +for y, (name, marker) in enumerate(markers.items()):
    +    ax.text(-0.5, y, name, **text_style)
    +    ax.plot([y] * 3, marker=marker, **marker_style)
    +format_axes(ax)
    +
    +# %%
    +# Advanced marker modifications with transform
    +# ============================================
    +#
    +# Markers can be modified by passing a transform to the MarkerStyle
    +# constructor. Following example shows how a supplied rotation is applied to
    +# several marker shapes.
    +
    +common_style = {k: v for k, v in filled_marker_style.items() if k != 'marker'}
    +angles = [0, 10, 20, 30, 45, 60, 90]
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Rotated markers', fontsize=14)
    +
    +ax.text(-0.5, 0, 'Filled marker', **text_style)
    +for x, theta in enumerate(angles):
    +    t = Affine2D().rotate_deg(theta)
    +    ax.plot(x, 0, marker=MarkerStyle('o', 'left', t), **common_style)
    +
    +ax.text(-0.5, 1, 'Un-filled marker', **text_style)
    +for x, theta in enumerate(angles):
    +    t = Affine2D().rotate_deg(theta)
    +    ax.plot(x, 1, marker=MarkerStyle('1', 'left', t), **common_style)
    +
    +ax.text(-0.5, 2, 'Equation marker', **text_style)
    +for x, theta in enumerate(angles):
    +    t = Affine2D().rotate_deg(theta)
    +    eq = r'$\frac{1}{x}$'
    +    ax.plot(x, 2, marker=MarkerStyle(eq, 'left', t), **common_style)
    +
    +for x, theta in enumerate(angles):
    +    ax.text(x, 2.5, f"{theta}°", horizontalalignment="center")
    +format_axes(ax)
    +
    +fig.tight_layout()
    +
    +# %%
    +# Setting marker cap style and join style
    +# =======================================
    +#
    +# Markers have default cap and join styles, but these can be
    +# customized when creating a MarkerStyle.
    +
    +from matplotlib.markers import CapStyle, JoinStyle
    +
    +marker_inner = dict(markersize=35,
    +                    markerfacecolor='tab:blue',
    +                    markerfacecoloralt='lightsteelblue',
    +                    markeredgecolor='brown',
    +                    markeredgewidth=8,
    +                    )
    +
    +marker_outer = dict(markersize=35,
    +                    markerfacecolor='tab:blue',
    +                    markerfacecoloralt='lightsteelblue',
    +                    markeredgecolor='white',
    +                    markeredgewidth=1,
    +                    )
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Marker CapStyle', fontsize=14)
    +fig.subplots_adjust(left=0.1)
    +
    +for y, cap_style in enumerate(CapStyle):
    +    ax.text(-0.5, y, cap_style.name, **text_style)
    +    for x, theta in enumerate(angles):
    +        t = Affine2D().rotate_deg(theta)
    +        m = MarkerStyle('1', transform=t, capstyle=cap_style)
    +        ax.plot(x, y, marker=m, **marker_inner)
    +        ax.plot(x, y, marker=m, **marker_outer)
    +        ax.text(x, len(CapStyle) - .5, f'{theta}°', ha='center')
    +format_axes(ax)
    +
    +# %%
    +# Modifying the join style:
    +
    +fig, ax = plt.subplots()
    +fig.suptitle('Marker JoinStyle', fontsize=14)
    +fig.subplots_adjust(left=0.05)
    +
    +for y, join_style in enumerate(JoinStyle):
    +    ax.text(-0.5, y, join_style.name, **text_style)
    +    for x, theta in enumerate(angles):
    +        t = Affine2D().rotate_deg(theta)
    +        m = MarkerStyle('*', transform=t, joinstyle=join_style)
    +        ax.plot(x, y, marker=m, **marker_inner)
    +        ax.text(x, len(JoinStyle) - .5, f'{theta}°', ha='center')
    +format_axes(ax)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    purpose: reference
    diff --git a/galleries/examples/lines_bars_and_markers/markevery_demo.py b/galleries/examples/lines_bars_and_markers/markevery_demo.py
    new file mode 100644
    index 000000000000..da4da0ecf9f1
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/markevery_demo.py
    @@ -0,0 +1,105 @@
    +"""
    +==============
    +Markevery Demo
    +==============
    +
    +The ``markevery`` property of `.Line2D` allows drawing markers at a subset of
    +data points.
    +
    +The list of possible parameters is specified at `.Line2D.set_markevery`.
    +In short:
    +
    +- A single integer N draws every N-th marker.
    +- A tuple of integers (start, N) draws every N-th marker, starting at data
    +  index *start*.
    +- A list of integers draws the markers at the specified indices.
    +- A slice draws the markers at the sliced indices.
    +- A float specifies the distance between markers as a fraction of the Axes
    +  diagonal in screen space. This will lead to a visually uniform distribution
    +  of the points along the line, irrespective of scales and zooming.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# define a list of markevery cases to plot
    +cases = [
    +    None,
    +    8,
    +    (30, 8),
    +    [16, 24, 32],
    +    [0, -1],
    +    slice(100, 200, 3),
    +    0.1,
    +    0.4,
    +    (0.2, 0.4)
    +]
    +
    +# data points
    +delta = 0.11
    +x = np.linspace(0, 10 - 2 * delta, 200) + delta
    +y = np.sin(x) + 1.0 + delta
    +
    +# %%
    +# markevery with linear scales
    +# ----------------------------
    +
    +fig, axs = plt.subplots(3, 3, figsize=(10, 6), layout='constrained')
    +for ax, markevery in zip(axs.flat, cases):
    +    ax.set_title(f'markevery={markevery}')
    +    ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery)
    +
    +# %%
    +# markevery with log scales
    +# -------------------------
    +#
    +# Note that the log scale causes a visual asymmetry in the marker distance for
    +# when subsampling the data using an integer. In contrast, subsampling on
    +# fraction of figure size creates even distributions, because it's based on
    +# fractions of the Axes diagonal, not on data coordinates or data indices.
    +
    +fig, axs = plt.subplots(3, 3, figsize=(10, 6), layout='constrained')
    +for ax, markevery in zip(axs.flat, cases):
    +    ax.set_title(f'markevery={markevery}')
    +    ax.set_xscale('log')
    +    ax.set_yscale('log')
    +    ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery)
    +
    +# %%
    +# markevery on zoomed plots
    +# -------------------------
    +#
    +# Integer-based *markevery* specifications select points from the underlying
    +# data and are independent on the view. In contrast, float-based specifications
    +# are related to the Axes diagonal. While zooming does not change the Axes
    +# diagonal, it changes the displayed data range, and more points will be
    +# displayed when zooming.
    +
    +fig, axs = plt.subplots(3, 3, figsize=(10, 6), layout='constrained')
    +for ax, markevery in zip(axs.flat, cases):
    +    ax.set_title(f'markevery={markevery}')
    +    ax.plot(x, y, 'o', ls='-', ms=4, markevery=markevery)
    +    ax.set_xlim(6, 6.7)
    +    ax.set_ylim(1.1, 1.7)
    +
    +# %%
    +# markevery on polar plots
    +# ------------------------
    +
    +r = np.linspace(0, 3.0, 200)
    +theta = 2 * np.pi * r
    +
    +fig, axs = plt.subplots(3, 3, figsize=(10, 6), layout='constrained',
    +                        subplot_kw={'projection': 'polar'})
    +for ax, markevery in zip(axs.flat, cases):
    +    ax.set_title(f'markevery={markevery}')
    +    ax.plot(theta, r, 'o', ls='-', ms=4, markevery=markevery)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/masked_demo.py b/galleries/examples/lines_bars_and_markers/masked_demo.py
    new file mode 100644
    index 000000000000..842c5c022f0b
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/masked_demo.py
    @@ -0,0 +1,56 @@
    +"""
    +==============================
    +Plotting masked and NaN values
    +==============================
    +
    +Sometimes you need to plot data with missing values.
    +
    +One possibility is to simply remove undesired data points. The line plotted
    +through the remaining data will be continuous, and not indicate where the
    +missing data is located.
    +
    +If it is useful to have gaps in the line where the data is missing, then the
    +undesired points can be indicated using a `masked array`_ or by setting their
    +values to NaN. No marker will be drawn where either x or y are masked and, if
    +plotting with a line, it will be broken there.
    +
    +.. _masked array:
    +   https://numpy.org/doc/stable/reference/maskedarray.generic.html
    +
    +The following example illustrates the three cases:
    +
    +1) Removing points.
    +2) Masking points.
    +3) Setting to NaN.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.linspace(-np.pi/2, np.pi/2, 31)
    +y = np.cos(x)**3
    +
    +# 1) remove points where y > 0.7
    +x2 = x[y <= 0.7]
    +y2 = y[y <= 0.7]
    +
    +# 2) mask points where y > 0.7
    +y3 = np.ma.masked_where(y > 0.7, y)
    +
    +# 3) set to NaN where y > 0.7
    +y4 = y.copy()
    +y4[y3 > 0.7] = np.nan
    +
    +plt.plot(x*0.1, y, 'o-', color='lightgrey', label='No mask')
    +plt.plot(x2*0.4, y2, 'o-', label='Points removed')
    +plt.plot(x*0.7, y3, 'o-', label='Masked values')
    +plt.plot(x*1.0, y4, 'o-', label='NaN values')
    +plt.legend()
    +plt.title('Masked and NaN data')
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/multicolored_line.py b/galleries/examples/lines_bars_and_markers/multicolored_line.py
    new file mode 100644
    index 000000000000..a643b2de160c
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/multicolored_line.py
    @@ -0,0 +1,188 @@
    +"""
    +==================
    +Multicolored lines
    +==================
    +
    +The example shows two ways to plot a line with the a varying color defined by
    +a third value. The first example defines the color at each (x, y) point.
    +The second example defines the color between pairs of points, so the length
    +of the color value list is one less than the length of the x and y lists.
    +
    +Color values at points
    +----------------------
    +
    +"""
    +
    +import warnings
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import LineCollection
    +
    +
    +def colored_line(x, y, c, ax=None, **lc_kwargs):
    +    """
    +    Plot a line with a color specified along the line by a third value.
    +
    +    It does this by creating a collection of line segments. Each line segment is
    +    made up of two straight lines each connecting the current (x, y) point to the
    +    midpoints of the lines connecting the current point with its two neighbors.
    +    This creates a smooth line with no gaps between the line segments.
    +
    +    Parameters
    +    ----------
    +    x, y : array-like
    +        The horizontal and vertical coordinates of the data points.
    +    c : array-like
    +        The color values, which should be the same size as x and y.
    +    ax : matplotlib.axes.Axes, optional
    +        The axes to plot on. If not provided, the current axes will be used.
    +    **lc_kwargs
    +        Any additional arguments to pass to matplotlib.collections.LineCollection
    +        constructor. This should not include the array keyword argument because
    +        that is set to the color argument. If provided, it will be overridden.
    +
    +    Returns
    +    -------
    +    matplotlib.collections.LineCollection
    +        The generated line collection representing the colored line.
    +    """
    +    if "array" in lc_kwargs:
    +        warnings.warn(
    +            'The provided "array" keyword argument will be overridden',
    +            UserWarning,
    +            stacklevel=2,
    +        )
    +
    +    xy = np.stack((x, y), axis=-1)
    +    xy_mid = np.concat(
    +        (xy[0, :][None, :], (xy[:-1, :] + xy[1:, :]) / 2, xy[-1, :][None, :]), axis=0
    +    )
    +    segments = np.stack((xy_mid[:-1, :], xy, xy_mid[1:, :]), axis=-2)
    +    # Note that
    +    # segments[0, :, :] is [xy[0, :], xy[0, :], (xy[0, :] + xy[1, :]) / 2]
    +    # segments[i, :, :] is [(xy[i - 1, :] + xy[i, :]) / 2, xy[i, :],
    +    #     (xy[i, :] + xy[i + 1, :]) / 2] if i not in {0, len(x) - 1}
    +    # segments[-1, :, :] is [(xy[-2, :] + xy[-1, :]) / 2, xy[-1, :], xy[-1, :]]
    +
    +    lc_kwargs["array"] = c
    +    lc = LineCollection(segments, **lc_kwargs)
    +
    +    # Plot the line collection to the axes
    +    ax = ax or plt.gca()
    +    ax.add_collection(lc)
    +
    +    return lc
    +
    +
    +# -------------- Create and show plot --------------
    +# Some arbitrary function that gives x, y, and color values
    +t = np.linspace(-7.4, -0.5, 200)
    +x = 0.9 * np.sin(t)
    +y = 0.9 * np.cos(1.6 * t)
    +color = np.linspace(0, 2, t.size)
    +
    +# Create a figure and plot the line on it
    +fig1, ax1 = plt.subplots()
    +lines = colored_line(x, y, color, ax1, linewidth=10, cmap="plasma")
    +fig1.colorbar(lines)  # add a color legend
    +
    +ax1.set_title("Color at each point")
    +
    +plt.show()
    +
    +####################################################################
    +# This method is designed to give a smooth impression when distances and color
    +# differences between adjacent points are not too large. The following example
    +# does not meet this criteria and by that serves to illustrate the segmentation
    +# and coloring mechanism.
    +x = [0, 1, 2, 3, 4]
    +y = [0, 1, 2, 1, 1]
    +c = [1, 2, 3, 4, 5]
    +fig, ax = plt.subplots()
    +ax.scatter(x, y, c=c, cmap='rainbow')
    +colored_line(x, y, c=c, ax=ax, cmap='rainbow')
    +
    +plt.show()
    +
    +####################################################################
    +# Color values between points
    +# ---------------------------
    +#
    +
    +
    +def colored_line_between_pts(x, y, c, ax, **lc_kwargs):
    +    """
    +    Plot a line with a color specified between (x, y) points by a third value.
    +
    +    It does this by creating a collection of line segments between each pair of
    +    neighboring points. The color of each segment is determined by the
    +    made up of two straight lines each connecting the current (x, y) point to the
    +    midpoints of the lines connecting the current point with its two neighbors.
    +    This creates a smooth line with no gaps between the line segments.
    +
    +    Parameters
    +    ----------
    +    x, y : array-like
    +        The horizontal and vertical coordinates of the data points.
    +    c : array-like
    +        The color values, which should have a size one less than that of x and y.
    +    ax : Axes
    +        Axis object on which to plot the colored line.
    +    **lc_kwargs
    +        Any additional arguments to pass to matplotlib.collections.LineCollection
    +        constructor. This should not include the array keyword argument because
    +        that is set to the color argument. If provided, it will be overridden.
    +
    +    Returns
    +    -------
    +    matplotlib.collections.LineCollection
    +        The generated line collection representing the colored line.
    +    """
    +    if "array" in lc_kwargs:
    +        warnings.warn('The provided "array" keyword argument will be overridden')
    +
    +    # Check color array size (LineCollection still works, but values are unused)
    +    if len(c) != len(x) - 1:
    +        warnings.warn(
    +            "The c argument should have a length one less than the length of x and y. "
    +            "If it has the same length, use the colored_line function instead."
    +        )
    +
    +    # Create a set of line segments so that we can color them individually
    +    # This creates the points as an N x 1 x 2 array so that we can stack points
    +    # together easily to get the segments. The segments array for line collection
    +    # needs to be (numlines) x (points per line) x 2 (for x and y)
    +    points = np.array([x, y]).T.reshape(-1, 1, 2)
    +    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    +    lc = LineCollection(segments, **lc_kwargs)
    +
    +    # Set the values used for colormapping
    +    lc.set_array(c)
    +
    +    return ax.add_collection(lc)
    +
    +
    +# -------------- Create and show plot --------------
    +x = np.linspace(0, 3 * np.pi, 500)
    +y = np.sin(x)
    +dydx = np.cos(0.5 * (x[:-1] + x[1:]))  # first derivative
    +
    +fig2, ax2 = plt.subplots()
    +line = colored_line_between_pts(x, y, dydx, ax2, linewidth=2, cmap="viridis")
    +fig2.colorbar(line, ax=ax2, label="dy/dx")
    +
    +ax2.set_xlim(x.min(), x.max())
    +ax2.set_ylim(-1.1, 1.1)
    +ax2.set_title("Color between points")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    styling: color
    +#    styling: linestyle
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/multivariate_marker_plot.py b/galleries/examples/lines_bars_and_markers/multivariate_marker_plot.py
    new file mode 100644
    index 000000000000..f9550d7459b2
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/multivariate_marker_plot.py
    @@ -0,0 +1,56 @@
    +"""
    +==============================================
    +Mapping marker properties to multivariate data
    +==============================================
    +
    +This example shows how to use different properties of markers to plot
    +multivariate datasets. Here we represent a successful baseball throw as a
    +smiley face with marker size mapped to the skill of thrower, marker rotation to
    +the take-off angle, and thrust to the marker color.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.colors import Normalize
    +from matplotlib.markers import MarkerStyle
    +from matplotlib.text import TextPath
    +from matplotlib.transforms import Affine2D
    +
    +SUCCESS_SYMBOLS = [
    +    TextPath((0, 0), "☹"),
    +    TextPath((0, 0), "😒"),
    +    TextPath((0, 0), "☺"),
    +]
    +
    +N = 25
    +np.random.seed(42)
    +skills = np.random.uniform(5, 80, size=N) * 0.1 + 5
    +takeoff_angles = np.random.normal(0, 90, N)
    +thrusts = np.random.uniform(size=N)
    +successful = np.random.randint(0, 3, size=N)
    +positions = np.random.normal(size=(N, 2)) * 5
    +data = zip(skills, takeoff_angles, thrusts, successful, positions)
    +
    +cmap = plt.colormaps["plasma"]
    +fig, ax = plt.subplots()
    +fig.suptitle("Throwing success", size=14)
    +for skill, takeoff, thrust, mood, pos in data:
    +    t = Affine2D().scale(skill).rotate_deg(takeoff)
    +    m = MarkerStyle(SUCCESS_SYMBOLS[mood], transform=t)
    +    ax.plot(pos[0], pos[1], marker=m, color=cmap(thrust))
    +fig.colorbar(plt.cm.ScalarMappable(norm=Normalize(0, 1), cmap="plasma"),
    +             ax=ax, label="Normalized Thrust [a.u.]")
    +ax.set_xlabel("X position [m]")
    +ax.set_ylabel("Y position [m]")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    styling: color,
    +#    styling: shape
    +#    level: beginner
    +#    purpose: fun
    diff --git a/galleries/examples/lines_bars_and_markers/scatter_demo2.py b/galleries/examples/lines_bars_and_markers/scatter_demo2.py
    new file mode 100644
    index 000000000000..f22c7239d250
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/scatter_demo2.py
    @@ -0,0 +1,45 @@
    +"""
    +=============
    +Scatter Demo2
    +=============
    +
    +Demo of scatter plot with varying marker colors and sizes.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +
    +# Load a numpy record array from yahoo csv data with fields date, open, high,
    +# low, close, volume, adj_close from the mpl-data/sample_data directory. The
    +# record array stores the date as an np.datetime64 with a day unit ('D') in
    +# the date column.
    +price_data = cbook.get_sample_data('goog.npz')['price_data']
    +price_data = price_data[-250:]  # get the most recent 250 trading days
    +
    +delta1 = np.diff(price_data["adj_close"]) / price_data["adj_close"][:-1]
    +
    +# Marker size in units of points^2
    +volume = (15 * price_data["volume"][:-2] / price_data["volume"][0])**2
    +close = 0.003 * price_data["close"][:-2] / 0.003 * price_data["open"][:-2]
    +
    +fig, ax = plt.subplots()
    +ax.scatter(delta1[:-1], delta1[1:], c=close, s=volume, alpha=0.5)
    +
    +ax.set_xlabel(r'$\Delta_i$', fontsize=15)
    +ax.set_ylabel(r'$\Delta_{i+1}$', fontsize=15)
    +ax.set_title('Volume and percent change')
    +
    +ax.grid(True)
    +fig.tight_layout()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    styling: color
    +#    styling: colormap
    +#    plot-type: scatter
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/scatter_hist.py b/galleries/examples/lines_bars_and_markers/scatter_hist.py
    new file mode 100644
    index 000000000000..d2b4c4eab228
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/scatter_hist.py
    @@ -0,0 +1,135 @@
    +"""
    +============================
    +Scatter plot with histograms
    +============================
    +
    +Add histograms to the x-axes and y-axes margins of a scatter plot.
    +
    +This layout features a central scatter plot illustrating the relationship
    +between x and y, a histogram at the top displaying the distribution of x, and a
    +histogram on the right showing the distribution of y.
    +
    +For a nice alignment of the main Axes with the marginals, two options are shown
    +below:
    +
    +.. contents::
    +   :local:
    +
    +While `.Axes.inset_axes` may be a bit more complex, it allows correct handling
    +of main Axes with a fixed aspect ratio.
    +
    +Let us first define a function that takes x and y data as input, as well as
    +three Axes, the main Axes for the scatter, and two marginal Axes. It will then
    +create the scatter and histograms inside the provided Axes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# some random data
    +x = np.random.randn(1000)
    +y = np.random.randn(1000)
    +
    +
    +def scatter_hist(x, y, ax, ax_histx, ax_histy):
    +    # no labels
    +    ax_histx.tick_params(axis="x", labelbottom=False)
    +    ax_histy.tick_params(axis="y", labelleft=False)
    +
    +    # the scatter plot:
    +    ax.scatter(x, y)
    +
    +    # now determine nice limits by hand:
    +    binwidth = 0.25
    +    xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
    +    lim = (int(xymax/binwidth) + 1) * binwidth
    +
    +    bins = np.arange(-lim, lim + binwidth, binwidth)
    +    ax_histx.hist(x, bins=bins)
    +    ax_histy.hist(y, bins=bins, orientation='horizontal')
    +
    +
    +# %%
    +# Defining the Axes positions using subplot_mosaic
    +# ------------------------------------------------
    +#
    +# We use the `~.pyplot.subplot_mosaic` function to define the positions and
    +# names of the three axes; the empty axes is specified by ``'.'``.  We manually
    +# specify the size of the figure, and can make the different axes have
    +# different sizes by specifying the *width_ratios* and *height_ratios*
    +# arguments. The *layout* argument is set to ``'constrained'`` to optimize the
    +# spacing between the axes.
    +
    +fig, axs = plt.subplot_mosaic([['histx', '.'],
    +                               ['scatter', 'histy']],
    +                              figsize=(6, 6),
    +                              width_ratios=(4, 1), height_ratios=(1, 4),
    +                              layout='constrained')
    +scatter_hist(x, y, axs['scatter'], axs['histx'], axs['histy'])
    +
    +
    +# %%
    +#
    +# Defining the Axes positions using inset_axes
    +# --------------------------------------------
    +#
    +# `~.Axes.inset_axes` can be used to position marginals *outside* the main
    +# Axes.  The advantage of doing so is that the aspect ratio of the main Axes
    +# can be fixed, and the marginals will always be drawn relative to the position
    +# of the Axes.
    +
    +# Create a Figure, which doesn't have to be square.
    +fig = plt.figure(layout='constrained')
    +# Create the main Axes.
    +ax = fig.add_subplot()
    +# The main Axes' aspect can be fixed.
    +ax.set_aspect('equal')
    +# Create marginal Axes, which have 25% of the size of the main Axes.  Note that
    +# the inset Axes are positioned *outside* (on the right and the top) of the
    +# main Axes, by specifying axes coordinates greater than 1.  Axes coordinates
    +# less than 0 would likewise specify positions on the left and the bottom of
    +# the main Axes.
    +ax_histx = ax.inset_axes([0, 1.05, 1, 0.25], sharex=ax)
    +ax_histy = ax.inset_axes([1.05, 0, 0.25, 1], sharey=ax)
    +# Draw the scatter plot and marginals.
    +scatter_hist(x, y, ax, ax_histx, ax_histy)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# While we recommend using one of the two methods described above, there are
    +# number of other ways to achieve a similar layout:
    +#
    +# - The Axes can be positioned manually in relative coordinates using
    +#   `~matplotlib.figure.Figure.add_axes`.
    +# - A gridspec can be used to create the layout
    +#   (`~matplotlib.figure.Figure.add_gridspec`) and adding only the three desired
    +#   axes (`~matplotlib.figure.Figure.add_subplot`).
    +# - Four subplots can be created  using `~.pyplot.subplots`,  and the unused
    +#   axes in the upper right can be removed manually.
    +# - The ``axes_grid1`` toolkit can be used, as shown in
    +#   :doc:`/gallery/axes_grid1/scatter_hist_locatable_axes`.
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.subplot_mosaic`
    +#    - `matplotlib.pyplot.subplot_mosaic`
    +#    - `matplotlib.figure.Figure.add_subplot`
    +#    - `matplotlib.axes.Axes.inset_axes`
    +#    - `matplotlib.axes.Axes.scatter`
    +#    - `matplotlib.axes.Axes.hist`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: scatter
    +#    plot-type: histogram
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/scatter_masked.py b/galleries/examples/lines_bars_and_markers/scatter_masked.py
    new file mode 100644
    index 000000000000..97132e85192e
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/scatter_masked.py
    @@ -0,0 +1,39 @@
    +"""
    +===============================
    +Scatter plot with masked values
    +===============================
    +
    +Mask some data points and add a line demarking
    +masked regions.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +N = 100
    +r0 = 0.6
    +x = 0.9 * np.random.rand(N)
    +y = 0.9 * np.random.rand(N)
    +area = (20 * np.random.rand(N))**2  # 0 to 10 point radii
    +c = np.sqrt(area)
    +r = np.sqrt(x ** 2 + y ** 2)
    +area1 = np.ma.masked_where(r < r0, area)
    +area2 = np.ma.masked_where(r >= r0, area)
    +plt.scatter(x, y, s=area1, marker='^', c=c)
    +plt.scatter(x, y, s=area2, marker='o', c=c)
    +# Show the boundary between the regions:
    +theta = np.arange(0, np.pi / 2, 0.01)
    +plt.plot(r0 * np.cos(theta), r0 * np.sin(theta))
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    plot-type: scatter
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/scatter_star_poly.py b/galleries/examples/lines_bars_and_markers/scatter_star_poly.py
    new file mode 100644
    index 000000000000..c2fee968ad7b
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/scatter_star_poly.py
    @@ -0,0 +1,59 @@
    +"""
    +===============
    +Marker examples
    +===============
    +
    +Example with different ways to specify markers.
    +
    +See also the `matplotlib.markers` documentation for a list of all markers and
    +:doc:`/gallery/lines_bars_and_markers/marker_reference` for more information
    +on configuring markers.
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/scatter_custom_symbol
    +.. redirect-from:: /gallery/lines_bars_and_markers/scatter_symbol
    +.. redirect-from:: /gallery/lines_bars_and_markers/scatter_piecharts
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +x = np.random.rand(10)
    +y = np.random.rand(10)
    +z = np.sqrt(x**2 + y**2)
    +
    +fig, axs = plt.subplots(2, 3, sharex=True, sharey=True, layout="constrained")
    +
    +# Matplotlib marker symbol
    +axs[0, 0].scatter(x, y, s=80, c=z, marker=">")
    +axs[0, 0].set_title("marker='>'")
    +
    +# marker from TeX: passing a TeX symbol name enclosed in $-signs
    +axs[0, 1].scatter(x, y, s=80, c=z, marker=r"$\clubsuit$")
    +axs[0, 1].set_title(r"marker=r'\$\clubsuit\$'")
    +
    +# marker from path: passing a custom path of N vertices as a (N, 2) array-like
    +verts = [[-1, -1], [1, -1], [1, 1], [-1, -1]]
    +axs[0, 2].scatter(x, y, s=80, c=z, marker=verts)
    +axs[0, 2].set_title("marker=verts")
    +
    +# regular pentagon marker
    +axs[1, 0].scatter(x, y, s=80, c=z, marker=(5, 0))
    +axs[1, 0].set_title("marker=(5, 0)")
    +
    +# regular 5-pointed star marker
    +axs[1, 1].scatter(x, y, s=80, c=z, marker=(5, 1))
    +axs[1, 1].set_title("marker=(5, 1)")
    +
    +# regular 5-pointed asterisk marker
    +axs[1, 2].scatter(x, y, s=80, c=z, marker=(5, 2))
    +axs[1, 2].set_title("marker=(5, 2)")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: marker
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/scatter_with_legend.py b/galleries/examples/lines_bars_and_markers/scatter_with_legend.py
    new file mode 100644
    index 000000000000..e9f19981fe4a
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/scatter_with_legend.py
    @@ -0,0 +1,117 @@
    +"""
    +==========================
    +Scatter plot with a legend
    +==========================
    +
    +To create a scatter plot with a legend one may use a loop and create one
    +`~.Axes.scatter` plot per item to appear in the legend and set the ``label``
    +accordingly.
    +
    +The following also demonstrates how transparency of the markers
    +can be adjusted by giving ``alpha`` a value between 0 and 1.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +
    +fig, ax = plt.subplots()
    +for color in ['tab:blue', 'tab:orange', 'tab:green']:
    +    n = 750
    +    x, y = np.random.rand(2, n)
    +    scale = 200.0 * np.random.rand(n)
    +    ax.scatter(x, y, c=color, s=scale, label=color,
    +               alpha=0.3, edgecolors='none')
    +
    +ax.legend()
    +ax.grid(True)
    +
    +plt.show()
    +
    +
    +# %%
    +# .. _automatedlegendcreation:
    +#
    +# Automated legend creation
    +# -------------------------
    +#
    +# Another option for creating a legend for a scatter is to use the
    +# `.PathCollection.legend_elements` method.  It will automatically try to
    +# determine a useful number of legend entries to be shown and return a tuple of
    +# handles and labels. Those can be passed to the call to `~.axes.Axes.legend`.
    +
    +
    +N = 45
    +x, y = np.random.rand(2, N)
    +c = np.random.randint(1, 5, size=N)
    +s = np.random.randint(10, 220, size=N)
    +
    +fig, ax = plt.subplots()
    +
    +scatter = ax.scatter(x, y, c=c, s=s)
    +
    +# produce a legend with the unique colors from the scatter
    +legend1 = ax.legend(*scatter.legend_elements(),
    +                    loc="lower left", title="Classes")
    +ax.add_artist(legend1)
    +
    +# produce a legend with a cross-section of sizes from the scatter
    +handles, labels = scatter.legend_elements(prop="sizes", alpha=0.6)
    +legend2 = ax.legend(handles, labels, loc="upper right", title="Sizes")
    +
    +plt.show()
    +
    +
    +# %%
    +# Further arguments to the `.PathCollection.legend_elements` method
    +# can be used to steer how many legend entries are to be created and how they
    +# should be labeled. The following shows how to use some of them.
    +
    +volume = np.random.rayleigh(27, size=40)
    +amount = np.random.poisson(10, size=40)
    +ranking = np.random.normal(size=40)
    +price = np.random.uniform(1, 10, size=40)
    +
    +fig, ax = plt.subplots()
    +
    +# Because the price is much too small when being provided as size for ``s``,
    +# we normalize it to some useful point sizes, s=0.3*(price*3)**2
    +scatter = ax.scatter(volume, amount, c=ranking, s=0.3*(price*3)**2,
    +                     vmin=-3, vmax=3, cmap="Spectral")
    +
    +# Produce a legend for the ranking (colors). Even though there are 40 different
    +# rankings, we only want to show 5 of them in the legend.
    +legend1 = ax.legend(*scatter.legend_elements(num=5),
    +                    loc="upper left", title="Ranking")
    +ax.add_artist(legend1)
    +
    +# Produce a legend for the price (sizes). Because we want to show the prices
    +# in dollars, we use the *func* argument to supply the inverse of the function
    +# used to calculate the sizes from above. The *fmt* ensures to show the price
    +# in dollars. Note how we target at 5 elements here, but obtain only 4 in the
    +# created legend due to the automatic round prices that are chosen for us.
    +kw = dict(prop="sizes", num=5, color=scatter.cmap(0.7), fmt="$ {x:.2f}",
    +          func=lambda s: np.sqrt(s/.3)/3)
    +legend2 = ax.legend(*scatter.legend_elements(**kw),
    +                    loc="lower right", title="Price")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#    - `matplotlib.collections.PathCollection.legend_elements`
    +#
    +# .. tags::
    +#
    +#    component: legend
    +#    plot-type: scatter
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/simple_plot.py b/galleries/examples/lines_bars_and_markers/simple_plot.py
    new file mode 100644
    index 000000000000..8f4324b0fc8d
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/simple_plot.py
    @@ -0,0 +1,40 @@
    +"""
    +=========
    +Line plot
    +=========
    +
    +Create a basic line plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Data for plotting
    +t = np.arange(0.0, 2.0, 0.01)
    +s = 1 + np.sin(2 * np.pi * t)
    +
    +fig, ax = plt.subplots()
    +ax.plot(t, s)
    +
    +ax.set(xlabel='time (s)', ylabel='voltage (mV)',
    +       title='About as simple as it gets, folks')
    +ax.grid()
    +
    +fig.savefig("test.png")
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.figure.Figure.savefig`
    +#
    +# .. tags::
    +#
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/span_regions.py b/galleries/examples/lines_bars_and_markers/span_regions.py
    new file mode 100644
    index 000000000000..ab9d6c8897dc
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/span_regions.py
    @@ -0,0 +1,37 @@
    +"""
    +==========================================================
    +Shade regions defined by a logical mask using fill_between
    +==========================================================
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2, 0.01)
    +s = np.sin(2*np.pi*t)
    +
    +fig, ax = plt.subplots()
    +
    +ax.plot(t, s, color='black')
    +ax.axhline(0, color='black')
    +
    +ax.fill_between(t, 1, where=s > 0, facecolor='green', alpha=.5)
    +ax.fill_between(t, -1, where=s < 0, facecolor='red', alpha=.5)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.fill_between`
    +#
    +# .. tags::
    +#
    +#    styling: conditional
    +#    plot-type: fill_between
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/spectrum_demo.py b/galleries/examples/lines_bars_and_markers/spectrum_demo.py
    new file mode 100644
    index 000000000000..57706e22be9d
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/spectrum_demo.py
    @@ -0,0 +1,58 @@
    +"""
    +========================
    +Spectrum representations
    +========================
    +
    +The plots show different spectrum representations of a sine signal with
    +additive noise. A (frequency) spectrum of a discrete-time signal is calculated
    +by utilizing the fast Fourier transform (FFT).
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(0)
    +
    +dt = 0.01  # sampling interval
    +Fs = 1 / dt  # sampling frequency
    +t = np.arange(0, 10, dt)
    +
    +# generate noise:
    +nse = np.random.randn(len(t))
    +r = np.exp(-t / 0.05)
    +cnse = np.convolve(nse, r) * dt
    +cnse = cnse[:len(t)]
    +
    +s = 0.1 * np.sin(4 * np.pi * t) + cnse  # the signal
    +
    +fig = plt.figure(figsize=(7, 7), layout='constrained')
    +axs = fig.subplot_mosaic([["signal", "signal"],
    +                          ["magnitude", "log_magnitude"],
    +                          ["phase", "angle"]])
    +
    +# plot time signal:
    +axs["signal"].set_title("Signal")
    +axs["signal"].plot(t, s, color='C0')
    +axs["signal"].set_xlabel("Time (s)")
    +axs["signal"].set_ylabel("Amplitude")
    +
    +# plot different spectrum types:
    +axs["magnitude"].set_title("Magnitude Spectrum")
    +axs["magnitude"].magnitude_spectrum(s, Fs=Fs, color='C1')
    +
    +axs["log_magnitude"].set_title("Log. Magnitude Spectrum")
    +axs["log_magnitude"].magnitude_spectrum(s, Fs=Fs, scale='dB', color='C1')
    +
    +axs["phase"].set_title("Phase Spectrum ")
    +axs["phase"].phase_spectrum(s, Fs=Fs, color='C2')
    +
    +axs["angle"].set_title("Angle Spectrum")
    +axs["angle"].angle_spectrum(s, Fs=Fs, color='C2')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    domain: signal-processing
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/stackplot_demo.py b/galleries/examples/lines_bars_and_markers/stackplot_demo.py
    new file mode 100644
    index 000000000000..2ed52ed9a2ce
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/stackplot_demo.py
    @@ -0,0 +1,81 @@
    +"""
    +===========================
    +Stackplots and streamgraphs
    +===========================
    +"""
    +
    +# %%
    +# Stackplots
    +# ----------
    +#
    +# Stackplots draw multiple datasets as vertically stacked areas. This is
    +# useful when the individual data values and additionally their cumulative
    +# value are of interest.
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as mticker
    +
    +# data from United Nations World Population Prospects (Revision 2019)
    +# https://population.un.org/wpp/, license: CC BY 3.0 IGO
    +year = [1950, 1960, 1970, 1980, 1990, 2000, 2010, 2018]
    +population_by_continent = {
    +    'Africa': [.228, .284, .365, .477, .631, .814, 1.044, 1.275],
    +    'the Americas': [.340, .425, .519, .619, .727, .840, .943, 1.006],
    +    'Asia': [1.394, 1.686, 2.120, 2.625, 3.202, 3.714, 4.169, 4.560],
    +    'Europe': [.220, .253, .276, .295, .310, .303, .294, .293],
    +    'Oceania': [.012, .015, .019, .022, .026, .031, .036, .039],
    +}
    +
    +fig, ax = plt.subplots()
    +ax.stackplot(year, population_by_continent.values(),
    +             labels=population_by_continent.keys(), alpha=0.8)
    +ax.legend(loc='upper left', reverse=True)
    +ax.set_title('World population')
    +ax.set_xlabel('Year')
    +ax.set_ylabel('Number of people (billions)')
    +# add tick at every 200 million people
    +ax.yaxis.set_minor_locator(mticker.MultipleLocator(.2))
    +
    +plt.show()
    +
    +# %%
    +# Streamgraphs
    +# ------------
    +#
    +# Using the *baseline* parameter, you can turn an ordinary stacked area plot
    +# with baseline 0 into a stream graph.
    +
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def gaussian_mixture(x, n=5):
    +    """Return a random mixture of *n* Gaussians, evaluated at positions *x*."""
    +    def add_random_gaussian(a):
    +        amplitude = 1 / (.1 + np.random.random())
    +        dx = x[-1] - x[0]
    +        x0 = (2 * np.random.random() - .5) * dx
    +        z = 10 / (.1 + np.random.random()) / dx
    +        a += amplitude * np.exp(-(z * (x - x0))**2)
    +    a = np.zeros_like(x)
    +    for j in range(n):
    +        add_random_gaussian(a)
    +    return a
    +
    +
    +x = np.linspace(0, 100, 101)
    +ys = [gaussian_mixture(x) for _ in range(3)]
    +
    +fig, ax = plt.subplots()
    +ax.stackplot(x, ys, baseline='wiggle')
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: stackplot
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/stairs_demo.py b/galleries/examples/lines_bars_and_markers/stairs_demo.py
    new file mode 100644
    index 000000000000..9c7506e52b27
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/stairs_demo.py
    @@ -0,0 +1,97 @@
    +"""
    +===========
    +Stairs Demo
    +===========
    +
    +This example demonstrates the use of `~.matplotlib.pyplot.stairs` for stepwise
    +constant functions. A common use case is histogram and histogram-like data
    +visualization.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import StepPatch
    +
    +np.random.seed(0)
    +h, edges = np.histogram(np.random.normal(5, 3, 5000),
    +                        bins=np.linspace(0, 10, 20))
    +
    +fig, axs = plt.subplots(3, 1, figsize=(7, 15))
    +axs[0].stairs(h, edges, label='Simple histogram')
    +axs[0].stairs(h, edges + 5, baseline=50, label='Modified baseline')
    +axs[0].stairs(h, edges + 10, baseline=None, label='No edges')
    +axs[0].set_title("Step Histograms")
    +
    +axs[1].stairs(np.arange(1, 6, 1), fill=True,
    +              label='Filled histogram\nw/ automatic edges')
    +axs[1].stairs(np.arange(1, 6, 1)*0.3, np.arange(2, 8, 1),
    +              orientation='horizontal', hatch='//',
    +              label='Hatched histogram\nw/ horizontal orientation')
    +axs[1].set_title("Filled histogram")
    +
    +patch = StepPatch(values=[1, 2, 3, 2, 1],
    +                  edges=range(1, 7),
    +                  label=('Patch derived underlying object\n'
    +                         'with default edge/facecolor behaviour'))
    +axs[2].add_patch(patch)
    +axs[2].set_xlim(0, 7)
    +axs[2].set_ylim(-1, 5)
    +axs[2].set_title("StepPatch artist")
    +
    +for ax in axs:
    +    ax.legend()
    +plt.show()
    +
    +# %%
    +# *baseline* can take an array to allow for stacked histogram plots
    +A = [[0, 0, 0],
    +     [1, 2, 3],
    +     [2, 4, 6],
    +     [3, 6, 9]]
    +
    +for i in range(len(A) - 1):
    +    plt.stairs(A[i+1], baseline=A[i], fill=True)
    +
    +# %%
    +# Comparison of `.pyplot.step` and `.pyplot.stairs`
    +# -------------------------------------------------
    +#
    +# `.pyplot.step` defines the positions of the steps as single values. The steps
    +# extend left/right/both ways from these reference values depending on the
    +# parameter *where*. The number of *x* and *y* values is the same.
    +#
    +# In contrast, `.pyplot.stairs` defines the positions of the steps via their
    +# bounds *edges*, which is one element longer than the step values.
    +
    +bins = np.arange(14)
    +centers = bins[:-1] + np.diff(bins) / 2
    +y = np.sin(centers / 2)
    +
    +plt.step(bins[:-1], y, where='post', label='step(where="post")')
    +plt.plot(bins[:-1], y, 'o--', color='grey', alpha=0.3)
    +
    +plt.stairs(y - 1, bins, baseline=None, label='stairs()')
    +plt.plot(centers, y - 1, 'o--', color='grey', alpha=0.3)
    +plt.plot(np.repeat(bins, 2), np.hstack([y[0], np.repeat(y, 2), y[-1]]) - 1,
    +         'o', color='red', alpha=0.2)
    +
    +plt.legend()
    +plt.title('step() vs. stairs()')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.stairs` / `matplotlib.pyplot.stairs`
    +#    - `matplotlib.patches.StepPatch`
    +#
    +# .. tags::
    +#
    +#    plot-type: stairs
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/stem_plot.py b/galleries/examples/lines_bars_and_markers/stem_plot.py
    new file mode 100644
    index 000000000000..cde8fd8e8017
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/stem_plot.py
    @@ -0,0 +1,43 @@
    +"""
    +=========
    +Stem plot
    +=========
    +
    +`~.pyplot.stem` plots vertical lines from a baseline to the y-coordinate and
    +places a marker at the tip.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.linspace(0.1, 2 * np.pi, 41)
    +y = np.exp(np.sin(x))
    +
    +plt.stem(x, y)
    +plt.show()
    +
    +# %%
    +#
    +# The position of the baseline can be adapted using *bottom*.
    +# The parameters *linefmt*, *markerfmt*, and *basefmt* control basic format
    +# properties of the plot. However, in contrast to `~.pyplot.plot` not all
    +# properties are configurable via keyword arguments. For more advanced
    +# control adapt the line objects returned by `.pyplot`.
    +
    +markerline, stemlines, baseline = plt.stem(
    +    x, y, linefmt='grey', markerfmt='D', bottom=1.1)
    +markerline.set_markerfacecolor('none')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.stem` / `matplotlib.pyplot.stem`
    +#
    +# .. tags::
    +#
    +#    plot-type: stem
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/step_demo.py b/galleries/examples/lines_bars_and_markers/step_demo.py
    new file mode 100644
    index 000000000000..f74a069e52f3
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/step_demo.py
    @@ -0,0 +1,71 @@
    +"""
    +=========
    +Step Demo
    +=========
    +
    +This example demonstrates the use of `.pyplot.step` for piece-wise constant
    +curves. In particular, it illustrates the effect of the parameter *where*
    +on the step position.
    +
    +.. note::
    +
    +    For the common case that you know the edge positions, use `.pyplot.stairs`
    +    instead.
    +
    +The circular markers created with `.pyplot.plot` show the actual data
    +positions so that it's easier to see the effect of *where*.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.arange(14)
    +y = np.sin(x / 2)
    +
    +plt.step(x, y + 2, label='pre (default)')
    +plt.plot(x, y + 2, 'o--', color='grey', alpha=0.3)
    +
    +plt.step(x, y + 1, where='mid', label='mid')
    +plt.plot(x, y + 1, 'o--', color='grey', alpha=0.3)
    +
    +plt.step(x, y, where='post', label='post')
    +plt.plot(x, y, 'o--', color='grey', alpha=0.3)
    +
    +plt.grid(axis='x', color='0.95')
    +plt.legend(title='Parameter where:')
    +plt.title('plt.step(where=...)')
    +plt.show()
    +
    +# %%
    +# The same behavior can be achieved by using the ``drawstyle`` parameter of
    +# `.pyplot.plot`.
    +
    +plt.plot(x, y + 2, drawstyle='steps', label='steps (=steps-pre)')
    +plt.plot(x, y + 2, 'o--', color='grey', alpha=0.3)
    +
    +plt.plot(x, y + 1, drawstyle='steps-mid', label='steps-mid')
    +plt.plot(x, y + 1, 'o--', color='grey', alpha=0.3)
    +
    +plt.plot(x, y, drawstyle='steps-post', label='steps-post')
    +plt.plot(x, y, 'o--', color='grey', alpha=0.3)
    +
    +plt.grid(axis='x', color='0.95')
    +plt.legend(title='Parameter drawstyle:')
    +plt.title('plt.plot(drawstyle=...)')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.step` / `matplotlib.pyplot.step`
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#
    +# .. tags::
    +#
    +#    plot-type: step
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/lines_bars_and_markers/timeline.py b/galleries/examples/lines_bars_and_markers/timeline.py
    new file mode 100644
    index 000000000000..07aec891dfaf
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/timeline.py
    @@ -0,0 +1,136 @@
    +"""
    +====================================
    +Timeline with lines, dates, and text
    +====================================
    +
    +How to create a simple timeline using Matplotlib release dates.
    +
    +Timelines can be created with a collection of dates and text. In this example,
    +we show how to create a simple timeline using the dates for recent releases
    +of Matplotlib. First, we'll pull the data from GitHub.
    +"""
    +
    +from datetime import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.dates as mdates
    +
    +try:
    +    # Try to fetch a list of Matplotlib releases and their dates
    +    # from https://api.github.com/repos/matplotlib/matplotlib/releases
    +    import json
    +    import urllib.request
    +
    +    url = 'https://api.github.com/repos/matplotlib/matplotlib/releases'
    +    url += '?per_page=100'
    +    data = json.loads(urllib.request.urlopen(url, timeout=1).read().decode())
    +
    +    dates = []
    +    releases = []
    +    for item in data:
    +        if 'rc' not in item['tag_name'] and 'b' not in item['tag_name']:
    +            dates.append(item['published_at'].split("T")[0])
    +            releases.append(item['tag_name'].lstrip("v"))
    +
    +except Exception:
    +    # In case the above fails, e.g. because of missing internet connection
    +    # use the following lists as fallback.
    +    releases = ['2.2.4', '3.0.3', '3.0.2', '3.0.1', '3.0.0', '2.2.3',
    +                '2.2.2', '2.2.1', '2.2.0', '2.1.2', '2.1.1', '2.1.0',
    +                '2.0.2', '2.0.1', '2.0.0', '1.5.3', '1.5.2', '1.5.1',
    +                '1.5.0', '1.4.3', '1.4.2', '1.4.1', '1.4.0']
    +    dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',
    +             '2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',
    +             '2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',
    +             '2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',
    +             '2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',
    +             '2014-10-26', '2014-10-18', '2014-08-26']
    +
    +dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates]  # Convert strs to dates.
    +releases = [tuple(release.split('.')) for release in releases]  # Split by component.
    +dates, releases = zip(*sorted(zip(dates, releases)))  # Sort by increasing date.
    +
    +# %%
    +# Next, we'll create a stem plot with some variation in levels as to
    +# distinguish even close-by events. We add markers on the baseline for visual
    +# emphasis on the one-dimensional nature of the timeline.
    +#
    +# For each event, we add a text label via `~.Axes.annotate`, which is offset
    +# in units of points from the tip of the event line.
    +#
    +# Note that Matplotlib will automatically plot datetime inputs.
    +
    +# Choose some nice levels: alternate meso releases between top and bottom, and
    +# progressively shorten the stems for micro releases.
    +levels = []
    +macro_meso_releases = sorted({release[:2] for release in releases})
    +for release in releases:
    +    macro_meso = release[:2]
    +    micro = int(release[2])
    +    h = 1 + 0.8 * (5 - micro)
    +    level = h if macro_meso_releases.index(macro_meso) % 2 == 0 else -h
    +    levels.append(level)
    +
    +
    +def is_feature(release):
    +    """Return whether a version (split into components) is a feature release."""
    +    return release[-1] == '0'
    +
    +
    +# The figure and the axes.
    +fig, ax = plt.subplots(figsize=(8.8, 4), layout="constrained")
    +ax.set(title="Matplotlib release dates")
    +
    +# The vertical stems.
    +ax.vlines(dates, 0, levels,
    +          color=[("tab:red", 1 if is_feature(release) else .5) for release in releases])
    +# The baseline.
    +ax.axhline(0, c="black")
    +# The markers on the baseline.
    +meso_dates = [date for date, release in zip(dates, releases) if is_feature(release)]
    +micro_dates = [date for date, release in zip(dates, releases)
    +               if not is_feature(release)]
    +ax.plot(micro_dates, np.zeros_like(micro_dates), "ko", mfc="white")
    +ax.plot(meso_dates, np.zeros_like(meso_dates), "ko", mfc="tab:red")
    +
    +# Annotate the lines.
    +for date, level, release in zip(dates, levels, releases):
    +    version_str = '.'.join(release)
    +    ax.annotate(version_str, xy=(date, level),
    +                xytext=(-3, np.sign(level)*3), textcoords="offset points",
    +                verticalalignment="bottom" if level > 0 else "top",
    +                weight="bold" if is_feature(release) else "normal",
    +                bbox=dict(boxstyle='square', pad=0, lw=0, fc=(1, 1, 1, 0.7)))
    +
    +ax.xaxis.set(major_locator=mdates.YearLocator(),
    +             major_formatter=mdates.DateFormatter("%Y"))
    +
    +# Remove the y-axis and some spines.
    +ax.yaxis.set_visible(False)
    +ax.spines[["left", "top", "right"]].set_visible(False)
    +
    +ax.margins(y=0.1)
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.annotate`
    +#    - `matplotlib.axes.Axes.vlines`
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    +#    - `matplotlib.dates.MonthLocator`
    +#    - `matplotlib.dates.DateFormatter`
    +#
    +# .. tags::
    +#
    +#    component: annotation
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/lines_bars_and_markers/vline_hline_demo.py b/galleries/examples/lines_bars_and_markers/vline_hline_demo.py
    new file mode 100644
    index 000000000000..4bec4be760ee
    --- /dev/null
    +++ b/galleries/examples/lines_bars_and_markers/vline_hline_demo.py
    @@ -0,0 +1,40 @@
    +"""
    +=================
    +hlines and vlines
    +=================
    +
    +This example showcases the functions hlines and vlines.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +t = np.arange(0.0, 5.0, 0.1)
    +s = np.exp(-t) + np.sin(2 * np.pi * t) + 1
    +nse = np.random.normal(0.0, 0.3, t.shape) * s
    +
    +fig, (vax, hax) = plt.subplots(1, 2, figsize=(12, 6))
    +
    +vax.plot(t, s + nse, '^')
    +vax.vlines(t, [0], s)
    +# By using ``transform=vax.get_xaxis_transform()`` the y coordinates are scaled
    +# such that 0 maps to the bottom of the Axes and 1 to the top.
    +vax.vlines([1, 2], 0, 1, transform=vax.get_xaxis_transform(), colors='r')
    +vax.set_xlabel('time (s)')
    +vax.set_title('Vertical lines demo')
    +
    +hax.plot(s + nse, t, '^')
    +hax.hlines(t, [0], s, lw=2)
    +hax.set_xlabel('time (s)')
    +hax.set_title('Horizontal lines demo')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/misc/README.txt b/galleries/examples/misc/README.txt
    new file mode 100644
    index 000000000000..c2fbe88e116c
    --- /dev/null
    +++ b/galleries/examples/misc/README.txt
    @@ -0,0 +1,4 @@
    +.. _misc-examples-index:
    +
    +Miscellaneous
    +=============
    diff --git a/galleries/examples/misc/anchored_artists.py b/galleries/examples/misc/anchored_artists.py
    new file mode 100644
    index 000000000000..be600449bba6
    --- /dev/null
    +++ b/galleries/examples/misc/anchored_artists.py
    @@ -0,0 +1,76 @@
    +"""
    +================
    +Anchored Artists
    +================
    +
    +This example illustrates the use of the anchored objects without the
    +helper classes found in :mod:`mpl_toolkits.axes_grid1`. This version
    +of the figure is similar to the one found in
    +:doc:`/gallery/axes_grid1/simple_anchored_artists`, but it is
    +implemented using only the matplotlib namespace, without the help
    +of additional toolkits.
    +
    +.. redirect-from:: /gallery/userdemo/anchored_box01
    +.. redirect-from:: /gallery/userdemo/anchored_box02
    +.. redirect-from:: /gallery/userdemo/anchored_box03
    +"""
    +
    +from matplotlib import pyplot as plt
    +from matplotlib.lines import Line2D
    +from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox, DrawingArea,
    +                                  TextArea, VPacker)
    +from matplotlib.patches import Circle, Ellipse
    +
    +
    +def draw_text(ax):
    +    """Draw a text-box anchored to the upper-left corner of the figure."""
    +    box = AnchoredOffsetbox(child=TextArea("Figure 1a"),
    +                            loc="upper left", frameon=True)
    +    box.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
    +    ax.add_artist(box)
    +
    +
    +def draw_circles(ax):
    +    """Draw circles in axes coordinates."""
    +    area = DrawingArea(width=40, height=20)
    +    area.add_artist(Circle((10, 10), 10, fc="tab:blue"))
    +    area.add_artist(Circle((30, 10), 5, fc="tab:red"))
    +    box = AnchoredOffsetbox(
    +        child=area, loc="upper right", pad=0, frameon=False)
    +    ax.add_artist(box)
    +
    +
    +def draw_ellipse(ax):
    +    """Draw an ellipse of width=0.1, height=0.15 in data coordinates."""
    +    aux_tr_box = AuxTransformBox(ax.transData)
    +    aux_tr_box.add_artist(Ellipse((0, 0), width=0.1, height=0.15))
    +    box = AnchoredOffsetbox(child=aux_tr_box, loc="lower left", frameon=True)
    +    ax.add_artist(box)
    +
    +
    +def draw_sizebar(ax):
    +    """
    +    Draw a horizontal bar with length of 0.1 in data coordinates,
    +    with a fixed label center-aligned underneath.
    +    """
    +    size = 0.1
    +    text = r"1$^{\prime}$"
    +    sizebar = AuxTransformBox(ax.transData)
    +    sizebar.add_artist(Line2D([0, size], [0, 0], color="black"))
    +    text = TextArea(text)
    +    packer = VPacker(
    +        children=[sizebar, text], align="center", sep=5)  # separation in points.
    +    ax.add_artist(AnchoredOffsetbox(
    +        child=packer, loc="lower center", frameon=False,
    +        pad=0.1, borderpad=0.5))  # paddings relative to the legend fontsize.
    +
    +
    +fig, ax = plt.subplots()
    +ax.set_aspect(1)
    +
    +draw_text(ax)
    +draw_circles(ax)
    +draw_ellipse(ax)
    +draw_sizebar(ax)
    +
    +plt.show()
    diff --git a/galleries/examples/misc/bbox_intersect.py b/galleries/examples/misc/bbox_intersect.py
    new file mode 100644
    index 000000000000..9103705537d5
    --- /dev/null
    +++ b/galleries/examples/misc/bbox_intersect.py
    @@ -0,0 +1,39 @@
    +"""
    +==================================
    +Identify whether artists intersect
    +==================================
    +
    +The lines intersecting the rectangle are colored in red, while the others
    +are left as blue lines. This example showcases the `.intersects_bbox` function.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.path import Path
    +from matplotlib.transforms import Bbox
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +left, bottom, width, height = (-1, -1, 2, 2)
    +rect = plt.Rectangle((left, bottom), width, height,
    +                     facecolor="black", alpha=0.1)
    +
    +fig, ax = plt.subplots()
    +ax.add_patch(rect)
    +
    +bbox = Bbox.from_bounds(left, bottom, width, height)
    +
    +for i in range(12):
    +    vertices = (np.random.random((2, 2)) - 0.5) * 6.0
    +    path = Path(vertices)
    +    if path.intersects_bbox(bbox):
    +        color = 'r'
    +    else:
    +        color = 'b'
    +    ax.plot(vertices[:, 0], vertices[:, 1], color=color)
    +
    +plt.show()
    diff --git a/galleries/examples/misc/contour_manual.py b/galleries/examples/misc/contour_manual.py
    new file mode 100644
    index 000000000000..796b8695d914
    --- /dev/null
    +++ b/galleries/examples/misc/contour_manual.py
    @@ -0,0 +1,59 @@
    +"""
    +==============
    +Manual Contour
    +==============
    +
    +Example of displaying your own contour lines and polygons using ContourSet.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.contour import ContourSet
    +from matplotlib.path import Path
    +
    +# %%
    +# Contour lines for each level are a list/tuple of polygons.
    +lines0 = [[[0, 0], [0, 4]]]
    +lines1 = [[[2, 0], [1, 2], [1, 3]]]
    +lines2 = [[[3, 0], [3, 2]], [[3, 3], [3, 4]]]  # Note two lines.
    +
    +# %%
    +# Filled contours between two levels are also a list/tuple of polygons.
    +# Points can be ordered clockwise or anticlockwise.
    +filled01 = [[[0, 0], [0, 4], [1, 3], [1, 2], [2, 0]]]
    +filled12 = [[[2, 0], [3, 0], [3, 2], [1, 3], [1, 2]],   # Note two polygons.
    +            [[1, 4], [3, 4], [3, 3]]]
    +
    +# %%
    +
    +fig, ax = plt.subplots()
    +
    +# Filled contours using filled=True.
    +cs = ContourSet(ax, [0, 1, 2], [filled01, filled12], filled=True, cmap="bone")
    +cbar = fig.colorbar(cs)
    +
    +# Contour lines (non-filled).
    +lines = ContourSet(
    +    ax, [0, 1, 2], [lines0, lines1, lines2], cmap="cool", linewidths=3)
    +cbar.add_lines(lines)
    +
    +ax.set(xlim=(-0.5, 3.5), ylim=(-0.5, 4.5),
    +       title='User-specified contours')
    +
    +# %%
    +# Multiple filled contour lines can be specified in a single list of polygon
    +# vertices along with a list of vertex kinds (code types) as described in the
    +# Path class.  This is particularly useful for polygons with holes.
    +
    +fig, ax = plt.subplots()
    +filled01 = [[[0, 0], [3, 0], [3, 3], [0, 3], [1, 1], [1, 2], [2, 2], [2, 1]]]
    +M = Path.MOVETO
    +L = Path.LINETO
    +kinds01 = [[M, L, L, L, M, L, L, L]]
    +cs = ContourSet(ax, [0, 1], [filled01], [kinds01], filled=True)
    +cbar = fig.colorbar(cs)
    +
    +ax.set(xlim=(-0.5, 3.5), ylim=(-0.5, 3.5),
    +       title='User specified filled contours with holes')
    +
    +plt.show()
    diff --git a/galleries/examples/misc/coords_report.py b/galleries/examples/misc/coords_report.py
    new file mode 100644
    index 000000000000..84503be35c5f
    --- /dev/null
    +++ b/galleries/examples/misc/coords_report.py
    @@ -0,0 +1,28 @@
    +"""
    +=============
    +Coords Report
    +=============
    +
    +Override the default reporting of coords as the mouse moves over the Axes
    +in an interactive backend.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def millions(x):
    +    return '$%1.1fM' % (x * 1e-6)
    +
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +x = np.random.rand(20)
    +y = 1e7 * np.random.rand(20)
    +
    +fig, ax = plt.subplots()
    +ax.fmt_ydata = millions
    +plt.plot(x, y, 'o')
    +
    +plt.show()
    diff --git a/galleries/examples/misc/custom_projection.py b/galleries/examples/misc/custom_projection.py
    new file mode 100644
    index 000000000000..1bd4ce772b0a
    --- /dev/null
    +++ b/galleries/examples/misc/custom_projection.py
    @@ -0,0 +1,446 @@
    +"""
    +=================
    +Custom projection
    +=================
    +
    +Showcase Hammer projection by alleviating many features of Matplotlib.
    +"""
    +
    +import numpy as np
    +
    +import matplotlib
    +from matplotlib.axes import Axes
    +import matplotlib.axis as maxis
    +from matplotlib.patches import Circle
    +from matplotlib.path import Path
    +from matplotlib.projections import register_projection
    +import matplotlib.spines as mspines
    +from matplotlib.ticker import FixedLocator, Formatter, NullLocator
    +from matplotlib.transforms import Affine2D, BboxTransformTo, Transform
    +
    +rcParams = matplotlib.rcParams
    +
    +# This example projection class is rather long, but it is designed to
    +# illustrate many features, not all of which will be used every time.
    +# It is also common to factor out a lot of these methods into common
    +# code used by a number of projections with similar characteristics
    +# (see geo.py).
    +
    +
    +class GeoAxes(Axes):
    +    """
    +    An abstract base class for geographic projections
    +    """
    +    class ThetaFormatter(Formatter):
    +        """
    +        Used to format the theta tick labels.  Converts the native
    +        unit of radians into degrees and adds a degree symbol.
    +        """
    +        def __init__(self, round_to=1.0):
    +            self._round_to = round_to
    +
    +        def __call__(self, x, pos=None):
    +            degrees = round(np.rad2deg(x) / self._round_to) * self._round_to
    +            return f"{degrees:0.0f}\N{DEGREE SIGN}"
    +
    +    RESOLUTION = 75
    +
    +    def _init_axis(self):
    +        self.xaxis = maxis.XAxis(self)
    +        self.yaxis = maxis.YAxis(self)
    +        # Do not register xaxis or yaxis with spines -- as done in
    +        # Axes._init_axis() -- until GeoAxes.xaxis.clear() works.
    +        # self.spines['geo'].register_axis(self.yaxis)
    +
    +    def clear(self):
    +        # docstring inherited
    +        super().clear()
    +
    +        self.set_longitude_grid(30)
    +        self.set_latitude_grid(15)
    +        self.set_longitude_grid_ends(75)
    +        self.xaxis.set_minor_locator(NullLocator())
    +        self.yaxis.set_minor_locator(NullLocator())
    +        self.xaxis.set_ticks_position('none')
    +        self.yaxis.set_ticks_position('none')
    +        self.yaxis.set_tick_params(label1On=True)
    +        # Why do we need to turn on yaxis tick labels, but
    +        # xaxis tick labels are already on?
    +
    +        self.grid(rcParams['axes.grid'])
    +
    +        Axes.set_xlim(self, -np.pi, np.pi)
    +        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
    +
    +    def _set_lim_and_transforms(self):
    +        # A (possibly non-linear) projection on the (already scaled) data
    +
    +        # There are three important coordinate spaces going on here:
    +        #
    +        # 1. Data space: The space of the data itself
    +        #
    +        # 2. Axes space: The unit rectangle (0, 0) to (1, 1)
    +        #    covering the entire plot area.
    +        #
    +        # 3. Display space: The coordinates of the resulting image,
    +        #    often in pixels or dpi/inch.
    +
    +        # This function makes heavy use of the Transform classes in
    +        # ``lib/matplotlib/transforms.py.`` For more information, see
    +        # the inline documentation there.
    +
    +        # The goal of the first two transformations is to get from the
    +        # data space (in this case longitude and latitude) to Axes
    +        # space.  It is separated into a non-affine and affine part so
    +        # that the non-affine part does not have to be recomputed when
    +        # a simple affine change to the figure has been made (such as
    +        # resizing the window or changing the dpi).
    +
    +        # 1) The core transformation from data space into
    +        # rectilinear space defined in the HammerTransform class.
    +        self.transProjection = self._get_core_transform(self.RESOLUTION)
    +
    +        # 2) The above has an output range that is not in the unit
    +        # rectangle, so scale and translate it so it fits correctly
    +        # within the Axes.  The peculiar calculations of xscale and
    +        # yscale are specific to an Aitoff-Hammer projection, so don't
    +        # worry about them too much.
    +        self.transAffine = self._get_affine_transform()
    +
    +        # 3) This is the transformation from Axes space to display
    +        # space.
    +        self.transAxes = BboxTransformTo(self.bbox)
    +
    +        # Now put these 3 transforms together -- from data all the way
    +        # to display coordinates.  Using the '+' operator, these
    +        # transforms will be applied "in order".  The transforms are
    +        # automatically simplified, if possible, by the underlying
    +        # transformation framework.
    +        self.transData = \
    +            self.transProjection + \
    +            self.transAffine + \
    +            self.transAxes
    +
    +        # The main data transformation is set up.  Now deal with
    +        # gridlines and tick labels.
    +
    +        # Longitude gridlines and ticklabels.  The input to these
    +        # transforms are in display space in x and Axes space in y.
    +        # Therefore, the input values will be in range (-xmin, 0),
    +        # (xmax, 1).  The goal of these transforms is to go from that
    +        # space to display space.  The tick labels will be offset 4
    +        # pixels from the equator.
    +        self._xaxis_pretransform = \
    +            Affine2D() \
    +            .scale(1.0, self._longitude_cap * 2.0) \
    +            .translate(0.0, -self._longitude_cap)
    +        self._xaxis_transform = \
    +            self._xaxis_pretransform + \
    +            self.transData
    +        self._xaxis_text1_transform = \
    +            Affine2D().scale(1.0, 0.0) + \
    +            self.transData + \
    +            Affine2D().translate(0.0, 4.0)
    +        self._xaxis_text2_transform = \
    +            Affine2D().scale(1.0, 0.0) + \
    +            self.transData + \
    +            Affine2D().translate(0.0, -4.0)
    +
    +        # Now set up the transforms for the latitude ticks.  The input to
    +        # these transforms are in Axes space in x and display space in
    +        # y.  Therefore, the input values will be in range (0, -ymin),
    +        # (1, ymax).  The goal of these transforms is to go from that
    +        # space to display space.  The tick labels will be offset 4
    +        # pixels from the edge of the Axes ellipse.
    +        yaxis_stretch = Affine2D().scale(np.pi*2, 1).translate(-np.pi, 0)
    +        yaxis_space = Affine2D().scale(1.0, 1.1)
    +        self._yaxis_transform = \
    +            yaxis_stretch + \
    +            self.transData
    +        yaxis_text_base = \
    +            yaxis_stretch + \
    +            self.transProjection + \
    +            (yaxis_space +
    +             self.transAffine +
    +             self.transAxes)
    +        self._yaxis_text1_transform = \
    +            yaxis_text_base + \
    +            Affine2D().translate(-8.0, 0.0)
    +        self._yaxis_text2_transform = \
    +            yaxis_text_base + \
    +            Affine2D().translate(8.0, 0.0)
    +
    +    def _get_affine_transform(self):
    +        transform = self._get_core_transform(1)
    +        xscale, _ = transform.transform((np.pi, 0))
    +        _, yscale = transform.transform((0, np.pi/2))
    +        return Affine2D() \
    +            .scale(0.5 / xscale, 0.5 / yscale) \
    +            .translate(0.5, 0.5)
    +
    +    def get_xaxis_transform(self, which='grid'):
    +        """
    +        Override this method to provide a transformation for the
    +        x-axis tick labels.
    +
    +        Returns a tuple of the form (transform, valign, halign)
    +        """
    +        if which not in ['tick1', 'tick2', 'grid']:
    +            raise ValueError(
    +                "'which' must be one of 'tick1', 'tick2', or 'grid'")
    +        return self._xaxis_transform
    +
    +    def get_xaxis_text1_transform(self, pad):
    +        return self._xaxis_text1_transform, 'bottom', 'center'
    +
    +    def get_xaxis_text2_transform(self, pad):
    +        """
    +        Override this method to provide a transformation for the
    +        secondary x-axis tick labels.
    +
    +        Returns a tuple of the form (transform, valign, halign)
    +        """
    +        return self._xaxis_text2_transform, 'top', 'center'
    +
    +    def get_yaxis_transform(self, which='grid'):
    +        """
    +        Override this method to provide a transformation for the
    +        y-axis grid and ticks.
    +        """
    +        if which not in ['tick1', 'tick2', 'grid']:
    +            raise ValueError(
    +                "'which' must be one of 'tick1', 'tick2', or 'grid'")
    +        return self._yaxis_transform
    +
    +    def get_yaxis_text1_transform(self, pad):
    +        """
    +        Override this method to provide a transformation for the
    +        y-axis tick labels.
    +
    +        Returns a tuple of the form (transform, valign, halign)
    +        """
    +        return self._yaxis_text1_transform, 'center', 'right'
    +
    +    def get_yaxis_text2_transform(self, pad):
    +        """
    +        Override this method to provide a transformation for the
    +        secondary y-axis tick labels.
    +
    +        Returns a tuple of the form (transform, valign, halign)
    +        """
    +        return self._yaxis_text2_transform, 'center', 'left'
    +
    +    def _gen_axes_patch(self):
    +        """
    +        Override this method to define the shape that is used for the
    +        background of the plot.  It should be a subclass of Patch.
    +
    +        In this case, it is a Circle (that may be warped by the Axes
    +        transform into an ellipse).  Any data and gridlines will be
    +        clipped to this shape.
    +        """
    +        return Circle((0.5, 0.5), 0.5)
    +
    +    def _gen_axes_spines(self):
    +        return {'geo': mspines.Spine.circular_spine(self, (0.5, 0.5), 0.5)}
    +
    +    def set_yscale(self, *args, **kwargs):
    +        if args[0] != 'linear':
    +            raise NotImplementedError
    +
    +    # Prevent the user from applying scales to one or both of the
    +    # axes.  In this particular case, scaling the axes wouldn't make
    +    # sense, so we don't allow it.
    +    set_xscale = set_yscale
    +
    +    # Prevent the user from changing the axes limits.  In our case, we
    +    # want to display the whole sphere all the time, so we override
    +    # set_xlim and set_ylim to ignore any input.  This also applies to
    +    # interactive panning and zooming in the GUI interfaces.
    +    def set_xlim(self, *args, **kwargs):
    +        raise TypeError("Changing axes limits of a geographic projection is "
    +                        "not supported.  Please consider using Cartopy.")
    +
    +    set_ylim = set_xlim
    +
    +    def format_coord(self, lon, lat):
    +        """
    +        Override this method to change how the values are displayed in
    +        the status bar.
    +
    +        In this case, we want them to be displayed in degrees N/S/E/W.
    +        """
    +        lon, lat = np.rad2deg([lon, lat])
    +        ns = 'N' if lat >= 0.0 else 'S'
    +        ew = 'E' if lon >= 0.0 else 'W'
    +        return ('%f\N{DEGREE SIGN}%s, %f\N{DEGREE SIGN}%s'
    +                % (abs(lat), ns, abs(lon), ew))
    +
    +    def set_longitude_grid(self, degrees):
    +        """
    +        Set the number of degrees between each longitude grid.
    +
    +        This is an example method that is specific to this projection
    +        class -- it provides a more convenient interface to set the
    +        ticking than set_xticks would.
    +        """
    +        # Skip -180 and 180, which are the fixed limits.
    +        grid = np.arange(-180 + degrees, 180, degrees)
    +        self.xaxis.set_major_locator(FixedLocator(np.deg2rad(grid)))
    +        self.xaxis.set_major_formatter(self.ThetaFormatter(degrees))
    +
    +    def set_latitude_grid(self, degrees):
    +        """
    +        Set the number of degrees between each longitude grid.
    +
    +        This is an example method that is specific to this projection
    +        class -- it provides a more convenient interface than
    +        set_yticks would.
    +        """
    +        # Skip -90 and 90, which are the fixed limits.
    +        grid = np.arange(-90 + degrees, 90, degrees)
    +        self.yaxis.set_major_locator(FixedLocator(np.deg2rad(grid)))
    +        self.yaxis.set_major_formatter(self.ThetaFormatter(degrees))
    +
    +    def set_longitude_grid_ends(self, degrees):
    +        """
    +        Set the latitude(s) at which to stop drawing the longitude grids.
    +
    +        Often, in geographic projections, you wouldn't want to draw
    +        longitude gridlines near the poles.  This allows the user to
    +        specify the degree at which to stop drawing longitude grids.
    +
    +        This is an example method that is specific to this projection
    +        class -- it provides an interface to something that has no
    +        analogy in the base Axes class.
    +        """
    +        self._longitude_cap = np.deg2rad(degrees)
    +        self._xaxis_pretransform \
    +            .clear() \
    +            .scale(1.0, self._longitude_cap * 2.0) \
    +            .translate(0.0, -self._longitude_cap)
    +
    +    def get_data_ratio(self):
    +        """
    +        Return the aspect ratio of the data itself.
    +
    +        This method should be overridden by any Axes that have a
    +        fixed data ratio.
    +        """
    +        return 1.0
    +
    +    # Interactive panning and zooming is not supported with this projection,
    +    # so we override all of the following methods to disable it.
    +    def can_zoom(self):
    +        """
    +        Return whether this Axes supports the zoom box button functionality.
    +
    +        This Axes object does not support interactive zoom box.
    +        """
    +        return False
    +
    +    def can_pan(self):
    +        """
    +        Return whether this Axes supports the pan/zoom button functionality.
    +
    +        This Axes object does not support interactive pan/zoom.
    +        """
    +        return False
    +
    +    def start_pan(self, x, y, button):
    +        pass
    +
    +    def end_pan(self):
    +        pass
    +
    +    def drag_pan(self, button, key, x, y):
    +        pass
    +
    +
    +class HammerAxes(GeoAxes):
    +    """
    +    A custom class for the Aitoff-Hammer projection, an equal-area map
    +    projection.
    +
    +    https://en.wikipedia.org/wiki/Hammer_projection
    +    """
    +
    +    # The projection must specify a name. This will be used by the
    +    # user to select the projection,
    +    # i.e. ``subplot(projection='custom_hammer')``.
    +    name = 'custom_hammer'
    +
    +    class HammerTransform(Transform):
    +        """The base Hammer transform."""
    +        input_dims = output_dims = 2
    +
    +        def __init__(self, resolution):
    +            """
    +            Create a new Hammer transform.  Resolution is the number of steps
    +            to interpolate between each input line segment to approximate its
    +            path in curved Hammer space.
    +            """
    +            Transform.__init__(self)
    +            self._resolution = resolution
    +
    +        def transform_non_affine(self, ll):
    +            longitude, latitude = ll.T
    +
    +            # Pre-compute some values
    +            half_long = longitude / 2
    +            cos_latitude = np.cos(latitude)
    +            sqrt2 = np.sqrt(2)
    +
    +            alpha = np.sqrt(1 + cos_latitude * np.cos(half_long))
    +            x = (2 * sqrt2) * (cos_latitude * np.sin(half_long)) / alpha
    +            y = (sqrt2 * np.sin(latitude)) / alpha
    +            return np.column_stack([x, y])
    +
    +        def transform_path_non_affine(self, path):
    +            # vertices = path.vertices
    +            ipath = path.interpolated(self._resolution)
    +            return Path(self.transform(ipath.vertices), ipath.codes)
    +
    +        def inverted(self):
    +            return HammerAxes.InvertedHammerTransform(self._resolution)
    +
    +    class InvertedHammerTransform(Transform):
    +        input_dims = output_dims = 2
    +
    +        def __init__(self, resolution):
    +            Transform.__init__(self)
    +            self._resolution = resolution
    +
    +        def transform_non_affine(self, xy):
    +            x, y = xy.T
    +            z = np.sqrt(1 - (x / 4) ** 2 - (y / 2) ** 2)
    +            longitude = 2 * np.arctan((z * x) / (2 * (2 * z ** 2 - 1)))
    +            latitude = np.arcsin(y*z)
    +            return np.column_stack([longitude, latitude])
    +
    +        def inverted(self):
    +            return HammerAxes.HammerTransform(self._resolution)
    +
    +    def __init__(self, *args, **kwargs):
    +        self._longitude_cap = np.pi / 2.0
    +        super().__init__(*args, **kwargs)
    +        self.set_aspect(0.5, adjustable='box', anchor='C')
    +        self.clear()
    +
    +    def _get_core_transform(self, resolution):
    +        return self.HammerTransform(resolution)
    +
    +
    +# Now register the projection with Matplotlib so the user can select it.
    +register_projection(HammerAxes)
    +
    +
    +if __name__ == '__main__':
    +    import matplotlib.pyplot as plt
    +
    +    # Now make a simple example using the custom projection.
    +    fig, ax = plt.subplots(subplot_kw={'projection': 'custom_hammer'})
    +    ax.plot([-1, 1, 1], [-1, -1, 1], "o-")
    +    ax.grid()
    +
    +    plt.show()
    diff --git a/galleries/examples/misc/customize_rc.py b/galleries/examples/misc/customize_rc.py
    new file mode 100644
    index 000000000000..3cf877059151
    --- /dev/null
    +++ b/galleries/examples/misc/customize_rc.py
    @@ -0,0 +1,59 @@
    +"""
    +============
    +Customize Rc
    +============
    +
    +I'm not trying to make a good-looking figure here, but just to show
    +some examples of customizing `.rcParams` on the fly.
    +
    +If you like to work interactively, and need to create different sets
    +of defaults for figures (e.g., one set of defaults for publication, one
    +set for interactive exploration), you may want to define some
    +functions in a custom module that set the defaults, e.g.,::
    +
    +    def set_pub():
    +        rcParams.update({
    +            "font.weight": "bold",  # bold fonts
    +            "tick.labelsize": 15,   # large tick labels
    +            "lines.linewidth": 1,   # thick lines
    +            "lines.color": "k",     # black lines
    +            "grid.color": "0.5",    # gray gridlines
    +            "grid.linestyle": "-",  # solid gridlines
    +            "grid.linewidth": 0.5,  # thin gridlines
    +            "savefig.dpi": 300,     # higher resolution output.
    +        })
    +
    +Then as you are working interactively, you just need to do::
    +
    +    >>> set_pub()
    +    >>> plot([1, 2, 3])
    +    >>> savefig('myfig')
    +    >>> rcdefaults()  # restore the defaults
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +plt.subplot(311)
    +plt.plot([1, 2, 3])
    +
    +# the axes attributes need to be set before the call to subplot
    +plt.rcParams.update({
    +    "font.weight": "bold",
    +    "xtick.major.size": 5,
    +    "xtick.major.pad": 7,
    +    "xtick.labelsize": 15,
    +    "grid.color": "0.5",
    +    "grid.linestyle": "-",
    +    "grid.linewidth": 5,
    +    "lines.linewidth": 2,
    +    "lines.color": "g",
    +})
    +plt.subplot(312)
    +plt.plot([1, 2, 3])
    +plt.grid(True)
    +
    +plt.rcdefaults()
    +plt.subplot(313)
    +plt.plot([1, 2, 3])
    +plt.grid(True)
    +plt.show()
    diff --git a/galleries/examples/misc/demo_agg_filter.py b/galleries/examples/misc/demo_agg_filter.py
    new file mode 100644
    index 000000000000..c736013e9718
    --- /dev/null
    +++ b/galleries/examples/misc/demo_agg_filter.py
    @@ -0,0 +1,297 @@
    +"""
    +==========
    +AGG filter
    +==========
    +
    +Most pixel-based backends in Matplotlib use `Anti-Grain Geometry (AGG)`_ for
    +rendering. You can modify the rendering of Artists by applying a filter via
    +`.Artist.set_agg_filter`.
    +
    +.. _Anti-Grain Geometry (AGG): http://agg.sourceforge.net/antigrain.com
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.artist import Artist
    +from matplotlib.colors import LightSource
    +import matplotlib.transforms as mtransforms
    +
    +
    +def smooth1d(x, window_len):
    +    # copied from https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html
    +    s = np.r_[2*x[0] - x[window_len:1:-1], x, 2*x[-1] - x[-1:-window_len:-1]]
    +    w = np.hanning(window_len)
    +    y = np.convolve(w/w.sum(), s, mode='same')
    +    return y[window_len-1:-window_len+1]
    +
    +
    +def smooth2d(A, sigma=3):
    +    window_len = max(int(sigma), 3) * 2 + 1
    +    A = np.apply_along_axis(smooth1d, 0, A, window_len)
    +    A = np.apply_along_axis(smooth1d, 1, A, window_len)
    +    return A
    +
    +
    +class BaseFilter:
    +
    +    def get_pad(self, dpi):
    +        return 0
    +
    +    def process_image(self, padded_src, dpi):
    +        raise NotImplementedError("Should be overridden by subclasses")
    +
    +    def __call__(self, im, dpi):
    +        pad = self.get_pad(dpi)
    +        padded_src = np.pad(im, [(pad, pad), (pad, pad), (0, 0)], "constant")
    +        tgt_image = self.process_image(padded_src, dpi)
    +        return tgt_image, -pad, -pad
    +
    +
    +class OffsetFilter(BaseFilter):
    +
    +    def __init__(self, offsets=(0, 0)):
    +        self.offsets = offsets
    +
    +    def get_pad(self, dpi):
    +        return int(max(self.offsets) / 72 * dpi)
    +
    +    def process_image(self, padded_src, dpi):
    +        ox, oy = self.offsets
    +        a1 = np.roll(padded_src, int(ox / 72 * dpi), axis=1)
    +        a2 = np.roll(a1, -int(oy / 72 * dpi), axis=0)
    +        return a2
    +
    +
    +class GaussianFilter(BaseFilter):
    +    """Simple Gaussian filter."""
    +
    +    def __init__(self, sigma, alpha=0.5, color=(0, 0, 0)):
    +        self.sigma = sigma
    +        self.alpha = alpha
    +        self.color = color
    +
    +    def get_pad(self, dpi):
    +        return int(self.sigma*3 / 72 * dpi)
    +
    +    def process_image(self, padded_src, dpi):
    +        tgt_image = np.empty_like(padded_src)
    +        tgt_image[:, :, :3] = self.color
    +        tgt_image[:, :, 3] = smooth2d(padded_src[:, :, 3] * self.alpha,
    +                                      self.sigma / 72 * dpi)
    +        return tgt_image
    +
    +
    +class DropShadowFilter(BaseFilter):
    +
    +    def __init__(self, sigma, alpha=0.3, color=(0, 0, 0), offsets=(0, 0)):
    +        self.gauss_filter = GaussianFilter(sigma, alpha, color)
    +        self.offset_filter = OffsetFilter(offsets)
    +
    +    def get_pad(self, dpi):
    +        return max(self.gauss_filter.get_pad(dpi),
    +                   self.offset_filter.get_pad(dpi))
    +
    +    def process_image(self, padded_src, dpi):
    +        t1 = self.gauss_filter.process_image(padded_src, dpi)
    +        t2 = self.offset_filter.process_image(t1, dpi)
    +        return t2
    +
    +
    +class LightFilter(BaseFilter):
    +    """Apply LightSource filter"""
    +
    +    def __init__(self, sigma, fraction=1):
    +        """
    +        Parameters
    +        ----------
    +        sigma : float
    +            sigma for gaussian filter
    +        fraction: number, default: 1
    +            Increases or decreases the contrast of the hillshade.
    +            See `matplotlib.colors.LightSource`
    +
    +        """
    +        self.gauss_filter = GaussianFilter(sigma, alpha=1)
    +        self.light_source = LightSource()
    +        self.fraction = fraction
    +
    +    def get_pad(self, dpi):
    +        return self.gauss_filter.get_pad(dpi)
    +
    +    def process_image(self, padded_src, dpi):
    +        t1 = self.gauss_filter.process_image(padded_src, dpi)
    +        elevation = t1[:, :, 3]
    +        rgb = padded_src[:, :, :3]
    +        alpha = padded_src[:, :, 3:]
    +        rgb2 = self.light_source.shade_rgb(rgb, elevation,
    +                                           fraction=self.fraction,
    +                                           blend_mode="overlay")
    +        return np.concatenate([rgb2, alpha], -1)
    +
    +
    +class GrowFilter(BaseFilter):
    +    """Enlarge the area."""
    +
    +    def __init__(self, pixels, color=(1, 1, 1)):
    +        self.pixels = pixels
    +        self.color = color
    +
    +    def __call__(self, im, dpi):
    +        alpha = np.pad(im[..., 3], self.pixels, "constant")
    +        alpha2 = np.clip(smooth2d(alpha, self.pixels / 72 * dpi) * 5, 0, 1)
    +        new_im = np.empty((*alpha2.shape, 4))
    +        new_im[:, :, :3] = self.color
    +        new_im[:, :, 3] = alpha2
    +        offsetx, offsety = -self.pixels, -self.pixels
    +        return new_im, offsetx, offsety
    +
    +
    +class FilteredArtistList(Artist):
    +    """A simple container to filter multiple artists at once."""
    +
    +    def __init__(self, artist_list, filter):
    +        super().__init__()
    +        self._artist_list = artist_list
    +        self._filter = filter
    +
    +    def draw(self, renderer):
    +        renderer.start_rasterizing()
    +        renderer.start_filter()
    +        for a in self._artist_list:
    +            a.draw(renderer)
    +        renderer.stop_filter(self._filter)
    +        renderer.stop_rasterizing()
    +
    +
    +def filtered_text(ax):
    +    # mostly copied from contour_demo.py
    +
    +    # prepare image
    +    delta = 0.025
    +    x = np.arange(-3.0, 3.0, delta)
    +    y = np.arange(-2.0, 2.0, delta)
    +    X, Y = np.meshgrid(x, y)
    +    Z1 = np.exp(-X**2 - Y**2)
    +    Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +    Z = (Z1 - Z2) * 2
    +
    +    # draw
    +    ax.imshow(Z, interpolation='bilinear', origin='lower',
    +              cmap="gray", extent=(-3, 3, -2, 2), aspect='auto')
    +    levels = np.arange(-1.2, 1.6, 0.2)
    +    CS = ax.contour(Z, levels,
    +                    origin='lower',
    +                    linewidths=2,
    +                    extent=(-3, 3, -2, 2))
    +
    +    # contour label
    +    cl = ax.clabel(CS, levels[1::2],  # label every second level
    +                   fmt='%1.1f',
    +                   fontsize=11)
    +
    +    # change clabel color to black
    +    from matplotlib.patheffects import Normal
    +    for t in cl:
    +        t.set_color("k")
    +        # to force TextPath (i.e., same font in all backends)
    +        t.set_path_effects([Normal()])
    +
    +    # Add white glows to improve visibility of labels.
    +    white_glows = FilteredArtistList(cl, GrowFilter(3))
    +    ax.add_artist(white_glows)
    +    white_glows.set_zorder(cl[0].get_zorder() - 0.1)
    +
    +    ax.xaxis.set_visible(False)
    +    ax.yaxis.set_visible(False)
    +
    +
    +def drop_shadow_line(ax):
    +    # copied from examples/misc/svg_filter_line.py
    +
    +    # draw lines
    +    l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-")
    +    l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "ro-")
    +
    +    gauss = DropShadowFilter(4)
    +
    +    for l in [l1, l2]:
    +
    +        # draw shadows with same lines with slight offset.
    +        xx = l.get_xdata()
    +        yy = l.get_ydata()
    +        shadow, = ax.plot(xx, yy)
    +        shadow.update_from(l)
    +
    +        # offset transform
    +        transform = mtransforms.offset_copy(l.get_transform(), ax.figure,
    +                                            x=4.0, y=-6.0, units='points')
    +        shadow.set_transform(transform)
    +
    +        # adjust zorder of the shadow lines so that it is drawn below the
    +        # original lines
    +        shadow.set_zorder(l.get_zorder() - 0.5)
    +        shadow.set_agg_filter(gauss)
    +        shadow.set_rasterized(True)  # to support mixed-mode renderers
    +
    +    ax.set_xlim(0., 1.)
    +    ax.set_ylim(0., 1.)
    +
    +    ax.xaxis.set_visible(False)
    +    ax.yaxis.set_visible(False)
    +
    +
    +def drop_shadow_patches(ax):
    +    # Copied from barchart_demo.py
    +    N = 5
    +    group1_means = [20, 35, 30, 35, 27]
    +
    +    ind = np.arange(N)  # the x locations for the groups
    +    width = 0.35  # the width of the bars
    +
    +    rects1 = ax.bar(ind, group1_means, width, color='r', ec="w", lw=2)
    +
    +    group2_means = [25, 32, 34, 20, 25]
    +    rects2 = ax.bar(ind + width + 0.1, group2_means, width,
    +                    color='y', ec="w", lw=2)
    +
    +    drop = DropShadowFilter(5, offsets=(1, 1))
    +    shadow = FilteredArtistList(rects1 + rects2, drop)
    +    ax.add_artist(shadow)
    +    shadow.set_zorder(rects1[0].get_zorder() - 0.1)
    +
    +    ax.set_ylim(0, 40)
    +
    +    ax.xaxis.set_visible(False)
    +    ax.yaxis.set_visible(False)
    +
    +
    +def light_filter_pie(ax):
    +    fracs = [15, 30, 45, 10]
    +    explode = (0.1, 0.2, 0.1, 0.1)
    +    pie = ax.pie(fracs, explode=explode)
    +
    +    light_filter = LightFilter(9)
    +    for p in pie.wedges:
    +        p.set_agg_filter(light_filter)
    +        p.set_rasterized(True)  # to support mixed-mode renderers
    +        p.set(ec="none",
    +              lw=2)
    +
    +    gauss = DropShadowFilter(9, offsets=(3, -4), alpha=0.7)
    +    shadow = FilteredArtistList(pie.wedges, gauss)
    +    ax.add_artist(shadow)
    +    shadow.set_zorder(pie.wedges[0].get_zorder() - 0.1)
    +
    +
    +if __name__ == "__main__":
    +
    +    fix, axs = plt.subplots(2, 2)
    +
    +    filtered_text(axs[0, 0])
    +    drop_shadow_line(axs[0, 1])
    +    drop_shadow_patches(axs[1, 0])
    +    light_filter_pie(axs[1, 1])
    +    axs[1, 1].set_frame_on(True)
    +
    +    plt.show()
    diff --git a/galleries/examples/misc/demo_ribbon_box.py b/galleries/examples/misc/demo_ribbon_box.py
    new file mode 100644
    index 000000000000..5400a2a0063e
    --- /dev/null
    +++ b/galleries/examples/misc/demo_ribbon_box.py
    @@ -0,0 +1,94 @@
    +"""
    +==========
    +Ribbon box
    +==========
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from matplotlib import colors as mcolors
    +from matplotlib.image import AxesImage
    +from matplotlib.transforms import Bbox, BboxTransformTo, TransformedBbox
    +
    +
    +class RibbonBox:
    +
    +    original_image = plt.imread(
    +        cbook.get_sample_data("Minduka_Present_Blue_Pack.png"))
    +    cut_location = 70
    +    b_and_h = original_image[:, :, 2:3]
    +    color = original_image[:, :, 2:3] - original_image[:, :, 0:1]
    +    alpha = original_image[:, :, 3:4]
    +    nx = original_image.shape[1]
    +
    +    def __init__(self, color):
    +        rgb = mcolors.to_rgb(color)
    +        self.im = np.dstack(
    +            [self.b_and_h - self.color * (1 - np.array(rgb)), self.alpha])
    +
    +    def get_stretched_image(self, stretch_factor):
    +        stretch_factor = max(stretch_factor, 1)
    +        ny, nx, nch = self.im.shape
    +        ny2 = int(ny*stretch_factor)
    +        return np.vstack(
    +            [self.im[:self.cut_location],
    +             np.broadcast_to(
    +                 self.im[self.cut_location], (ny2 - ny, nx, nch)),
    +             self.im[self.cut_location:]])
    +
    +
    +class RibbonBoxImage(AxesImage):
    +    zorder = 1
    +
    +    def __init__(self, ax, bbox, color, *, extent=(0, 1, 0, 1), **kwargs):
    +        super().__init__(ax, extent=extent, **kwargs)
    +        self._bbox = bbox
    +        self._ribbonbox = RibbonBox(color)
    +        self.set_transform(BboxTransformTo(bbox))
    +
    +    def draw(self, renderer):
    +        stretch_factor = self._bbox.height / self._bbox.width
    +
    +        ny = int(stretch_factor*self._ribbonbox.nx)
    +        if self.get_array() is None or self.get_array().shape[0] != ny:
    +            arr = self._ribbonbox.get_stretched_image(stretch_factor)
    +            self.set_array(arr)
    +
    +        super().draw(renderer)
    +
    +
    +def main():
    +    fig, ax = plt.subplots()
    +
    +    years = np.arange(2004, 2009)
    +    heights = [7900, 8100, 7900, 6900, 2800]
    +    box_colors = [
    +        (0.8, 0.2, 0.2),
    +        (0.2, 0.8, 0.2),
    +        (0.2, 0.2, 0.8),
    +        (0.7, 0.5, 0.8),
    +        (0.3, 0.8, 0.7),
    +    ]
    +
    +    for year, h, bc in zip(years, heights, box_colors):
    +        bbox0 = Bbox.from_extents(year - 0.4, 0., year + 0.4, h)
    +        bbox = TransformedBbox(bbox0, ax.transData)
    +        ax.add_artist(RibbonBoxImage(ax, bbox, bc, interpolation="bicubic"))
    +        ax.annotate(str(h), (year, h), va="bottom", ha="center")
    +
    +    ax.set_xlim(years[0] - 0.5, years[-1] + 0.5)
    +    ax.set_ylim(0, 10000)
    +
    +    background_gradient = np.zeros((2, 2, 4))
    +    background_gradient[:, :, :3] = [1, 1, 0]
    +    background_gradient[:, :, 3] = [[0.1, 0.3], [0.3, 0.5]]  # alpha channel
    +    ax.imshow(background_gradient, interpolation="bicubic", zorder=0.1,
    +              extent=(0, 1, 0, 1), transform=ax.transAxes)
    +
    +    plt.show()
    +
    +
    +main()
    diff --git a/galleries/examples/misc/fig_x.py b/galleries/examples/misc/fig_x.py
    new file mode 100644
    index 000000000000..593a7e8f8aa5
    --- /dev/null
    +++ b/galleries/examples/misc/fig_x.py
    @@ -0,0 +1,30 @@
    +"""
    +==============================
    +Add lines directly to a figure
    +==============================
    +
    +You can add artists such as a `.Line2D` directly to a figure. This is
    +typically useful for visual structuring.
    +
    +.. redirect-from:: /gallery/pyplots/fig_x
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.lines as lines
    +
    +fig, axs = plt.subplots(2, 2, gridspec_kw={'hspace': 0.4, 'wspace': 0.4})
    +fig.add_artist(lines.Line2D([0, 1], [0.47, 0.47], linewidth=3))
    +fig.add_artist(lines.Line2D([0.5, 0.5], [1, 0], linewidth=3))
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.figure`
    +#    - `matplotlib.lines`
    +#    - `matplotlib.lines.Line2D`
    diff --git a/examples/pylab_examples/fill_spiral.py b/galleries/examples/misc/fill_spiral.py
    similarity index 91%
    rename from examples/pylab_examples/fill_spiral.py
    rename to galleries/examples/misc/fill_spiral.py
    index 9dd2e4c07f8f..35b06886e985 100644
    --- a/examples/pylab_examples/fill_spiral.py
    +++ b/galleries/examples/misc/fill_spiral.py
    @@ -1,3 +1,9 @@
    +"""
    +===========
    +Fill spiral
    +===========
    +
    +"""
     import matplotlib.pyplot as plt
     import numpy as np
     
    diff --git a/examples/pylab_examples/findobj_demo.py b/galleries/examples/misc/findobj_demo.py
    similarity index 95%
    rename from examples/pylab_examples/findobj_demo.py
    rename to galleries/examples/misc/findobj_demo.py
    index 17bd6dd83b79..c953040f8aa3 100644
    --- a/examples/pylab_examples/findobj_demo.py
    +++ b/galleries/examples/misc/findobj_demo.py
    @@ -1,8 +1,13 @@
     """
    +============
    +Findobj Demo
    +============
    +
     Recursively find all objects that match some criteria
     """
    -import numpy as np
     import matplotlib.pyplot as plt
    +import numpy as np
    +
     import matplotlib.text as text
     
     a = np.arange(0, 3, .02)
    @@ -25,6 +30,7 @@
     def myfunc(x):
         return hasattr(x, 'set_color') and not hasattr(x, 'set_facecolor')
     
    +
     for o in fig.findobj(myfunc):
         o.set_color('blue')
     
    diff --git a/galleries/examples/misc/font_indexing.py b/galleries/examples/misc/font_indexing.py
    new file mode 100644
    index 000000000000..31388737bcae
    --- /dev/null
    +++ b/galleries/examples/misc/font_indexing.py
    @@ -0,0 +1,36 @@
    +"""
    +=============
    +Font indexing
    +=============
    +
    +This example shows how the font tables relate to one another.
    +"""
    +
    +import os
    +
    +import matplotlib
    +from matplotlib.ft2font import FT2Font, Kerning
    +
    +font = FT2Font(
    +    os.path.join(matplotlib.get_data_path(), 'fonts/ttf/DejaVuSans.ttf'))
    +font.set_charmap(0)
    +
    +codes = font.get_charmap().items()
    +
    +# make a charname to charcode and glyphind dictionary
    +coded = {}
    +glyphd = {}
    +for ccode, glyphind in codes:
    +    name = font.get_glyph_name(glyphind)
    +    coded[name] = ccode
    +    glyphd[name] = glyphind
    +    # print(glyphind, ccode, hex(int(ccode)), name)
    +
    +code = coded['A']
    +glyph = font.load_char(code)
    +print(glyph.bbox)
    +print(glyphd['A'], glyphd['V'], coded['A'], coded['V'])
    +print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.DEFAULT))
    +print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNFITTED))
    +print('AV', font.get_kerning(glyphd['A'], glyphd['V'], Kerning.UNSCALED))
    +print('AT', font.get_kerning(glyphd['A'], glyphd['T'], Kerning.UNSCALED))
    diff --git a/galleries/examples/misc/ftface_props.py b/galleries/examples/misc/ftface_props.py
    new file mode 100644
    index 000000000000..ec26dff5bf6a
    --- /dev/null
    +++ b/galleries/examples/misc/ftface_props.py
    @@ -0,0 +1,56 @@
    +"""
    +===============
    +Font properties
    +===============
    +
    +This example lists the attributes of an `.FT2Font` object, which describe
    +global font properties.  For individual character metrics, use the `.Glyph`
    +object, as returned by `.load_char`.
    +"""
    +
    +import os
    +
    +import matplotlib
    +import matplotlib.ft2font as ft
    +
    +font = ft.FT2Font(
    +    # Use a font shipped with Matplotlib.
    +    os.path.join(matplotlib.get_data_path(),
    +                 'fonts/ttf/DejaVuSans-Oblique.ttf'))
    +
    +print('Num instances:  ', font.num_named_instances)  # number of named instances in file
    +print('Num faces:      ', font.num_faces)            # number of faces in file
    +print('Num glyphs:     ', font.num_glyphs)           # number of glyphs in the face
    +print('Family name:    ', font.family_name)          # face family name
    +print('Style name:     ', font.style_name)           # face style name
    +print('PS name:        ', font.postscript_name)      # the postscript name
    +print('Num fixed:      ', font.num_fixed_sizes)      # number of embedded bitmaps
    +
    +# the following are only available if face.scalable
    +if font.scalable:
    +    # the face global bounding box (xmin, ymin, xmax, ymax)
    +    print('Bbox:               ', font.bbox)
    +    # number of font units covered by the EM
    +    print('EM:                 ', font.units_per_EM)
    +    # the ascender in 26.6 units
    +    print('Ascender:           ', font.ascender)
    +    # the descender in 26.6 units
    +    print('Descender:          ', font.descender)
    +    # the height in 26.6 units
    +    print('Height:             ', font.height)
    +    # maximum horizontal cursor advance
    +    print('Max adv width:      ', font.max_advance_width)
    +    # same for vertical layout
    +    print('Max adv height:     ', font.max_advance_height)
    +    # vertical position of the underline bar
    +    print('Underline pos:      ', font.underline_position)
    +    # vertical thickness of the underline
    +    print('Underline thickness:', font.underline_thickness)
    +
    +for flag in ft.StyleFlags:
    +    name = flag.name.replace('_', ' ').title() + ':'
    +    print(f"{name:17}", flag in font.style_flags)
    +
    +for flag in ft.FaceFlags:
    +    name = flag.name.replace('_', ' ').title() + ':'
    +    print(f"{name:17}", flag in font.face_flags)
    diff --git a/galleries/examples/misc/histogram_path.py b/galleries/examples/misc/histogram_path.py
    new file mode 100644
    index 000000000000..d35e291aa8ce
    --- /dev/null
    +++ b/galleries/examples/misc/histogram_path.py
    @@ -0,0 +1,100 @@
    +"""
    +========================================================
    +Building histograms using Rectangles and PolyCollections
    +========================================================
    +
    +Using a path patch to draw rectangles.
    +
    +The technique of using lots of `.Rectangle` instances, or the faster method of
    +using `.PolyCollection`, were implemented before we had proper paths with
    +moveto, lineto, closepoly, etc. in Matplotlib.  Now that we have them, we can
    +draw collections of regularly shaped objects with homogeneous properties more
    +efficiently with a PathCollection. This example makes a histogram -- it's more
    +work to set up the vertex arrays at the outset, but it should be much faster
    +for large numbers of objects.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.patches as patches
    +import matplotlib.path as path
    +
    +np.random.seed(19680801)  # Fixing random state for reproducibility
    +
    +# histogram our data with numpy
    +data = np.random.randn(1000)
    +n, bins = np.histogram(data, 50)
    +
    +# get the corners of the rectangles for the histogram
    +left = bins[:-1]
    +right = bins[1:]
    +bottom = np.zeros(len(left))
    +top = bottom + n
    +
    +# we need a (numrects x numsides x 2) numpy array for the path helper
    +# function to build a compound path
    +XY = np.array([[left, left, right, right], [bottom, top, top, bottom]]).T
    +
    +# get the Path object
    +barpath = path.Path.make_compound_path_from_polys(XY)
    +
    +# make a patch out of it, don't add a margin at y=0
    +patch = patches.PathPatch(barpath)
    +patch.sticky_edges.y[:] = [0]
    +
    +fig, ax = plt.subplots()
    +ax.add_patch(patch)
    +ax.autoscale_view()
    +plt.show()
    +
    +# %%
    +# Instead of creating a three-dimensional array and using
    +# `~.path.Path.make_compound_path_from_polys`, we could as well create the
    +# compound path directly using vertices and codes as shown below
    +
    +nrects = len(left)
    +nverts = nrects*(1+3+1)
    +verts = np.zeros((nverts, 2))
    +codes = np.ones(nverts, int) * path.Path.LINETO
    +codes[0::5] = path.Path.MOVETO
    +codes[4::5] = path.Path.CLOSEPOLY
    +verts[0::5, 0] = left
    +verts[0::5, 1] = bottom
    +verts[1::5, 0] = left
    +verts[1::5, 1] = top
    +verts[2::5, 0] = right
    +verts[2::5, 1] = top
    +verts[3::5, 0] = right
    +verts[3::5, 1] = bottom
    +
    +barpath = path.Path(verts, codes)
    +
    +# make a patch out of it, don't add a margin at y=0
    +patch = patches.PathPatch(barpath)
    +patch.sticky_edges.y[:] = [0]
    +
    +fig, ax = plt.subplots()
    +ax.add_patch(patch)
    +ax.autoscale_view()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.path.Path.make_compound_path_from_polys`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.collections.PathCollection`
    +#
    +#    This example shows an alternative to
    +#
    +#    - `matplotlib.collections.PolyCollection`
    +#    - `matplotlib.axes.Axes.hist`
    diff --git a/galleries/examples/misc/hyperlinks_sgskip.py b/galleries/examples/misc/hyperlinks_sgskip.py
    new file mode 100644
    index 000000000000..26421c941573
    --- /dev/null
    +++ b/galleries/examples/misc/hyperlinks_sgskip.py
    @@ -0,0 +1,37 @@
    +"""
    +==========
    +Hyperlinks
    +==========
    +
    +This example demonstrates how to set a hyperlinks on various kinds of elements.
    +
    +This currently only works with the SVG backend.
    +
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +
    +fig = plt.figure()
    +s = plt.scatter([1, 2, 3], [4, 5, 6])
    +s.set_urls(['https://www.bbc.com/news', 'https://www.google.com/', None])
    +fig.savefig('scatter.svg')
    +
    +# %%
    +
    +fig = plt.figure()
    +delta = 0.025
    +x = y = np.arange(-3.0, 3.0, delta)
    +X, Y = np.meshgrid(x, y)
    +Z1 = np.exp(-X**2 - Y**2)
    +Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
    +Z = (Z1 - Z2) * 2
    +
    +im = plt.imshow(Z, interpolation='bilinear', cmap="gray",
    +                origin='lower', extent=(-3, 3, -3, 3))
    +
    +im.set_url('https://www.google.com/')
    +fig.savefig('image.svg')
    diff --git a/galleries/examples/misc/image_thumbnail_sgskip.py b/galleries/examples/misc/image_thumbnail_sgskip.py
    new file mode 100644
    index 000000000000..e361d3bf53ab
    --- /dev/null
    +++ b/galleries/examples/misc/image_thumbnail_sgskip.py
    @@ -0,0 +1,32 @@
    +"""
    +===============
    +Image thumbnail
    +===============
    +
    +You can use Matplotlib to generate thumbnails from existing images.
    +Matplotlib relies on Pillow_ for reading images, and thus supports all formats
    +supported by Pillow.
    +
    +.. _Pillow: https://python-pillow.github.io
    +"""
    +
    +from argparse import ArgumentParser
    +from pathlib import Path
    +import sys
    +
    +import matplotlib.image as image
    +
    +parser = ArgumentParser(
    +    description="Build thumbnails of all images in a directory.")
    +parser.add_argument("imagedir", type=Path)
    +args = parser.parse_args()
    +if not args.imagedir.is_dir():
    +    sys.exit(f"Could not find input directory {args.imagedir}")
    +
    +outdir = Path("thumbs")
    +outdir.mkdir(parents=True, exist_ok=True)
    +
    +for path in args.imagedir.glob("*.png"):
    +    outpath = outdir / path.name
    +    fig = image.thumbnail(path, outpath, scale=0.15)
    +    print(f"saved thumbnail of {path} to {outpath}")
    diff --git a/galleries/examples/misc/keyword_plotting.py b/galleries/examples/misc/keyword_plotting.py
    new file mode 100644
    index 000000000000..e8a2d944fe0d
    --- /dev/null
    +++ b/galleries/examples/misc/keyword_plotting.py
    @@ -0,0 +1,30 @@
    +"""
    +======================
    +Plotting with keywords
    +======================
    +
    +Some data structures, like dict, `structured numpy array
    +`_
    +or `pandas.DataFrame` provide access to labelled data via string index access
    +``data[key]``.
    +
    +For these data types, Matplotlib supports passing the whole datastructure via the
    +``data`` keyword argument, and using the string names as plot function parameters,
    +where you'd normally pass in your data.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +data = {'a': np.arange(50),
    +        'c': np.random.randint(0, 50, 50),
    +        'd': np.random.randn(50)}
    +data['b'] = data['a'] + 10 * np.random.randn(50)
    +data['d'] = np.abs(data['d']) * 100
    +
    +fig, ax = plt.subplots()
    +ax.scatter('a', 'b', c='c', s='d', data=data)
    +ax.set(xlabel='entry a', ylabel='entry b')
    +plt.show()
    diff --git a/galleries/examples/misc/logos2.py b/galleries/examples/misc/logos2.py
    new file mode 100644
    index 000000000000..aca348474e7b
    --- /dev/null
    +++ b/galleries/examples/misc/logos2.py
    @@ -0,0 +1,158 @@
    +"""
    +===============
    +Matplotlib logo
    +===============
    +
    +This example generates the current matplotlib logo.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cm as cm
    +import matplotlib.font_manager
    +from matplotlib.patches import PathPatch, Rectangle
    +from matplotlib.text import TextPath
    +import matplotlib.transforms as mtrans
    +
    +MPL_BLUE = '#11557c'
    +
    +
    +def get_font_properties():
    +    # The original font is Calibri, if that is not installed, we fall back
    +    # to Carlito, which is metrically equivalent.
    +    if 'Calibri' in matplotlib.font_manager.findfont('Calibri:bold'):
    +        return matplotlib.font_manager.FontProperties(family='Calibri',
    +                                                      weight='bold')
    +    if 'Carlito' in matplotlib.font_manager.findfont('Carlito:bold'):
    +        print('Original font not found. Falling back to Carlito. '
    +              'The logo text will not be in the correct font.')
    +        return matplotlib.font_manager.FontProperties(family='Carlito',
    +                                                      weight='bold')
    +    print('Original font not found. '
    +          'The logo text will not be in the correct font.')
    +    return None
    +
    +
    +def create_icon_axes(fig, ax_position, lw_bars, lw_grid, lw_border, rgrid):
    +    """
    +    Create a polar Axes containing the matplotlib radar plot.
    +
    +    Parameters
    +    ----------
    +    fig : matplotlib.figure.Figure
    +        The figure to draw into.
    +    ax_position : (float, float, float, float)
    +        The position of the created Axes in figure coordinates as
    +        (x, y, width, height).
    +    lw_bars : float
    +        The linewidth of the bars.
    +    lw_grid : float
    +        The linewidth of the grid.
    +    lw_border : float
    +        The linewidth of the Axes border.
    +    rgrid : array-like
    +        Positions of the radial grid.
    +
    +    Returns
    +    -------
    +    ax : matplotlib.axes.Axes
    +        The created Axes.
    +    """
    +    with plt.rc_context({'axes.edgecolor': MPL_BLUE,
    +                         'axes.linewidth': lw_border}):
    +        ax = fig.add_axes(ax_position, projection='polar')
    +        ax.set_axisbelow(True)
    +
    +        N = 7
    +        arc = 2. * np.pi
    +        theta = np.arange(0.0, arc, arc / N)
    +        radii = np.array([2, 6, 8, 7, 4, 5, 8])
    +        width = np.pi / 4 * np.array([0.4, 0.4, 0.6, 0.8, 0.2, 0.5, 0.3])
    +        bars = ax.bar(theta, radii, width=width, bottom=0.0, align='edge',
    +                      edgecolor='0.3', lw=lw_bars)
    +        for r, bar in zip(radii, bars):
    +            color = *cm.jet(r / 10.)[:3], 0.6  # color from jet with alpha=0.6
    +            bar.set_facecolor(color)
    +
    +        ax.tick_params(labelbottom=False, labeltop=False,
    +                       labelleft=False, labelright=False)
    +
    +        ax.grid(lw=lw_grid, color='0.9')
    +        ax.set_rmax(9)
    +        ax.set_yticks(rgrid)
    +
    +        # the actual visible background - extends a bit beyond the axis
    +        ax.add_patch(Rectangle((0, 0), arc, 9.58,
    +                               facecolor='white', zorder=0,
    +                               clip_on=False, in_layout=False))
    +        return ax
    +
    +
    +def create_text_axes(fig, height_px):
    +    """Create an Axes in *fig* that contains 'matplotlib' as Text."""
    +    ax = fig.add_axes((0, 0, 1, 1))
    +    ax.set_aspect("equal")
    +    ax.set_axis_off()
    +
    +    path = TextPath((0, 0), "matplotlib", size=height_px * 0.8,
    +                    prop=get_font_properties())
    +
    +    angle = 4.25  # degrees
    +    trans = mtrans.Affine2D().skew_deg(angle, 0)
    +
    +    patch = PathPatch(path, transform=trans + ax.transData, color=MPL_BLUE,
    +                      lw=0)
    +    ax.add_patch(patch)
    +    ax.autoscale()
    +
    +
    +def make_logo(height_px, lw_bars, lw_grid, lw_border, rgrid, with_text=False):
    +    """
    +    Create a full figure with the Matplotlib logo.
    +
    +    Parameters
    +    ----------
    +    height_px : int
    +        Height of the figure in pixel.
    +    lw_bars : float
    +        The linewidth of the bar border.
    +    lw_grid : float
    +        The linewidth of the grid.
    +    lw_border : float
    +        The linewidth of icon border.
    +    rgrid : sequence of float
    +        The radial grid positions.
    +    with_text : bool
    +        Whether to draw only the icon or to include 'matplotlib' as text.
    +    """
    +    dpi = 100
    +    height = height_px / dpi
    +    figsize = (5 * height, height) if with_text else (height, height)
    +    fig = plt.figure(figsize=figsize, dpi=dpi)
    +    fig.patch.set_alpha(0)
    +
    +    if with_text:
    +        create_text_axes(fig, height_px)
    +    ax_pos = (0.535, 0.12, .17, 0.75) if with_text else (0.03, 0.03, .94, .94)
    +    ax = create_icon_axes(fig, ax_pos, lw_bars, lw_grid, lw_border, rgrid)
    +
    +    return fig, ax
    +
    +# %%
    +# A large logo:
    +
    +make_logo(height_px=110, lw_bars=0.7, lw_grid=0.5, lw_border=1,
    +          rgrid=[1, 3, 5, 7])
    +
    +# %%
    +# A small 32px logo:
    +
    +make_logo(height_px=32, lw_bars=0.3, lw_grid=0.3, lw_border=0.3, rgrid=[5])
    +
    +# %%
    +# A large logo including text, as used on the matplotlib website.
    +
    +make_logo(height_px=110, lw_bars=0.7, lw_grid=0.5, lw_border=1,
    +          rgrid=[1, 3, 5, 7], with_text=True)
    +plt.show()
    diff --git a/examples/pylab_examples/multipage_pdf.py b/galleries/examples/misc/multipage_pdf.py
    similarity index 79%
    rename from examples/pylab_examples/multipage_pdf.py
    rename to galleries/examples/misc/multipage_pdf.py
    index d6f58f330f93..e04cd0431f87 100644
    --- a/examples/pylab_examples/multipage_pdf.py
    +++ b/galleries/examples/misc/multipage_pdf.py
    @@ -1,12 +1,19 @@
     """
    +=============
    +Multipage PDF
    +=============
    +
     This is a demo of creating a pdf file with several pages,
     as well as adding metadata and annotations to pdf files.
    +
     """
     
     import datetime
    +
    +import matplotlib.pyplot as plt
     import numpy as np
    +
     from matplotlib.backends.backend_pdf import PdfPages
    -import matplotlib.pyplot as plt
     
     # Create the PdfPages object to which we will save the pages:
     # The with statement makes sure that the PdfPages object is closed properly at
    @@ -18,20 +25,19 @@
         pdf.savefig()  # saves the current figure into a pdf page
         plt.close()
     
    -    # if LaTeX is not installed or error caught, change to `usetex=False`
    -    plt.rc('text', usetex=True)
    +    # if LaTeX is not installed or error caught, change to `False`
    +    plt.rcParams['text.usetex'] = True
         plt.figure(figsize=(8, 6))
         x = np.arange(0, 5, 0.1)
         plt.plot(x, np.sin(x), 'b-')
         plt.title('Page Two')
    -    pdf.attach_note("plot of sin(x)")  # you can add a pdf note to
    -                                       # attach metadata to a page
    +    pdf.attach_note("plot of sin(x)")  # attach metadata (as pdf note) to page
         pdf.savefig()
         plt.close()
     
    -    plt.rc('text', usetex=False)
    +    plt.rcParams['text.usetex'] = False
         fig = plt.figure(figsize=(4, 5))
    -    plt.plot(x, x*x, 'ko')
    +    plt.plot(x, x ** 2, 'ko')
         plt.title('Page Three')
         pdf.savefig(fig)  # or you can pass a Figure object to pdf.savefig
         plt.close()
    @@ -39,7 +45,7 @@
         # We can also set the file's metadata via the PdfPages object:
         d = pdf.infodict()
         d['Title'] = 'Multipage PDF Example'
    -    d['Author'] = u'Jouni K. Sepp\xe4nen'
    +    d['Author'] = 'Jouni K. Sepp\xe4nen'
         d['Subject'] = 'How to create a multipage pdf file and set its metadata'
         d['Keywords'] = 'PdfPages multipage keywords author title subject'
         d['CreationDate'] = datetime.datetime(2009, 11, 13)
    diff --git a/galleries/examples/misc/multiprocess_sgskip.py b/galleries/examples/misc/multiprocess_sgskip.py
    new file mode 100644
    index 000000000000..5951dab1b895
    --- /dev/null
    +++ b/galleries/examples/misc/multiprocess_sgskip.py
    @@ -0,0 +1,106 @@
    +"""
    +===============
    +Multiprocessing
    +===============
    +
    +Demo of using multiprocessing for generating data in one process and
    +plotting in another.
    +
    +Written by Robert Cimrman
    +"""
    +
    +import multiprocessing as mp
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# %%
    +#
    +# Processing Class
    +# ================
    +#
    +# This class plots data it receives from a pipe.
    +#
    +
    +
    +class ProcessPlotter:
    +    def __init__(self):
    +        self.x = []
    +        self.y = []
    +
    +    def terminate(self):
    +        plt.close('all')
    +
    +    def call_back(self):
    +        while self.pipe.poll():
    +            command = self.pipe.recv()
    +            if command is None:
    +                self.terminate()
    +                return False
    +            else:
    +                self.x.append(command[0])
    +                self.y.append(command[1])
    +                self.ax.plot(self.x, self.y, 'ro')
    +        self.fig.canvas.draw()
    +        return True
    +
    +    def __call__(self, pipe):
    +        print('starting plotter...')
    +
    +        self.pipe = pipe
    +        self.fig, self.ax = plt.subplots()
    +        timer = self.fig.canvas.new_timer(interval=1000)
    +        timer.add_callback(self.call_back)
    +        timer.start()
    +
    +        print('...done')
    +        plt.show()
    +
    +# %%
    +#
    +# Plotting class
    +# ==============
    +#
    +# This class uses multiprocessing to spawn a process to run code from the
    +# class above. When initialized, it creates a pipe and an instance of
    +# ``ProcessPlotter`` which will be run in a separate process.
    +#
    +# When run from the command line, the parent process sends data to the spawned
    +# process which is then plotted via the callback function specified in
    +# ``ProcessPlotter:__call__``.
    +#
    +
    +
    +class NBPlot:
    +    def __init__(self):
    +        self.plot_pipe, plotter_pipe = mp.Pipe()
    +        self.plotter = ProcessPlotter()
    +        self.plot_process = mp.Process(
    +            target=self.plotter, args=(plotter_pipe,), daemon=True)
    +        self.plot_process.start()
    +
    +    def plot(self, finished=False):
    +        send = self.plot_pipe.send
    +        if finished:
    +            send(None)
    +        else:
    +            data = np.random.random(2)
    +            send(data)
    +
    +
    +def main():
    +    pl = NBPlot()
    +    for _ in range(10):
    +        pl.plot()
    +        time.sleep(0.5)
    +    pl.plot(finished=True)
    +
    +
    +if __name__ == '__main__':
    +    if plt.get_backend() == "MacOSX":
    +        mp.set_start_method("forkserver")
    +    main()
    diff --git a/galleries/examples/misc/packed_bubbles.py b/galleries/examples/misc/packed_bubbles.py
    new file mode 100644
    index 000000000000..b1f9448e4c81
    --- /dev/null
    +++ b/galleries/examples/misc/packed_bubbles.py
    @@ -0,0 +1,170 @@
    +"""
    +===================
    +Packed-bubble chart
    +===================
    +
    +Create a packed-bubble chart to represent scalar data.
    +The presented algorithm tries to move all bubbles as close to the center of
    +mass as possible while avoiding some collisions by moving around colliding
    +objects. In this example we plot the market share of different desktop
    +browsers.
    +(source: https://gs.statcounter.com/browser-market-share/desktop/worldwidev)
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +browser_market_share = {
    +    'browsers': ['firefox', 'chrome', 'safari', 'edge', 'ie', 'opera'],
    +    'market_share': [8.61, 69.55, 8.36, 4.12, 2.76, 2.43],
    +    'color': ['#5A69AF', '#579E65', '#F9C784', '#FC944A', '#F24C00', '#00B825']
    +}
    +
    +
    +class BubbleChart:
    +    def __init__(self, area, bubble_spacing=0):
    +        """
    +        Setup for bubble collapse.
    +
    +        Parameters
    +        ----------
    +        area : array-like
    +            Area of the bubbles.
    +        bubble_spacing : float, default: 0
    +            Minimal spacing between bubbles after collapsing.
    +
    +        Notes
    +        -----
    +        If "area" is sorted, the results might look weird.
    +        """
    +        area = np.asarray(area)
    +        r = np.sqrt(area / np.pi)
    +
    +        self.bubble_spacing = bubble_spacing
    +        self.bubbles = np.ones((len(area), 4))
    +        self.bubbles[:, 2] = r
    +        self.bubbles[:, 3] = area
    +        self.maxstep = 2 * self.bubbles[:, 2].max() + self.bubble_spacing
    +        self.step_dist = self.maxstep / 2
    +
    +        # calculate initial grid layout for bubbles
    +        length = np.ceil(np.sqrt(len(self.bubbles)))
    +        grid = np.arange(length) * self.maxstep
    +        gx, gy = np.meshgrid(grid, grid)
    +        self.bubbles[:, 0] = gx.flatten()[:len(self.bubbles)]
    +        self.bubbles[:, 1] = gy.flatten()[:len(self.bubbles)]
    +
    +        self.com = self.center_of_mass()
    +
    +    def center_of_mass(self):
    +        return np.average(
    +            self.bubbles[:, :2], axis=0, weights=self.bubbles[:, 3]
    +        )
    +
    +    def center_distance(self, bubble, bubbles):
    +        return np.hypot(bubble[0] - bubbles[:, 0],
    +                        bubble[1] - bubbles[:, 1])
    +
    +    def outline_distance(self, bubble, bubbles):
    +        center_distance = self.center_distance(bubble, bubbles)
    +        return center_distance - bubble[2] - \
    +            bubbles[:, 2] - self.bubble_spacing
    +
    +    def check_collisions(self, bubble, bubbles):
    +        distance = self.outline_distance(bubble, bubbles)
    +        return len(distance[distance < 0])
    +
    +    def collides_with(self, bubble, bubbles):
    +        distance = self.outline_distance(bubble, bubbles)
    +        return np.argmin(distance, keepdims=True)
    +
    +    def collapse(self, n_iterations=50):
    +        """
    +        Move bubbles to the center of mass.
    +
    +        Parameters
    +        ----------
    +        n_iterations : int, default: 50
    +            Number of moves to perform.
    +        """
    +        for _i in range(n_iterations):
    +            moves = 0
    +            for i in range(len(self.bubbles)):
    +                rest_bub = np.delete(self.bubbles, i, 0)
    +                # try to move directly towards the center of mass
    +                # direction vector from bubble to the center of mass
    +                dir_vec = self.com - self.bubbles[i, :2]
    +
    +                # shorten direction vector to have length of 1
    +                dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
    +
    +                # calculate new bubble position
    +                new_point = self.bubbles[i, :2] + dir_vec * self.step_dist
    +                new_bubble = np.append(new_point, self.bubbles[i, 2:4])
    +
    +                # check whether new bubble collides with other bubbles
    +                if not self.check_collisions(new_bubble, rest_bub):
    +                    self.bubbles[i, :] = new_bubble
    +                    self.com = self.center_of_mass()
    +                    moves += 1
    +                else:
    +                    # try to move around a bubble that you collide with
    +                    # find colliding bubble
    +                    for colliding in self.collides_with(new_bubble, rest_bub):
    +                        # calculate direction vector
    +                        dir_vec = rest_bub[colliding, :2] - self.bubbles[i, :2]
    +                        dir_vec = dir_vec / np.sqrt(dir_vec.dot(dir_vec))
    +                        # calculate orthogonal vector
    +                        orth = np.array([dir_vec[1], -dir_vec[0]])
    +                        # test which direction to go
    +                        new_point1 = (self.bubbles[i, :2] + orth *
    +                                      self.step_dist)
    +                        new_point2 = (self.bubbles[i, :2] - orth *
    +                                      self.step_dist)
    +                        dist1 = self.center_distance(
    +                            self.com, np.array([new_point1]))
    +                        dist2 = self.center_distance(
    +                            self.com, np.array([new_point2]))
    +                        new_point = new_point1 if dist1 < dist2 else new_point2
    +                        new_bubble = np.append(new_point, self.bubbles[i, 2:4])
    +                        if not self.check_collisions(new_bubble, rest_bub):
    +                            self.bubbles[i, :] = new_bubble
    +                            self.com = self.center_of_mass()
    +
    +            if moves / len(self.bubbles) < 0.1:
    +                self.step_dist = self.step_dist / 2
    +
    +    def plot(self, ax, labels, colors):
    +        """
    +        Draw the bubble plot.
    +
    +        Parameters
    +        ----------
    +        ax : matplotlib.axes.Axes
    +        labels : list
    +            Labels of the bubbles.
    +        colors : list
    +            Colors of the bubbles.
    +        """
    +        for i in range(len(self.bubbles)):
    +            circ = plt.Circle(
    +                self.bubbles[i, :2], self.bubbles[i, 2], color=colors[i])
    +            ax.add_patch(circ)
    +            ax.text(*self.bubbles[i, :2], labels[i],
    +                    horizontalalignment='center', verticalalignment='center')
    +
    +
    +bubble_chart = BubbleChart(area=browser_market_share['market_share'],
    +                           bubble_spacing=0.1)
    +
    +bubble_chart.collapse()
    +
    +fig, ax = plt.subplots(subplot_kw=dict(aspect="equal"))
    +bubble_chart.plot(
    +    ax, browser_market_share['browsers'], browser_market_share['color'])
    +ax.axis("off")
    +ax.relim()
    +ax.autoscale_view()
    +ax.set_title('Browser market share')
    +
    +plt.show()
    diff --git a/galleries/examples/misc/patheffect_demo.py b/galleries/examples/misc/patheffect_demo.py
    new file mode 100644
    index 000000000000..aa424959cbff
    --- /dev/null
    +++ b/galleries/examples/misc/patheffect_demo.py
    @@ -0,0 +1,43 @@
    +"""
    +===============
    +Patheffect Demo
    +===============
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import patheffects
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(8, 3))
    +ax1.imshow([[1, 2], [2, 3]])
    +txt = ax1.annotate("test", (1., 1.), (0., 0),
    +                   arrowprops=dict(arrowstyle="->",
    +                                   connectionstyle="angle3", lw=2),
    +                   size=20, ha="center",
    +                   path_effects=[patheffects.withStroke(linewidth=3,
    +                                                        foreground="w")])
    +txt.arrow_patch.set_path_effects([
    +    patheffects.Stroke(linewidth=5, foreground="w"),
    +    patheffects.Normal()])
    +
    +pe = [patheffects.withStroke(linewidth=3,
    +                             foreground="w")]
    +ax1.grid(True, linestyle="-", path_effects=pe)
    +
    +arr = np.arange(25).reshape((5, 5))
    +ax2.imshow(arr)
    +cntr = ax2.contour(arr, colors="k")
    +
    +cntr.set(path_effects=[patheffects.withStroke(linewidth=3, foreground="w")])
    +
    +clbls = ax2.clabel(cntr, fmt="%2.0f", use_clabeltext=True)
    +plt.setp(clbls, path_effects=[
    +    patheffects.withStroke(linewidth=3, foreground="w")])
    +
    +# shadow as a path effect
    +p1, = ax3.plot([0, 1], [0, 1])
    +leg = ax3.legend([p1], ["Line 1"], fancybox=True, loc='upper left')
    +leg.legendPatch.set_path_effects([patheffects.withSimplePatchShadow()])
    +
    +plt.show()
    diff --git a/galleries/examples/misc/print_stdout_sgskip.py b/galleries/examples/misc/print_stdout_sgskip.py
    new file mode 100644
    index 000000000000..9c9848a73d9c
    --- /dev/null
    +++ b/galleries/examples/misc/print_stdout_sgskip.py
    @@ -0,0 +1,20 @@
    +"""
    +=====================
    +Print image to stdout
    +=====================
    +
    +print png to standard out
    +
    +usage: python print_stdout.py > somefile.png
    +
    +"""
    +
    +import sys
    +
    +import matplotlib
    +
    +matplotlib.use('Agg')
    +import matplotlib.pyplot as plt
    +
    +plt.plot([1, 2, 3])
    +plt.savefig(sys.stdout.buffer)
    diff --git a/galleries/examples/misc/rasterization_demo.py b/galleries/examples/misc/rasterization_demo.py
    new file mode 100644
    index 000000000000..ce0cf02dfac2
    --- /dev/null
    +++ b/galleries/examples/misc/rasterization_demo.py
    @@ -0,0 +1,94 @@
    +"""
    +=================================
    +Rasterization for vector graphics
    +=================================
    +
    +Rasterization converts vector graphics into a raster image (pixels). It can
    +speed up rendering and produce smaller files for large data sets, but comes
    +at the cost of a fixed resolution.
    +
    +Whether rasterization should be used can be specified per artist.  This can be
    +useful to reduce the file size of large artists, while maintaining the
    +advantages of vector graphics for other artists such as the Axes
    +and text.  For instance a complicated `~.Axes.pcolormesh` or
    +`~.Axes.contourf` can be made significantly simpler by rasterizing.
    +Setting rasterization only affects vector backends such as PDF, SVG, or PS.
    +
    +Rasterization is disabled by default. There are two ways to enable it, which
    +can also be combined:
    +
    +- Set `~.Artist.set_rasterized` on individual artists, or use the keyword
    +  argument *rasterized* when creating the artist.
    +- Set `.Axes.set_rasterization_zorder` to rasterize all artists with a zorder
    +  less than the given value.
    +
    +The storage size and the resolution of the rasterized artist is determined by
    +its physical size and the value of the ``dpi`` parameter passed to
    +`~.Figure.savefig`.
    +
    +.. note::
    +
    +    The image of this example shown in the HTML documentation is not a vector
    +    graphic. Therefore, it cannot illustrate the rasterization effect. Please
    +    run this example locally and check the generated graphics files.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +d = np.arange(100).reshape(10, 10)  # the values to be color-mapped
    +x, y = np.meshgrid(np.arange(11), np.arange(11))
    +
    +theta = 0.25*np.pi
    +xx = x*np.cos(theta) - y*np.sin(theta)  # rotate x by -theta
    +yy = x*np.sin(theta) + y*np.cos(theta)  # rotate y by -theta
    +
    +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, layout="constrained")
    +
    +# pcolormesh without rasterization
    +ax1.set_aspect(1)
    +ax1.pcolormesh(xx, yy, d)
    +ax1.set_title("No Rasterization")
    +
    +# pcolormesh with rasterization; enabled by keyword argument
    +ax2.set_aspect(1)
    +ax2.set_title("Rasterization")
    +ax2.pcolormesh(xx, yy, d, rasterized=True)
    +
    +# pcolormesh with an overlaid text without rasterization
    +ax3.set_aspect(1)
    +ax3.pcolormesh(xx, yy, d)
    +ax3.text(0.5, 0.5, "Text", alpha=0.2,
    +         va="center", ha="center", size=50, transform=ax3.transAxes)
    +ax3.set_title("No Rasterization")
    +
    +# pcolormesh with an overlaid text without rasterization; enabled by zorder.
    +# Setting the rasterization zorder threshold to 0 and a negative zorder on the
    +# pcolormesh rasterizes it. All artists have a non-negative zorder by default,
    +# so they (e.g. the text here) are not affected.
    +ax4.set_aspect(1)
    +m = ax4.pcolormesh(xx, yy, d, zorder=-10)
    +ax4.text(0.5, 0.5, "Text", alpha=0.2,
    +         va="center", ha="center", size=50, transform=ax4.transAxes)
    +ax4.set_rasterization_zorder(0)
    +ax4.set_title("Rasterization z$<-10$")
    +
    +# Save files in pdf and eps format
    +plt.savefig("test_rasterization.pdf", dpi=150)
    +plt.savefig("test_rasterization.eps", dpi=150)
    +
    +if not plt.rcParams["text.usetex"]:
    +    plt.savefig("test_rasterization.svg", dpi=150)
    +    # svg backend currently ignores the dpi
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.artist.Artist.set_rasterized`
    +#    - `matplotlib.axes.Axes.set_rasterization_zorder`
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    diff --git a/galleries/examples/misc/set_and_get.py b/galleries/examples/misc/set_and_get.py
    new file mode 100644
    index 000000000000..453f32276cb1
    --- /dev/null
    +++ b/galleries/examples/misc/set_and_get.py
    @@ -0,0 +1,101 @@
    +"""
    +======================
    +Set and get properties
    +======================
    +
    +The pyplot interface allows you to use ``setp`` and ``getp`` to
    +set and get object properties respectively, as well as to do
    +introspection on the object.
    +
    +Setting with ``setp``
    +=====================
    +
    +To set the linestyle of a line to be dashed, you use ``setp``::
    +
    +  >>> line, = plt.plot([1, 2, 3])
    +  >>> plt.setp(line, linestyle='--')
    +
    +If you want to know the valid types of arguments, you can provide the
    +name of the property you want to set without a value::
    +
    +  >>> plt.setp(line, 'linestyle')
    +      linestyle: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...}
    +
    +If you want to see all the properties that can be set, and their
    +possible values, you can do::
    +
    +    >>> plt.setp(line)
    +
    +``setp`` operates on a single instance or a list of instances.  If you
    +are in query mode introspecting the possible values, only the first
    +instance in the sequence is used.  When actually setting values, all
    +the instances will be set.  For example, suppose you have a list of
    +two lines, the following will make both lines thicker and red::
    +
    +    >>> x = np.arange(0, 1, 0.01)
    +    >>> y1 = np.sin(2*np.pi*x)
    +    >>> y2 = np.sin(4*np.pi*x)
    +    >>> lines = plt.plot(x, y1, x, y2)
    +    >>> plt.setp(lines, linewidth=2, color='r')
    +
    +
    +Getting with ``getp``
    +=====================
    +
    +``getp`` returns the value of a given attribute.  You can use it to query
    +the value of a single attribute::
    +
    +    >>> plt.getp(line, 'linewidth')
    +        0.5
    +
    +or all the attribute/value pairs::
    +
    +    >>> plt.getp(line)
    +        aa = True
    +        alpha = 1.0
    +        antialiased = True
    +        c = b
    +        clip_on = True
    +        color = b
    +        ... long listing skipped ...
    +
    +Aliases
    +=======
    +
    +To reduce keystrokes in interactive mode, a number of properties
    +have short aliases, e.g., 'lw' for 'linewidth' and 'mec' for
    +'markeredgecolor'.  When calling set or get in introspection mode,
    +these properties will be listed as 'fullname' or 'aliasname'.
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.arange(0, 1.0, 0.01)
    +y1 = np.sin(2*np.pi*x)
    +y2 = np.sin(4*np.pi*x)
    +lines = plt.plot(x, y1, x, y2)
    +l1, l2 = lines
    +plt.setp(lines, linestyle='--')       # set both to dashed
    +plt.setp(l1, linewidth=2, color='r')  # line1 is thick and red
    +plt.setp(l2, linewidth=1, color='g')  # line2 is thinner and green
    +
    +
    +print('Line setters')
    +plt.setp(l1)
    +print('Line getters')
    +plt.getp(l1)
    +
    +print('Rectangle setters')
    +plt.setp(plt.gca().patch)
    +print('Rectangle getters')
    +plt.getp(plt.gca().patch)
    +
    +t = plt.title('Hi mom')
    +print('Text setters')
    +plt.setp(t)
    +print('Text getters')
    +plt.getp(t)
    +
    +plt.show()
    diff --git a/galleries/examples/misc/svg_filter_line.py b/galleries/examples/misc/svg_filter_line.py
    new file mode 100644
    index 000000000000..dd97dc975eda
    --- /dev/null
    +++ b/galleries/examples/misc/svg_filter_line.py
    @@ -0,0 +1,86 @@
    +"""
    +==========================
    +Apply SVG filter to a line
    +==========================
    +
    +Demonstrate SVG filtering effects which might be used with Matplotlib.
    +
    +Note that the filtering effects are only effective if your SVG renderer
    +support it.
    +"""
    +
    +import io
    +import xml.etree.ElementTree as ET
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.transforms as mtransforms
    +
    +fig1 = plt.figure()
    +ax = fig1.add_axes((0.1, 0.1, 0.8, 0.8))
    +
    +# draw lines
    +l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-",
    +              mec="b", lw=5, ms=10, label="Line 1")
    +l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "rs-",
    +              mec="r", lw=5, ms=10, label="Line 2")
    +
    +
    +for l in [l1, l2]:
    +
    +    # draw shadows with same lines with slight offset and gray colors.
    +
    +    xx = l.get_xdata()
    +    yy = l.get_ydata()
    +    shadow, = ax.plot(xx, yy)
    +    shadow.update_from(l)
    +
    +    # adjust color
    +    shadow.set_color("0.2")
    +    # adjust zorder of the shadow lines so that it is drawn below the
    +    # original lines
    +    shadow.set_zorder(l.get_zorder() - 0.5)
    +
    +    # offset transform
    +    transform = mtransforms.offset_copy(l.get_transform(), fig1,
    +                                        x=4.0, y=-6.0, units='points')
    +    shadow.set_transform(transform)
    +
    +    # set the id for a later use
    +    shadow.set_gid(l.get_label() + "_shadow")
    +
    +
    +ax.set_xlim(0., 1.)
    +ax.set_ylim(0., 1.)
    +
    +# save the figure as a bytes string in the svg format.
    +f = io.BytesIO()
    +plt.savefig(f, format="svg")
    +
    +
    +# filter definition for a gaussian blur
    +filter_def = """
    +  
    +    
    +      
    +    
    +  
    +"""
    +
    +
    +# read in the saved svg
    +tree, xmlid = ET.XMLID(f.getvalue())
    +
    +# insert the filter definition in the svg dom tree.
    +tree.insert(0, ET.XML(filter_def))
    +
    +for l in [l1, l2]:
    +    # pick up the svg element with given id
    +    shadow = xmlid[l.get_label() + "_shadow"]
    +    # apply shadow filter
    +    shadow.set("filter", 'url(#dropshadow)')
    +
    +fn = "svg_filter_line.svg"
    +print(f"Saving '{fn}'")
    +ET.ElementTree(tree).write(fn)
    diff --git a/galleries/examples/misc/svg_filter_pie.py b/galleries/examples/misc/svg_filter_pie.py
    new file mode 100644
    index 000000000000..f8ccc5bcb22b
    --- /dev/null
    +++ b/galleries/examples/misc/svg_filter_pie.py
    @@ -0,0 +1,98 @@
    +"""
    +==============
    +SVG filter pie
    +==============
    +
    +Demonstrate SVG filtering effects which might be used with Matplotlib.
    +The pie chart drawing code is borrowed from pie_demo.py
    +
    +Note that the filtering effects are only effective if your SVG renderer
    +support it.
    +"""
    +
    +import io
    +import xml.etree.ElementTree as ET
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.patches import Shadow
    +
    +# make a square figure and Axes
    +fig = plt.figure(figsize=(6, 6))
    +ax = fig.add_axes((0.1, 0.1, 0.8, 0.8))
    +
    +labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
    +fracs = [15, 30, 45, 10]
    +
    +explode = (0, 0.05, 0, 0)
    +
    +# We want to draw the shadow for each pie, but we will not use "shadow"
    +# option as it doesn't save the references to the shadow patches.
    +pie = ax.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%')
    +
    +for w in pie.wedges:
    +    # set the id with the label.
    +    w.set_gid(w.get_label())
    +
    +    # we don't want to draw the edge of the pie
    +    w.set_edgecolor("none")
    +
    +for w in pie.wedges:
    +    # create shadow patch
    +    s = Shadow(w, -0.01, -0.01)
    +    s.set_gid(w.get_gid() + "_shadow")
    +    s.set_zorder(w.get_zorder() - 0.1)
    +    ax.add_patch(s)
    +
    +
    +# save
    +f = io.BytesIO()
    +plt.savefig(f, format="svg")
    +
    +
    +# Filter definition for shadow using a gaussian blur and lighting effect.
    +# The lighting filter is copied from http://www.w3.org/TR/SVG/filters.html
    +
    +# I tested it with Inkscape and Firefox3. "Gaussian blur" is supported
    +# in both, but the lighting effect only in Inkscape. Also note
    +# that, Inkscape's exporting also may not support it.
    +
    +filter_def = """
    +  
    +    
    +      
    +    
    +
    +    
    +      
    +      
    +      
    +        
    +      
    +      
    +      
    +    
    +  
    +"""
    +
    +
    +tree, xmlid = ET.XMLID(f.getvalue())
    +
    +# insert the filter definition in the svg dom tree.
    +tree.insert(0, ET.XML(filter_def))
    +
    +for i, pie_name in enumerate(labels):
    +    pie = xmlid[pie_name]
    +    pie.set("filter", 'url(#MyFilter)')
    +
    +    shadow = xmlid[pie_name + "_shadow"]
    +    shadow.set("filter", 'url(#dropshadow)')
    +
    +fn = "svg_filter_pie.svg"
    +print(f"Saving '{fn}'")
    +ET.ElementTree(tree).write(fn)
    diff --git a/galleries/examples/misc/table_demo.py b/galleries/examples/misc/table_demo.py
    new file mode 100644
    index 000000000000..47c820bafb28
    --- /dev/null
    +++ b/galleries/examples/misc/table_demo.py
    @@ -0,0 +1,58 @@
    +"""
    +==========
    +Table Demo
    +==========
    +
    +Demo of table function to display a table within a plot.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +data = [[ 66386, 174296,  75131, 577908,  32015],
    +        [ 58230, 381139,  78045,  99308, 160454],
    +        [ 89135,  80552, 152558, 497981, 603535],
    +        [ 78415,  81858, 150656, 193263,  69638],
    +        [139361, 331509, 343164, 781380,  52269]]
    +
    +columns = ('Freeze', 'Wind', 'Flood', 'Quake', 'Hail')
    +rows = ['%d year' % x for x in (100, 50, 20, 10, 5)]
    +
    +values = np.arange(0, 2500, 500)
    +value_increment = 1000
    +
    +# Get some pastel shades for the colors
    +colors = plt.colormaps["BuPu"](np.linspace(0, 0.5, len(rows)))
    +n_rows = len(data)
    +
    +index = np.arange(len(columns)) + 0.3
    +bar_width = 0.4
    +
    +# Initialize the vertical-offset for the stacked bar chart.
    +y_offset = np.zeros(len(columns))
    +
    +# Plot bars and create text labels for the table
    +cell_text = []
    +for row in range(n_rows):
    +    plt.bar(index, data[row], bar_width, bottom=y_offset, color=colors[row])
    +    y_offset = y_offset + data[row]
    +    cell_text.append(['%1.1f' % (x / 1000.0) for x in y_offset])
    +# Reverse colors and text labels to display the last value at the top.
    +colors = colors[::-1]
    +cell_text.reverse()
    +
    +# Add a table at the bottom of the Axes
    +the_table = plt.table(cellText=cell_text,
    +                      rowLabels=rows,
    +                      rowColours=colors,
    +                      colLabels=columns,
    +                      loc='bottom')
    +
    +# Adjust layout to make room for the table:
    +plt.subplots_adjust(left=0.2, bottom=0.2)
    +
    +plt.ylabel(f"Loss in ${value_increment}'s")
    +plt.yticks(values * value_increment, ['%d' % val for val in values])
    +plt.xticks([])
    +plt.title('Loss by Disaster')
    +
    +plt.show()
    diff --git a/galleries/examples/misc/tickedstroke_demo.py b/galleries/examples/misc/tickedstroke_demo.py
    new file mode 100644
    index 000000000000..af32ce169bb6
    --- /dev/null
    +++ b/galleries/examples/misc/tickedstroke_demo.py
    @@ -0,0 +1,119 @@
    +"""
    +=======================
    +TickedStroke patheffect
    +=======================
    +
    +Matplotlib's :mod:`.patheffects` can be used to alter the way paths
    +are drawn at a low enough level that they can affect almost anything.
    +
    +The :ref:`patheffects guide`
    +details the use of patheffects.
    +
    +The `~matplotlib.patheffects.TickedStroke` patheffect illustrated here
    +draws a path with a ticked style.  The spacing, length, and angle of
    +ticks can be controlled.
    +
    +See also the :doc:`/gallery/lines_bars_and_markers/lines_with_ticks_demo` example.
    +
    +See also the :doc:`/gallery/images_contours_and_fields/contours_in_optimization_demo`
    +example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +# Applying TickedStroke to paths
    +# ==============================
    +import matplotlib.patches as patches
    +from matplotlib.path import Path
    +import matplotlib.patheffects as patheffects
    +
    +fig, ax = plt.subplots(figsize=(6, 6))
    +path = Path.unit_circle()
    +patch = patches.PathPatch(path, facecolor='none', lw=2, path_effects=[
    +    patheffects.withTickedStroke(angle=-90, spacing=10, length=1)])
    +
    +ax.add_patch(patch)
    +ax.axis('equal')
    +ax.set_xlim(-2, 2)
    +ax.set_ylim(-2, 2)
    +
    +plt.show()
    +
    +# %%
    +# Applying TickedStroke to lines
    +# ==============================
    +fig, ax = plt.subplots(figsize=(6, 6))
    +ax.plot([0, 1], [0, 1], label="Line",
    +        path_effects=[patheffects.withTickedStroke(spacing=7, angle=135)])
    +
    +nx = 101
    +x = np.linspace(0.0, 1.0, nx)
    +y = 0.3*np.sin(x*8) + 0.4
    +ax.plot(x, y, label="Curve", path_effects=[patheffects.withTickedStroke()])
    +
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# Applying TickedStroke to contour plots
    +# ======================================
    +#
    +# Contour plot with objective and constraints.
    +# Curves generated by contour to represent a typical constraint in an
    +# optimization problem should be plotted with angles between zero and
    +# 180 degrees.
    +fig, ax = plt.subplots(figsize=(6, 6))
    +
    +nx = 101
    +ny = 105
    +
    +# Set up survey vectors
    +xvec = np.linspace(0.001, 4.0, nx)
    +yvec = np.linspace(0.001, 4.0, ny)
    +
    +# Set up survey matrices.  Design disk loading and gear ratio.
    +x1, x2 = np.meshgrid(xvec, yvec)
    +
    +# Evaluate some stuff to plot
    +obj = x1**2 + x2**2 - 2*x1 - 2*x2 + 2
    +g1 = -(3*x1 + x2 - 5.5)
    +g2 = -(x1 + 2*x2 - 4.5)
    +g3 = 0.8 + x1**-3 - x2
    +
    +cntr = ax.contour(x1, x2, obj, [0.01, 0.1, 0.5, 1, 2, 4, 8, 16],
    +                  colors='black')
    +ax.clabel(cntr, fmt="%2.1f", use_clabeltext=True)
    +
    +cg1 = ax.contour(x1, x2, g1, [0], colors='sandybrown')
    +cg1.set(path_effects=[patheffects.withTickedStroke(angle=135)])
    +
    +cg2 = ax.contour(x1, x2, g2, [0], colors='orangered')
    +cg2.set(path_effects=[patheffects.withTickedStroke(angle=60, length=2)])
    +
    +cg3 = ax.contour(x1, x2, g3, [0], colors='mediumblue')
    +cg3.set(path_effects=[patheffects.withTickedStroke(spacing=7)])
    +
    +ax.set_xlim(0, 4)
    +ax.set_ylim(0, 4)
    +
    +plt.show()
    +
    +# %%
    +# Direction/side of the ticks
    +# ===========================
    +#
    +# To change which side of the line the ticks are drawn, change the sign of the angle.
    +
    +fig, ax = plt.subplots(figsize=(6, 6))
    +line_x = line_y = [0, 1]
    +ax.plot(line_x, line_y, label="Line",
    +        path_effects=[patheffects.withTickedStroke(spacing=7, angle=135)])
    +
    +ax.plot(line_x, line_y, label="Opposite side",
    +        path_effects=[patheffects.withTickedStroke(spacing=7, angle=-135)])
    +
    +ax.legend()
    +plt.show()
    diff --git a/galleries/examples/misc/transoffset.py b/galleries/examples/misc/transoffset.py
    new file mode 100644
    index 000000000000..b3163e8df703
    --- /dev/null
    +++ b/galleries/examples/misc/transoffset.py
    @@ -0,0 +1,57 @@
    +"""
    +======================
    +transforms.offset_copy
    +======================
    +
    +This illustrates the use of `.transforms.offset_copy` to
    +make a transform that positions a drawing element such as
    +a text string at a specified offset in screen coordinates
    +(dots or inches) relative to a location given in any
    +coordinates.
    +
    +Every Artist (Text, Line2D, etc.) has a transform that can be
    +set when the Artist is created, such as by the corresponding
    +pyplot function.  By default, this is usually the Axes.transData
    +transform, going from data units to screen pixels.  We can
    +use the `.offset_copy` function to make a modified copy of
    +this transform, where the modification consists of an
    +offset.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.transforms as mtransforms
    +
    +xs = np.arange(7)
    +ys = xs**2
    +
    +fig = plt.figure(figsize=(5, 10))
    +ax = plt.subplot(2, 1, 1)
    +
    +# If we want the same offset for each text instance,
    +# we only need to make one transform.  To get the
    +# transform argument to offset_copy, we need to make the Axes
    +# first; the subplot function above is one way to do this.
    +trans_offset = mtransforms.offset_copy(ax.transData, fig=fig,
    +                                       x=0.05, y=0.10, units='inches')
    +
    +for x, y in zip(xs, ys):
    +    plt.plot(x, y, 'ro')
    +    plt.text(x, y, '%d, %d' % (int(x), int(y)), transform=trans_offset)
    +
    +
    +# offset_copy works for polar plots also.
    +ax = plt.subplot(2, 1, 2, projection='polar')
    +
    +trans_offset = mtransforms.offset_copy(ax.transData, fig=fig,
    +                                       y=6, units='dots')
    +
    +for x, y in zip(xs, ys):
    +    plt.polar(x, y, 'ro')
    +    plt.text(x, y, '%d, %d' % (int(x), int(y)),
    +             transform=trans_offset,
    +             horizontalalignment='center',
    +             verticalalignment='bottom')
    +
    +plt.show()
    diff --git a/galleries/examples/misc/zorder_demo.py b/galleries/examples/misc/zorder_demo.py
    new file mode 100644
    index 000000000000..e077dc56d2d0
    --- /dev/null
    +++ b/galleries/examples/misc/zorder_demo.py
    @@ -0,0 +1,75 @@
    +"""
    +===========
    +Zorder Demo
    +===========
    +
    +The drawing order of artists is determined by their ``zorder`` attribute, which
    +is a floating point number. Artists with higher ``zorder`` are drawn on top.
    +You can change the order for individual artists by setting their ``zorder``.
    +The default value depends on the type of the Artist:
    +
    +================================================================    =======
    +Artist                                                              Z-order
    +================================================================    =======
    +Images (`.AxesImage`, `.FigureImage`, `.BboxImage`)                 0
    +`.Patch`, `.PatchCollection`                                        1
    +`.Line2D`, `.LineCollection` (including minor ticks, grid lines)    2
    +Major ticks                                                         2.01
    +`.Text` (including Axes labels and titles)                          3
    +`.Legend`                                                           5
    +================================================================    =======
    +
    +Any call to a plotting method can set a value for the zorder of that particular
    +item explicitly.
    +
    +.. note::
    +
    +   `~.axes.Axes.set_axisbelow` and :rc:`axes.axisbelow` are convenient helpers
    +   for setting the zorder of ticks and grid lines.
    +
    +Drawing is done per `~.axes.Axes` at a time. If you have overlapping Axes, all
    +elements of the second Axes are drawn on top of the first Axes, irrespective of
    +their relative zorder.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +r = np.linspace(0.3, 1, 30)
    +theta = np.linspace(0, 4*np.pi, 30)
    +x = r * np.sin(theta)
    +y = r * np.cos(theta)
    +
    +# %%
    +# The following example contains a `.Line2D` created by `~.axes.Axes.plot()`
    +# and the dots (a `.PatchCollection`) created by `~.axes.Axes.scatter()`.
    +# Hence, by default the dots are below the line (first subplot).
    +# In the second subplot, the ``zorder`` is set explicitly to move the dots
    +# on top of the line.
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6, 3.2))
    +
    +ax1.plot(x, y, 'C3', lw=3)
    +ax1.scatter(x, y, s=120)
    +ax1.set_title('Lines on top of dots')
    +
    +ax2.plot(x, y, 'C3', lw=3)
    +ax2.scatter(x, y, s=120, zorder=2.5)  # move dots on top of line
    +ax2.set_title('Dots on top of lines')
    +
    +plt.tight_layout()
    +
    +# %%
    +# Many functions that create a visible object accepts a ``zorder`` parameter.
    +# Alternatively, you can call ``set_zorder()`` on the created object later.
    +
    +x = np.linspace(0, 7.5, 100)
    +plt.rcParams['lines.linewidth'] = 5
    +plt.figure()
    +plt.plot(x, np.sin(x), label='zorder=2', zorder=2)  # bottom
    +plt.plot(x, np.sin(x+0.5), label='zorder=3',  zorder=3)
    +plt.axhline(0, label='zorder=2.5', color='lightgrey', zorder=2.5)
    +plt.title('Custom order of elements')
    +l = plt.legend(loc='upper right')
    +l.set_zorder(2.5)  # legend between blue and orange line
    +plt.show()
    diff --git a/galleries/examples/mplot3d/2dcollections3d.py b/galleries/examples/mplot3d/2dcollections3d.py
    new file mode 100644
    index 000000000000..ae88776d133e
    --- /dev/null
    +++ b/galleries/examples/mplot3d/2dcollections3d.py
    @@ -0,0 +1,54 @@
    +"""
    +=======================
    +Plot 2D data on 3D plot
    +=======================
    +
    +Demonstrates using ax.plot's *zdir* keyword to plot 2D data on
    +selective axes of a 3D plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# Plot a sin curve using the x and y axes.
    +x = np.linspace(0, 1, 100)
    +y = np.sin(x * 2 * np.pi) / 2 + 0.5
    +ax.plot(x, y, zs=0, zdir='z', label='curve in (x, y)')
    +
    +# Plot scatterplot data (20 2D points per colour) on the x and z axes.
    +colors = ('r', 'g', 'b', 'k')
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +x = np.random.sample(20 * len(colors))
    +y = np.random.sample(20 * len(colors))
    +c_list = []
    +for c in colors:
    +    c_list.extend([c] * 20)
    +# By using zdir='y', the y value of these points is fixed to the zs value 0
    +# and the (x, y) points are plotted on the x and z axes.
    +ax.scatter(x, y, zs=0, zdir='y', c=c_list, label='points in (x, z)')
    +
    +# Make legend, set axes limits and labels
    +ax.legend()
    +ax.set_xlim(0, 1)
    +ax.set_ylim(0, 1)
    +ax.set_zlim(0, 1)
    +ax.set_xlabel('X')
    +ax.set_ylabel('Y')
    +ax.set_zlabel('Z')
    +
    +# Customize the view angle so it's easier to see that the scatter points lie
    +# on the plane y=0
    +ax.view_init(elev=20., azim=-35, roll=0)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: scatter, plot-type: line,
    +#    component: axes,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/3d_bars.py b/galleries/examples/mplot3d/3d_bars.py
    new file mode 100644
    index 000000000000..9d8feeaeb12b
    --- /dev/null
    +++ b/galleries/examples/mplot3d/3d_bars.py
    @@ -0,0 +1,40 @@
    +"""
    +=====================
    +Demo of 3D bar charts
    +=====================
    +
    +A basic demo of how to plot 3D bars with and without shading.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# set up the figure and Axes
    +fig = plt.figure(figsize=(8, 3))
    +ax1 = fig.add_subplot(121, projection='3d')
    +ax2 = fig.add_subplot(122, projection='3d')
    +
    +# fake data
    +_x = np.arange(4)
    +_y = np.arange(5)
    +_xx, _yy = np.meshgrid(_x, _y)
    +x, y = _xx.ravel(), _yy.ravel()
    +
    +top = x + y
    +bottom = np.zeros_like(top)
    +width = depth = 1
    +
    +ax1.bar3d(x, y, bottom, width, depth, top, shade=True)
    +ax1.set_title('Shaded')
    +
    +ax2.bar3d(x, y, bottom, width, depth, top, shade=False)
    +ax2.set_title('Not Shaded')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: texture,
    +#    plot-type: bar,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/README.txt b/galleries/examples/mplot3d/README.txt
    new file mode 100644
    index 000000000000..f9832f3ab3a5
    --- /dev/null
    +++ b/galleries/examples/mplot3d/README.txt
    @@ -0,0 +1,6 @@
    +.. _mplot3d_example:
    +
    +.. _mplot3d-examples-index:
    +
    +3D plotting
    +===========
    diff --git a/galleries/examples/mplot3d/axlim_clip.py b/galleries/examples/mplot3d/axlim_clip.py
    new file mode 100644
    index 000000000000..2a29f2bf2431
    --- /dev/null
    +++ b/galleries/examples/mplot3d/axlim_clip.py
    @@ -0,0 +1,40 @@
    +"""
    +=====================================
    +Clip the data to the axes view limits
    +=====================================
    +
    +Demonstrate clipping of line and marker data to the axes view limits. The
    +``axlim_clip`` keyword argument can be used in any of the 3D plotting
    +functions.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    +
    +# Make the data
    +x = np.arange(-5, 5, 0.5)
    +y = np.arange(-5, 5, 0.5)
    +X, Y = np.meshgrid(x, y)
    +R = np.sqrt(X**2 + Y**2)
    +Z = np.sin(R)
    +
    +# Default behavior is axlim_clip=False
    +ax.plot_wireframe(X, Y, Z, color='C0')
    +
    +# When axlim_clip=True, note that when a line segment has one vertex outside
    +# the view limits, the entire line is hidden. The same is true for 3D patches
    +# if one of their vertices is outside the limits (not shown).
    +ax.plot_wireframe(X, Y, Z, color='C1', axlim_clip=True)
    +
    +# In this example, data where x < 0 or z > 0.5 is clipped
    +ax.set(xlim=(0, 10), ylim=(-5, 5), zlim=(-1, 0.5))
    +ax.legend(['axlim_clip=False (default)', 'axlim_clip=True'])
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/bars3d.py b/galleries/examples/mplot3d/bars3d.py
    new file mode 100644
    index 000000000000..3ea4a100c2f6
    --- /dev/null
    +++ b/galleries/examples/mplot3d/bars3d.py
    @@ -0,0 +1,48 @@
    +"""
    +========================================
    +Create 2D bar graphs in different planes
    +========================================
    +
    +Demonstrates making a 3D plot which has 2D bar graphs projected onto
    +planes y=0, y=1, etc.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +colors = ['r', 'g', 'b', 'y']
    +yticks = [3, 2, 1, 0]
    +for c, k in zip(colors, yticks):
    +    # Generate the random data for the y=k 'layer'.
    +    xs = np.arange(20)
    +    ys = np.random.rand(20)
    +
    +    # You can provide either a single color or an array with the same length as
    +    # xs and ys. To demonstrate this, we color the first bar of each set cyan.
    +    cs = [c] * len(xs)
    +    cs[0] = 'c'
    +
    +    # Plot the bar graph given by xs and ys on the plane y=k with 80% opacity.
    +    ax.bar(xs, ys, zs=k, zdir='y', color=cs, alpha=0.8)
    +
    +ax.set_xlabel('X')
    +ax.set_ylabel('Y')
    +ax.set_zlabel('Z')
    +
    +# On the y-axis let's only label the discrete values that we have data for.
    +ax.set_yticks(yticks)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: bar,
    +#    styling: color,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/box3d.py b/galleries/examples/mplot3d/box3d.py
    new file mode 100644
    index 000000000000..4d75c8bc2809
    --- /dev/null
    +++ b/galleries/examples/mplot3d/box3d.py
    @@ -0,0 +1,83 @@
    +"""
    +===================
    +3D box surface plot
    +===================
    +
    +Given data on a gridded volume ``X``, ``Y``, ``Z``, this example plots the
    +data values on the volume surfaces.
    +
    +The strategy is to select the data from each surface and plot
    +contours separately using `.axes3d.Axes3D.contourf` with appropriate
    +parameters *zdir* and *offset*.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Define dimensions
    +Nx, Ny, Nz = 100, 300, 500
    +X, Y, Z = np.meshgrid(np.arange(Nx), np.arange(Ny), -np.arange(Nz))
    +
    +# Create fake data
    +data = (((X+100)**2 + (Y-20)**2 + 2*Z)/1000+1)
    +
    +kw = {
    +    'vmin': data.min(),
    +    'vmax': data.max(),
    +    'levels': np.linspace(data.min(), data.max(), 10),
    +}
    +
    +# Create a figure with 3D ax
    +fig = plt.figure(figsize=(5, 4))
    +ax = fig.add_subplot(111, projection='3d')
    +
    +# Plot contour surfaces
    +_ = ax.contourf(
    +    X[:, :, 0], Y[:, :, 0], data[:, :, 0],
    +    zdir='z', offset=0, **kw
    +)
    +_ = ax.contourf(
    +    X[0, :, :], data[0, :, :], Z[0, :, :],
    +    zdir='y', offset=0, **kw
    +)
    +C = ax.contourf(
    +    data[:, -1, :], Y[:, -1, :], Z[:, -1, :],
    +    zdir='x', offset=X.max(), **kw
    +)
    +# --
    +
    +
    +# Set limits of the plot from coord limits
    +xmin, xmax = X.min(), X.max()
    +ymin, ymax = Y.min(), Y.max()
    +zmin, zmax = Z.min(), Z.max()
    +ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax), zlim=(zmin, zmax))
    +
    +# Plot edges
    +edges_kw = dict(color='0.4', linewidth=1, zorder=1e3)
    +ax.plot([xmax, xmax], [ymin, ymax], 0, **edges_kw)
    +ax.plot([xmin, xmax], [ymin, ymin], 0, **edges_kw)
    +ax.plot([xmax, xmax], [ymin, ymin], [zmin, zmax], **edges_kw)
    +
    +# Set labels and zticks
    +ax.set(
    +    xlabel='X [km]',
    +    ylabel='Y [km]',
    +    zlabel='Z [m]',
    +    zticks=[0, -150, -300, -450],
    +)
    +
    +# Set zoom and angle view
    +ax.view_init(40, -30, 0)
    +ax.set_box_aspect(None, zoom=0.9)
    +
    +# Colorbar
    +fig.colorbar(C, ax=ax, fraction=0.02, pad=0.1, label='Name [units]')
    +
    +# Show Figure
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/contour3d.py b/galleries/examples/mplot3d/contour3d.py
    new file mode 100644
    index 000000000000..747fdbe37d8a
    --- /dev/null
    +++ b/galleries/examples/mplot3d/contour3d.py
    @@ -0,0 +1,24 @@
    +"""
    +=================================
    +Plot contour (level) curves in 3D
    +=================================
    +
    +This is like a contour plot in 2D except that the ``f(x, y)=c`` curve is
    +plotted on the plane ``z=c``.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +ax.contour(X, Y, Z, cmap="coolwarm")  # Plot contour curves
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/contour3d_2.py b/galleries/examples/mplot3d/contour3d_2.py
    new file mode 100644
    index 000000000000..f70409efcc36
    --- /dev/null
    +++ b/galleries/examples/mplot3d/contour3d_2.py
    @@ -0,0 +1,23 @@
    +"""
    +===========================================================
    +Plot contour (level) curves in 3D using the extend3d option
    +===========================================================
    +
    +This modification of the :doc:`contour3d` example uses ``extend3d=True`` to
    +extend the curves vertically into 'ribbons'.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +X, Y, Z = axes3d.get_test_data(0.05)
    +ax.contour(X, Y, Z, extend3d=True, cmap="coolwarm")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/contour3d_3.py b/galleries/examples/mplot3d/contour3d_3.py
    new file mode 100644
    index 000000000000..92adb97fc04e
    --- /dev/null
    +++ b/galleries/examples/mplot3d/contour3d_3.py
    @@ -0,0 +1,37 @@
    +"""
    +=====================================
    +Project contour profiles onto a graph
    +=====================================
    +Demonstrates displaying a 3D surface while also projecting contour 'profiles'
    +onto the 'walls' of the graph.
    +See :doc:`contourf3d_2` for the filled version.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +# Plot the 3D surface
    +ax.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, rstride=8, cstride=8,
    +                alpha=0.3)
    +
    +# Plot projections of the contours for each dimension.  By choosing offsets
    +# that match the appropriate axes limits, the projected contours will sit on
    +# the 'walls' of the graph.
    +ax.contour(X, Y, Z, zdir='z', offset=-100, cmap='coolwarm')
    +ax.contour(X, Y, Z, zdir='x', offset=-40, cmap='coolwarm')
    +ax.contour(X, Y, Z, zdir='y', offset=40, cmap='coolwarm')
    +
    +ax.set(xlim=(-40, 40), ylim=(-40, 40), zlim=(-100, 100),
    +       xlabel='X', ylabel='Y', zlabel='Z')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: axes,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/contourf3d.py b/galleries/examples/mplot3d/contourf3d.py
    new file mode 100644
    index 000000000000..831547f9aaa1
    --- /dev/null
    +++ b/galleries/examples/mplot3d/contourf3d.py
    @@ -0,0 +1,26 @@
    +"""
    +===============
    +Filled contours
    +===============
    +
    +`.Axes3D.contourf` differs from `.Axes3D.contour` in that it creates filled
    +contours, i.e. a discrete number of colours are used to shade the domain.
    +
    +This is like a `.Axes.contourf` plot in 2D except that the shaded region
    +corresponding to the level c is graphed on the plane ``z=c``.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +X, Y, Z = axes3d.get_test_data(0.05)
    +ax.contourf(X, Y, Z, cmap="coolwarm")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/contourf3d_2.py b/galleries/examples/mplot3d/contourf3d_2.py
    new file mode 100644
    index 000000000000..58fede4e3ab5
    --- /dev/null
    +++ b/galleries/examples/mplot3d/contourf3d_2.py
    @@ -0,0 +1,37 @@
    +"""
    +===================================
    +Project filled contour onto a graph
    +===================================
    +Demonstrates displaying a 3D surface while also projecting filled contour
    +'profiles' onto the 'walls' of the graph.
    +See :doc:`contour3d_3` for the unfilled version.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +# Plot the 3D surface
    +ax.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, rstride=8, cstride=8,
    +                alpha=0.3)
    +
    +# Plot projections of the contours for each dimension.  By choosing offsets
    +# that match the appropriate axes limits, the projected contours will sit on
    +# the 'walls' of the graph
    +ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap='coolwarm')
    +ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap='coolwarm')
    +ax.contourf(X, Y, Z, zdir='y', offset=40, cmap='coolwarm')
    +
    +ax.set(xlim=(-40, 40), ylim=(-40, 40), zlim=(-100, 100),
    +       xlabel='X', ylabel='Y', zlabel='Z')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: axes,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/custom_shaded_3d_surface.py b/galleries/examples/mplot3d/custom_shaded_3d_surface.py
    new file mode 100644
    index 000000000000..e8d1a4f33d87
    --- /dev/null
    +++ b/galleries/examples/mplot3d/custom_shaded_3d_surface.py
    @@ -0,0 +1,42 @@
    +"""
    +=======================================
    +Custom hillshading in a 3D surface plot
    +=======================================
    +
    +Demonstrates using custom hillshading in a 3D surface plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +from matplotlib.colors import LightSource
    +
    +# Load and format data
    +dem = cbook.get_sample_data('jacksboro_fault_dem.npz')
    +z = dem['elevation']
    +nrows, ncols = z.shape
    +x = np.linspace(dem['xmin'], dem['xmax'], ncols)
    +y = np.linspace(dem['ymin'], dem['ymax'], nrows)
    +x, y = np.meshgrid(x, y)
    +
    +region = np.s_[5:50, 5:50]
    +x, y, z = x[region], y[region], z[region]
    +
    +# Set up plot
    +fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
    +
    +ls = LightSource(270, 45)
    +# To use a custom hillshading mode, override the built-in shading and pass
    +# in the rgb colors of the shaded surface calculated from "shade".
    +rgb = ls.shade(z, cmap=plt.colormaps["gist_earth"], vert_exag=0.1, blend_mode='soft')
    +surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb,
    +                       linewidth=0, antialiased=False, shade=False)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: intermediate,
    +#    domain: cartography
    diff --git a/galleries/examples/mplot3d/errorbar3d.py b/galleries/examples/mplot3d/errorbar3d.py
    new file mode 100644
    index 000000000000..1ece3ca1e8cf
    --- /dev/null
    +++ b/galleries/examples/mplot3d/errorbar3d.py
    @@ -0,0 +1,35 @@
    +"""
    +============
    +3D errorbars
    +============
    +
    +An example of using errorbars with upper and lower limits in mplot3d.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# setting up a parametric curve
    +t = np.arange(0, 2*np.pi+.1, 0.01)
    +x, y, z = np.sin(t), np.cos(3*t), np.sin(5*t)
    +
    +estep = 15
    +i = np.arange(t.size)
    +zuplims = (i % estep == 0) & (i // estep % 3 == 0)
    +zlolims = (i % estep == 0) & (i // estep % 3 == 2)
    +
    +ax.errorbar(x, y, z, 0.2, zuplims=zuplims, zlolims=zlolims, errorevery=estep)
    +
    +ax.set_xlabel("X label")
    +ax.set_ylabel("Y label")
    +ax.set_zlabel("Z label")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: error,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/fillbetween3d.py b/galleries/examples/mplot3d/fillbetween3d.py
    new file mode 100644
    index 000000000000..b9d61b4d1eb2
    --- /dev/null
    +++ b/galleries/examples/mplot3d/fillbetween3d.py
    @@ -0,0 +1,34 @@
    +"""
    +=====================
    +Fill between 3D lines
    +=====================
    +
    +Demonstrate how to fill the space between 3D lines with surfaces. Here we
    +create a sort of "lampshade" shape.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +N = 50
    +theta = np.linspace(0, 2*np.pi, N)
    +
    +x1 = np.cos(theta)
    +y1 = np.sin(theta)
    +z1 = 0.1 * np.sin(6 * theta)
    +
    +x2 = 0.6 * np.cos(theta)
    +y2 = 0.6 * np.sin(theta)
    +z2 = 2  # Note that scalar values work in addition to length N arrays
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +ax.fill_between(x1, y1, z1, x2, y2, z2, alpha=0.5, edgecolor='k')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    plot-type: fill_between,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/fillunder3d.py b/galleries/examples/mplot3d/fillunder3d.py
    new file mode 100644
    index 000000000000..7e9889633f70
    --- /dev/null
    +++ b/galleries/examples/mplot3d/fillunder3d.py
    @@ -0,0 +1,40 @@
    +"""
    +=========================
    +Fill under 3D line graphs
    +=========================
    +
    +Demonstrate how to create polygons which fill the space under a line
    +graph. In this example polygons are semi-transparent, creating a sort
    +of 'jagged stained glass' effect.
    +"""
    +
    +import math
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +gamma = np.vectorize(math.gamma)
    +N = 31
    +x = np.linspace(0., 10., N)
    +lambdas = range(1, 9)
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +facecolors = plt.colormaps['viridis_r'](np.linspace(0, 1, len(lambdas)))
    +
    +for i, l in enumerate(lambdas):
    +    # Note fill_between can take coordinates as length N vectors, or scalars
    +    ax.fill_between(x, l, l**x * np.exp(-l) / gamma(x + 1),
    +                    x, l, 0,
    +                    facecolors=facecolors[i], alpha=.7)
    +
    +ax.set(xlim=(0, 10), ylim=(1, 9), zlim=(0, 0.35),
    +       xlabel='x', ylabel=r'$\lambda$', zlabel='probability')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    plot-type: fill_between,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/hist3d.py b/galleries/examples/mplot3d/hist3d.py
    new file mode 100644
    index 000000000000..65d0d60958d8
    --- /dev/null
    +++ b/galleries/examples/mplot3d/hist3d.py
    @@ -0,0 +1,39 @@
    +"""
    +==============================
    +Create 3D histogram of 2D data
    +==============================
    +
    +Demo of a histogram for 2D data as a bar graph in 3D.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +x, y = np.random.rand(2, 100) * 4
    +hist, xedges, yedges = np.histogram2d(x, y, bins=4, range=[[0, 4], [0, 4]])
    +
    +# Construct arrays for the anchor positions of the 16 bars.
    +xpos, ypos = np.meshgrid(xedges[:-1] + 0.25, yedges[:-1] + 0.25, indexing="ij")
    +xpos = xpos.ravel()
    +ypos = ypos.ravel()
    +zpos = 0
    +
    +# Construct arrays with the dimensions for the 16 bars.
    +dx = dy = 0.5 * np.ones_like(zpos)
    +dz = hist.ravel()
    +
    +ax.bar3d(xpos, ypos, zpos, dx, dy, dz, zsort='average')
    +
    +plt.show()
    +
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: histogram,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/imshow3d.py b/galleries/examples/mplot3d/imshow3d.py
    new file mode 100644
    index 000000000000..dba962734bbe
    --- /dev/null
    +++ b/galleries/examples/mplot3d/imshow3d.py
    @@ -0,0 +1,94 @@
    +"""
    +===============
    +2D images in 3D
    +===============
    +
    +This example demonstrates how to plot 2D color coded images (similar to
    +`.Axes.imshow`) as a plane in 3D.
    +
    +Matplotlib does not have a native function for this. Below we build one by relying
    +on `.Axes3D.plot_surface`. For simplicity, there are some differences to
    +`.Axes.imshow`: This function does not set the aspect of the Axes, hence pixels are
    +not necessarily square. Also, pixel edges are on integer values rather than pixel
    +centers. Furthermore, many optional parameters of `.Axes.imshow` are not implemented.
    +
    +Multiple calls of ``imshow3d`` use independent norms and thus different color scales
    +by default. If you want to have a single common color scale, you need to construct
    +a suitable norm beforehand and pass it to all ``imshow3d`` calls.
    +
    +A fundamental limitation of the 3D plotting engine is that intersecting objects cannot
    +be drawn correctly. One object will always be drawn after the other. Therefore,
    +multiple image planes can well be used in the background as shown in this example.
    +But this approach is not suitable if the planes intersect.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.colors import Normalize
    +
    +
    +def imshow3d(ax, array, value_direction='z', pos=0, norm=None, cmap=None):
    +    """
    +    Display a 2D array as a  color-coded 2D image embedded in 3d.
    +
    +    The image will be in a plane perpendicular to the coordinate axis *value_direction*.
    +
    +    Parameters
    +    ----------
    +    ax : Axes3D
    +        The 3D Axes to plot into.
    +    array : 2D numpy array
    +        The image values.
    +    value_direction : {'x', 'y', 'z'}
    +        The axis normal to the image plane.
    +    pos : float
    +        The numeric value on the *value_direction* axis at which the image plane is
    +        located.
    +    norm : `~matplotlib.colors.Normalize`, default: Normalize
    +        The normalization method used to scale scalar data. See `imshow()`.
    +    cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
    +        The Colormap instance or registered colormap name used to map scalar data
    +        to colors.
    +    """
    +    if norm is None:
    +        norm = Normalize()
    +    colors = plt.get_cmap(cmap)(norm(array))
    +
    +    if value_direction == 'x':
    +        nz, ny = array.shape
    +        zi, yi = np.mgrid[0:nz + 1, 0:ny + 1]
    +        xi = np.full_like(yi, pos)
    +    elif value_direction == 'y':
    +        nx, nz = array.shape
    +        xi, zi = np.mgrid[0:nx + 1, 0:nz + 1]
    +        yi = np.full_like(zi, pos)
    +    elif value_direction == 'z':
    +        ny, nx = array.shape
    +        yi, xi = np.mgrid[0:ny + 1, 0:nx + 1]
    +        zi = np.full_like(xi, pos)
    +    else:
    +        raise ValueError(f"Invalid value_direction: {value_direction!r}")
    +    ax.plot_surface(xi, yi, zi, rstride=1, cstride=1, facecolors=colors, shade=False)
    +
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +ax.set(xlabel="x", ylabel="y", zlabel="z")
    +
    +nx, ny, nz = 8, 10, 5
    +data_xy = np.arange(ny * nx).reshape(ny, nx) + 15 * np.random.random((ny, nx))
    +data_yz = np.arange(nz * ny).reshape(nz, ny) + 10 * np.random.random((nz, ny))
    +data_zx = np.arange(nx * nz).reshape(nx, nz) + 8 * np.random.random((nx, nz))
    +
    +imshow3d(ax, data_xy)
    +imshow3d(ax, data_yz, value_direction='x', cmap='magma')
    +imshow3d(ax, data_zx, value_direction='y', pos=ny, cmap='plasma')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: colormap,
    +#    level: advanced
    diff --git a/galleries/examples/mplot3d/intersecting_planes.py b/galleries/examples/mplot3d/intersecting_planes.py
    new file mode 100644
    index 000000000000..4f42e7bbeb94
    --- /dev/null
    +++ b/galleries/examples/mplot3d/intersecting_planes.py
    @@ -0,0 +1,95 @@
    +"""
    +===================
    +Intersecting planes
    +===================
    +
    +This examples demonstrates drawing intersecting planes in 3D. It is a generalization
    +of :doc:`/gallery/mplot3d/imshow3d`.
    +
    +Drawing intersecting planes in `.mplot3d` is complicated, because `.mplot3d` is not a
    +real 3D renderer, but only projects the Artists into 3D and draws them in the right
    +order. This does not work correctly if Artists overlap each other mutually. In this
    +example, we lift the problem of mutual overlap by segmenting the planes at their
    +intersections, making four parts out of each plane.
    +
    +This examples only works correctly for planes that cut each other in halves. This
    +limitation is intentional to keep the code more readable. Cutting at arbitrary
    +positions would of course be possible but makes the code even more complex.
    +Thus, this example is more a demonstration of the concept how to work around
    +limitations of the 3D visualization, it's not a refined solution for drawing
    +arbitrary intersecting planes, which you can copy-and-paste as is.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def plot_quadrants(ax, array, fixed_coord, cmap):
    +    """For a given 3d *array* plot a plane with *fixed_coord*, using four quadrants."""
    +    nx, ny, nz = array.shape
    +    index = {
    +        'x': (nx // 2, slice(None), slice(None)),
    +        'y': (slice(None), ny // 2, slice(None)),
    +        'z': (slice(None), slice(None), nz // 2),
    +    }[fixed_coord]
    +    plane_data = array[index]
    +
    +    n0, n1 = plane_data.shape
    +    quadrants = [
    +        plane_data[:n0 // 2, :n1 // 2],
    +        plane_data[:n0 // 2, n1 // 2:],
    +        plane_data[n0 // 2:, :n1 // 2],
    +        plane_data[n0 // 2:, n1 // 2:]
    +    ]
    +
    +    min_val = array.min()
    +    max_val = array.max()
    +
    +    cmap = plt.get_cmap(cmap)
    +
    +    for i, quadrant in enumerate(quadrants):
    +        facecolors = cmap((quadrant - min_val) / (max_val - min_val))
    +        if fixed_coord == 'x':
    +            Y, Z = np.mgrid[0:ny // 2, 0:nz // 2]
    +            X = nx // 2 * np.ones_like(Y)
    +            Y_offset = (i // 2) * ny // 2
    +            Z_offset = (i % 2) * nz // 2
    +            ax.plot_surface(X, Y + Y_offset, Z + Z_offset, rstride=1, cstride=1,
    +                            facecolors=facecolors, shade=False)
    +        elif fixed_coord == 'y':
    +            X, Z = np.mgrid[0:nx // 2, 0:nz // 2]
    +            Y = ny // 2 * np.ones_like(X)
    +            X_offset = (i // 2) * nx // 2
    +            Z_offset = (i % 2) * nz // 2
    +            ax.plot_surface(X + X_offset, Y, Z + Z_offset, rstride=1, cstride=1,
    +                            facecolors=facecolors, shade=False)
    +        elif fixed_coord == 'z':
    +            X, Y = np.mgrid[0:nx // 2, 0:ny // 2]
    +            Z = nz // 2 * np.ones_like(X)
    +            X_offset = (i // 2) * nx // 2
    +            Y_offset = (i % 2) * ny // 2
    +            ax.plot_surface(X + X_offset, Y + Y_offset, Z, rstride=1, cstride=1,
    +                            facecolors=facecolors, shade=False)
    +
    +
    +def figure_3D_array_slices(array, cmap=None):
    +    """Plot a 3d array using three intersecting centered planes."""
    +    fig = plt.figure()
    +    ax = fig.add_subplot(projection='3d')
    +    ax.set_box_aspect(array.shape)
    +    plot_quadrants(ax, array, 'x', cmap=cmap)
    +    plot_quadrants(ax, array, 'y', cmap=cmap)
    +    plot_quadrants(ax, array, 'z', cmap=cmap)
    +    return fig, ax
    +
    +
    +nx, ny, nz = 70, 100, 50
    +r_square = (np.mgrid[-1:1:1j * nx, -1:1:1j * ny, -1:1:1j * nz] ** 2).sum(0)
    +
    +figure_3D_array_slices(r_square, cmap='viridis_r')
    +plt.show()
    +
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: advanced
    diff --git a/galleries/examples/mplot3d/lines3d.py b/galleries/examples/mplot3d/lines3d.py
    new file mode 100644
    index 000000000000..ee38dade6997
    --- /dev/null
    +++ b/galleries/examples/mplot3d/lines3d.py
    @@ -0,0 +1,29 @@
    +"""
    +================
    +Parametric curve
    +================
    +
    +This example demonstrates plotting a parametric curve in 3D.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# Prepare arrays x, y, z
    +theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
    +z = np.linspace(-2, 2, 100)
    +r = z**2 + 1
    +x = r * np.sin(theta)
    +y = r * np.cos(theta)
    +
    +ax.plot(x, y, z, label='parametric curve')
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/lorenz_attractor.py b/galleries/examples/mplot3d/lorenz_attractor.py
    new file mode 100644
    index 000000000000..72d25ea544cb
    --- /dev/null
    +++ b/galleries/examples/mplot3d/lorenz_attractor.py
    @@ -0,0 +1,66 @@
    +"""
    +================
    +Lorenz attractor
    +================
    +
    +This is an example of plotting Edward Lorenz's 1963 `"Deterministic Nonperiodic
    +Flow"`_ in a 3-dimensional space using mplot3d.
    +
    +.. _"Deterministic Nonperiodic Flow":
    +   https://journals.ametsoc.org/view/journals/atsc/20/2/1520-0469_1963_020_0130_dnf_2_0_co_2.xml
    +
    +.. note::
    +   Because this is a simple non-linear ODE, it would be more easily done using
    +   SciPy's ODE solver, but this approach depends only upon NumPy.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def lorenz(xyz, *, s=10, r=28, b=2.667):
    +    """
    +    Parameters
    +    ----------
    +    xyz : array-like, shape (3,)
    +       Point of interest in three-dimensional space.
    +    s, r, b : float
    +       Parameters defining the Lorenz attractor.
    +
    +    Returns
    +    -------
    +    xyz_dot : array, shape (3,)
    +       Values of the Lorenz attractor's partial derivatives at *xyz*.
    +    """
    +    x, y, z = xyz
    +    x_dot = s*(y - x)
    +    y_dot = r*x - y - x*z
    +    z_dot = x*y - b*z
    +    return np.array([x_dot, y_dot, z_dot])
    +
    +
    +dt = 0.01
    +num_steps = 10000
    +
    +xyzs = np.empty((num_steps + 1, 3))  # Need one more for the initial values
    +xyzs[0] = (0., 1., 1.05)  # Set initial values
    +# Step through "time", calculating the partial derivatives at the current point
    +# and using them to estimate the next point
    +for i in range(num_steps):
    +    xyzs[i + 1] = xyzs[i] + lorenz(xyzs[i]) * dt
    +
    +# Plot
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +ax.plot(*xyzs.T, lw=0.5)
    +ax.set_xlabel("X Axis")
    +ax.set_ylabel("Y Axis")
    +ax.set_zlabel("Z Axis")
    +ax.set_title("Lorenz Attractor")
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/mixed_subplots.py b/galleries/examples/mplot3d/mixed_subplots.py
    new file mode 100644
    index 000000000000..a38fd2e10a2b
    --- /dev/null
    +++ b/galleries/examples/mplot3d/mixed_subplots.py
    @@ -0,0 +1,52 @@
    +"""
    +=============================
    +2D and 3D Axes in same figure
    +=============================
    +
    +This example shows a how to plot a 2D and a 3D plot on the same figure.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def f(t):
    +    return np.cos(2*np.pi*t) * np.exp(-t)
    +
    +
    +# Set up a figure twice as tall as it is wide
    +fig = plt.figure(figsize=plt.figaspect(2.))
    +fig.suptitle('A tale of 2 subplots')
    +
    +# First subplot
    +ax = fig.add_subplot(2, 1, 1)
    +
    +t1 = np.arange(0.0, 5.0, 0.1)
    +t2 = np.arange(0.0, 5.0, 0.02)
    +t3 = np.arange(0.0, 2.0, 0.01)
    +
    +ax.plot(t1, f(t1), 'bo',
    +        t2, f(t2), 'k--', markerfacecolor='green')
    +ax.grid(True)
    +ax.set_ylabel('Damped oscillation')
    +
    +# Second subplot
    +ax = fig.add_subplot(2, 1, 2, projection='3d')
    +
    +X = np.arange(-5, 5, 0.25)
    +Y = np.arange(-5, 5, 0.25)
    +X, Y = np.meshgrid(X, Y)
    +R = np.sqrt(X**2 + Y**2)
    +Z = np.sin(R)
    +
    +surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
    +                       linewidth=0, antialiased=False)
    +ax.set_zlim(-1, 1)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: subplot,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/offset.py b/galleries/examples/mplot3d/offset.py
    new file mode 100644
    index 000000000000..4c5e4b06b62b
    --- /dev/null
    +++ b/galleries/examples/mplot3d/offset.py
    @@ -0,0 +1,38 @@
    +"""
    +=========================
    +Automatic text offsetting
    +=========================
    +
    +This example demonstrates mplot3d's offset text display.
    +As one rotates the 3D figure, the offsets should remain oriented the
    +same way as the axis label, and should also be located "away"
    +from the center of the plot.
    +
    +This demo triggers the display of the offset text for the x- and
    +y-axis by adding 1e5 to X and Y. Anything less would not
    +automatically trigger it.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +X, Y = np.mgrid[0:6*np.pi:0.25, 0:4*np.pi:0.25]
    +Z = np.sqrt(np.abs(np.cos(X) + np.cos(Y)))
    +
    +ax.plot_surface(X + 1e5, Y + 1e5, Z, cmap='autumn', cstride=2, rstride=2)
    +
    +ax.set_xlabel("X label")
    +ax.set_ylabel("Y label")
    +ax.set_zlabel("Z label")
    +ax.set_zlim(0, 2)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: label,
    +#    interactivity: pan,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/pathpatch3d.py b/galleries/examples/mplot3d/pathpatch3d.py
    new file mode 100644
    index 000000000000..8cb7c4951809
    --- /dev/null
    +++ b/galleries/examples/mplot3d/pathpatch3d.py
    @@ -0,0 +1,77 @@
    +"""
    +============================
    +Draw flat objects in 3D plot
    +============================
    +
    +Demonstrate using `.pathpatch_2d_to_3d` to 'draw' shapes and text on a 3D plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Circle, PathPatch
    +from matplotlib.text import TextPath
    +from matplotlib.transforms import Affine2D
    +import mpl_toolkits.mplot3d.art3d as art3d
    +
    +
    +def text3d(ax, xyz, s, zdir="z", size=None, angle=0, usetex=False, **kwargs):
    +    """
    +    Plots the string *s* on the Axes *ax*, with position *xyz*, size *size*,
    +    and rotation angle *angle*. *zdir* gives the axis which is to be treated as
    +    the third dimension. *usetex* is a boolean indicating whether the string
    +    should be run through a LaTeX subprocess or not.  Any additional keyword
    +    arguments are forwarded to `.transform_path`.
    +
    +    Note: zdir affects the interpretation of xyz.
    +    """
    +    x, y, z = xyz
    +    if zdir == "y":
    +        xy1, z1 = (x, z), y
    +    elif zdir == "x":
    +        xy1, z1 = (y, z), x
    +    else:
    +        xy1, z1 = (x, y), z
    +
    +    text_path = TextPath((0, 0), s, size=size, usetex=usetex)
    +    trans = Affine2D().rotate(angle).translate(xy1[0], xy1[1])
    +
    +    p1 = PathPatch(trans.transform_path(text_path), **kwargs)
    +    ax.add_patch(p1)
    +    art3d.pathpatch_2d_to_3d(p1, z=z1, zdir=zdir)
    +
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Draw a circle on the x=0 'wall'
    +p = Circle((5, 5), 3)
    +ax.add_patch(p)
    +art3d.pathpatch_2d_to_3d(p, z=0, zdir="x")
    +
    +# Manually label the axes
    +text3d(ax, (4, -2, 0), "X-axis", zdir="z", size=.5, usetex=False,
    +       ec="none", fc="k")
    +text3d(ax, (12, 4, 0), "Y-axis", zdir="z", size=.5, usetex=False,
    +       angle=np.pi / 2, ec="none", fc="k")
    +text3d(ax, (12, 10, 4), "Z-axis", zdir="y", size=.5, usetex=False,
    +       angle=np.pi / 2, ec="none", fc="k")
    +
    +# Write a Latex formula on the z=0 'floor'
    +text3d(ax, (1, 5, 0),
    +       r"$\displaystyle G_{\mu\nu} + \Lambda g_{\mu\nu} = "
    +       r"\frac{8\pi G}{c^4} T_{\mu\nu}  $",
    +       zdir="z", size=1, usetex=True,
    +       ec="none", fc="k")
    +
    +ax.set_xlim(0, 10)
    +ax.set_ylim(0, 10)
    +ax.set_zlim(0, 10)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: label,
    +#    level: advanced
    diff --git a/galleries/examples/mplot3d/polys3d.py b/galleries/examples/mplot3d/polys3d.py
    new file mode 100644
    index 000000000000..19979ceddaa5
    --- /dev/null
    +++ b/galleries/examples/mplot3d/polys3d.py
    @@ -0,0 +1,41 @@
    +"""
    +====================
    +Generate 3D polygons
    +====================
    +
    +Demonstrate how to create polygons in 3D. Here we stack 3 hexagons.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    +
    +# Coordinates of a hexagon
    +angles = np.linspace(0, 2 * np.pi, 6, endpoint=False)
    +x = np.cos(angles)
    +y = np.sin(angles)
    +zs = [-3, -2, -1]
    +
    +# Close the hexagon by repeating the first vertex
    +x = np.append(x, x[0])
    +y = np.append(y, y[0])
    +
    +verts = []
    +for z in zs:
    +    verts.append(list(zip(x*z, y*z, np.full_like(x, z))))
    +verts = np.array(verts)
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +poly = Poly3DCollection(verts, alpha=.7)
    +ax.add_collection3d(poly)
    +ax.set_aspect('equalxy')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: colormap,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/projections.py b/galleries/examples/mplot3d/projections.py
    new file mode 100644
    index 000000000000..ff9d88ccb5cd
    --- /dev/null
    +++ b/galleries/examples/mplot3d/projections.py
    @@ -0,0 +1,62 @@
    +"""
    +========================
    +3D plot projection types
    +========================
    +
    +Demonstrates the different camera projections for 3D plots, and the effects of
    +changing the focal length for a perspective projection. Note that Matplotlib
    +corrects for the 'zoom' effect of changing the focal length.
    +
    +The default focal length of 1 corresponds to a Field of View (FOV) of 90 deg.
    +An increased focal length between 1 and infinity "flattens" the image, while a
    +decreased focal length between 1 and 0 exaggerates the perspective and gives
    +the image more apparent depth. In the limiting case, a focal length of
    +infinity corresponds to an orthographic projection after correction of the
    +zoom effect.
    +
    +You can calculate focal length from a FOV via the equation:
    +
    +.. math::
    +
    +    1 / \\tan (\\mathrm{FOV} / 2)
    +
    +Or vice versa:
    +
    +.. math::
    +
    +    \\mathrm{FOV} = 2 \\arctan (1 / \\mathrm{focal length})
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +fig, axs = plt.subplots(1, 3, subplot_kw={'projection': '3d'})
    +
    +# Get the test data
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +# Plot the data
    +for ax in axs:
    +    ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
    +
    +# Set the orthographic projection.
    +axs[0].set_proj_type('ortho')  # FOV = 0 deg
    +axs[0].set_title("'ortho'\nfocal_length = ∞", fontsize=10)
    +
    +# Set the perspective projections
    +axs[1].set_proj_type('persp')  # FOV = 90 deg
    +axs[1].set_title("'persp'\nfocal_length = 1 (default)", fontsize=10)
    +
    +axs[2].set_proj_type('persp', focal_length=0.2)  # FOV = 157.4 deg
    +axs[2].set_title("'persp'\nfocal_length = 0.2", fontsize=10)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: small-multiples,
    +#    component: subplot,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/quiver3d.py b/galleries/examples/mplot3d/quiver3d.py
    new file mode 100644
    index 000000000000..adc58c2e9d89
    --- /dev/null
    +++ b/galleries/examples/mplot3d/quiver3d.py
    @@ -0,0 +1,32 @@
    +"""
    +==============
    +3D quiver plot
    +==============
    +
    +Demonstrates plotting directional arrows at points on a 3D meshgrid.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# Make the grid
    +x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
    +                      np.arange(-0.8, 1, 0.2),
    +                      np.arange(-0.8, 1, 0.8))
    +
    +# Make the direction data for the arrows
    +u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
    +v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
    +w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) *
    +     np.sin(np.pi * z))
    +
    +ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/rotate_axes3d_sgskip.py b/galleries/examples/mplot3d/rotate_axes3d_sgskip.py
    new file mode 100644
    index 000000000000..76a3369a20d6
    --- /dev/null
    +++ b/galleries/examples/mplot3d/rotate_axes3d_sgskip.py
    @@ -0,0 +1,58 @@
    +"""
    +==================
    +Rotating a 3D plot
    +==================
    +
    +A very simple animation of a rotating 3D plot about all three axes.
    +
    +See :doc:`wire3d_animation_sgskip` for another example of animating a 3D plot.
    +
    +(This example is skipped when building the documentation gallery because it
    +intentionally takes a long time to run)
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Grab some example data and plot a basic wireframe.
    +X, Y, Z = axes3d.get_test_data(0.05)
    +ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
    +
    +# Set the axis labels
    +ax.set_xlabel('x')
    +ax.set_ylabel('y')
    +ax.set_zlabel('z')
    +
    +# Rotate the axes and update
    +for angle in range(0, 360*4 + 1):
    +    # Normalize the angle to the range [-180, 180] for display
    +    angle_norm = (angle + 180) % 360 - 180
    +
    +    # Cycle through a full rotation of elevation, then azimuth, roll, and all
    +    elev = azim = roll = 0
    +    if angle <= 360:
    +        elev = angle_norm
    +    elif angle <= 360*2:
    +        azim = angle_norm
    +    elif angle <= 360*3:
    +        roll = angle_norm
    +    else:
    +        elev = azim = roll = angle_norm
    +
    +    # Update the axis view and title
    +    ax.view_init(elev, azim, roll)
    +    plt.title('Elevation: %d°, Azimuth: %d°, Roll: %d°' % (elev, azim, roll))
    +
    +    plt.draw()
    +    plt.pause(.001)
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: animation,
    +#    level: advanced,
    +#    internal: high-bandwidth
    diff --git a/galleries/examples/mplot3d/scatter3d.py b/galleries/examples/mplot3d/scatter3d.py
    new file mode 100644
    index 000000000000..0fc9bf3fe8da
    --- /dev/null
    +++ b/galleries/examples/mplot3d/scatter3d.py
    @@ -0,0 +1,45 @@
    +"""
    +==============
    +3D scatterplot
    +==============
    +
    +Demonstration of a basic scatterplot in 3D.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def randrange(n, vmin, vmax):
    +    """
    +    Helper function to make an array of random numbers having shape (n, )
    +    with each number distributed Uniform(vmin, vmax).
    +    """
    +    return (vmax - vmin)*np.random.rand(n) + vmin
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +n = 100
    +
    +# For each set of style and range settings, plot n random points in the box
    +# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh].
    +for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]:
    +    xs = randrange(n, 23, 32)
    +    ys = randrange(n, 0, 100)
    +    zs = randrange(n, zlow, zhigh)
    +    ax.scatter(xs, ys, zs, marker=m)
    +
    +ax.set_xlabel('X Label')
    +ax.set_ylabel('Y Label')
    +ax.set_zlabel('Z Label')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: scatter,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/stem3d_demo.py b/galleries/examples/mplot3d/stem3d_demo.py
    new file mode 100644
    index 000000000000..82b45ff19068
    --- /dev/null
    +++ b/galleries/examples/mplot3d/stem3d_demo.py
    @@ -0,0 +1,56 @@
    +"""
    +=======
    +3D stem
    +=======
    +
    +Demonstration of a stem plot in 3D, which plots vertical lines from a baseline
    +to the *z*-coordinate and places a marker at the tip.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +theta = np.linspace(0, 2*np.pi)
    +x = np.cos(theta - np.pi/2)
    +y = np.sin(theta - np.pi/2)
    +z = theta
    +
    +fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
    +ax.stem(x, y, z)
    +
    +plt.show()
    +
    +# %%
    +#
    +# The position of the baseline can be adapted using *bottom*. The parameters
    +# *linefmt*, *markerfmt*, and *basefmt* control basic format properties of the
    +# plot. However, in contrast to `~.axes3d.Axes3D.plot` not all properties are
    +# configurable via keyword arguments. For more advanced control adapt the line
    +# objects returned by `~mpl_toolkits.mplot3d.axes3d.Axes3D.stem`.
    +
    +fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
    +markerline, stemlines, baseline = ax.stem(
    +    x, y, z, linefmt='grey', markerfmt='D', bottom=np.pi)
    +markerline.set_markerfacecolor('none')
    +
    +plt.show()
    +
    +# %%
    +#
    +# The orientation of the stems and baseline can be changed using *orientation*.
    +# This determines in which direction the stems are projected from the head
    +# points, towards the *bottom* baseline.
    +#
    +# For examples, by setting ``orientation='x'``, the stems are projected along
    +# the *x*-direction, and the baseline is in the *yz*-plane.
    +
    +fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
    +markerline, stemlines, baseline = ax.stem(x, y, z, bottom=-1, orientation='x')
    +ax.set(xlabel='x', ylabel='y', zlabel='z')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: specialty,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/subplot3d.py b/galleries/examples/mplot3d/subplot3d.py
    new file mode 100644
    index 000000000000..bdca8a309c2d
    --- /dev/null
    +++ b/galleries/examples/mplot3d/subplot3d.py
    @@ -0,0 +1,50 @@
    +"""
    +====================
    +3D plots as subplots
    +====================
    +
    +Demonstrate including 3D plots as subplots.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from mpl_toolkits.mplot3d.axes3d import get_test_data
    +
    +# set up a figure twice as wide as it is tall
    +fig = plt.figure(figsize=plt.figaspect(0.5))
    +
    +# =============
    +# First subplot
    +# =============
    +# set up the Axes for the first plot
    +ax = fig.add_subplot(1, 2, 1, projection='3d')
    +
    +# plot a 3D surface like in the example mplot3d/surface3d_demo
    +X = np.arange(-5, 5, 0.25)
    +Y = np.arange(-5, 5, 0.25)
    +X, Y = np.meshgrid(X, Y)
    +R = np.sqrt(X**2 + Y**2)
    +Z = np.sin(R)
    +surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap="coolwarm",
    +                       linewidth=0, antialiased=False)
    +ax.set_zlim(-1.01, 1.01)
    +fig.colorbar(surf, shrink=0.5, aspect=10)
    +
    +# ==============
    +# Second subplot
    +# ==============
    +# set up the Axes for the second plot
    +ax = fig.add_subplot(1, 2, 2, projection='3d')
    +
    +# plot a 3D wireframe like in the example mplot3d/wire3d_demo
    +X, Y, Z = get_test_data(0.05)
    +ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: subplot,
    +#    level: advanced
    diff --git a/galleries/examples/mplot3d/surface3d.py b/galleries/examples/mplot3d/surface3d.py
    new file mode 100644
    index 000000000000..6c51a69c0d1f
    --- /dev/null
    +++ b/galleries/examples/mplot3d/surface3d.py
    @@ -0,0 +1,59 @@
    +"""
    +=====================
    +3D surface (colormap)
    +=====================
    +
    +Demonstrates plotting a 3D surface colored with the coolwarm colormap.
    +The surface is made opaque by using ``antialiased=False``.
    +
    +Also demonstrates using the `.LinearLocator` and custom formatting for the
    +z axis tick labels.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.ticker import LinearLocator
    +
    +fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    +
    +# Make data.
    +X = np.arange(-5, 5, 0.25)
    +Y = np.arange(-5, 5, 0.25)
    +X, Y = np.meshgrid(X, Y)
    +R = np.sqrt(X**2 + Y**2)
    +Z = np.sin(R)
    +
    +# Plot the surface.
    +surf = ax.plot_surface(X, Y, Z, cmap="coolwarm",
    +                       linewidth=0, antialiased=False)
    +
    +# Customize the z axis.
    +ax.set_zlim(-1.01, 1.01)
    +ax.zaxis.set_major_locator(LinearLocator(10))
    +# A StrMethodFormatter is used automatically
    +ax.zaxis.set_major_formatter('{x:.02f}')
    +
    +# Add a color bar which maps values to colors.
    +fig.colorbar(surf, shrink=0.5, aspect=5)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.ticker.LinearLocator`
    +#    - `matplotlib.ticker.StrMethodFormatter`
    +#
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: colormap,
    +#    level: advanced
    diff --git a/galleries/examples/mplot3d/surface3d_2.py b/galleries/examples/mplot3d/surface3d_2.py
    new file mode 100644
    index 000000000000..2a4406abc259
    --- /dev/null
    +++ b/galleries/examples/mplot3d/surface3d_2.py
    @@ -0,0 +1,33 @@
    +"""
    +========================
    +3D surface (solid color)
    +========================
    +
    +Demonstrates a very basic plot of a 3D surface using a solid color.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Make data
    +u = np.linspace(0, 2 * np.pi, 100)
    +v = np.linspace(0, np.pi, 100)
    +x = 10 * np.outer(np.cos(u), np.sin(v))
    +y = 10 * np.outer(np.sin(u), np.sin(v))
    +z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
    +
    +# Plot the surface
    +ax.plot_surface(x, y, z)
    +
    +# Set an equal aspect ratio
    +ax.set_aspect('equal')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/surface3d_3.py b/galleries/examples/mplot3d/surface3d_3.py
    new file mode 100644
    index 000000000000..c129ef6d3635
    --- /dev/null
    +++ b/galleries/examples/mplot3d/surface3d_3.py
    @@ -0,0 +1,46 @@
    +"""
    +=========================
    +3D surface (checkerboard)
    +=========================
    +
    +Demonstrates plotting a 3D surface colored in a checkerboard pattern.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.ticker import LinearLocator
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# Make data.
    +X = np.arange(-5, 5, 0.25)
    +xlen = len(X)
    +Y = np.arange(-5, 5, 0.25)
    +ylen = len(Y)
    +X, Y = np.meshgrid(X, Y)
    +R = np.sqrt(X**2 + Y**2)
    +Z = np.sin(R)
    +
    +# Create an empty array of strings with the same shape as the meshgrid, and
    +# populate it with two colors in a checkerboard pattern.
    +colortuple = ('y', 'b')
    +colors = np.empty(X.shape, dtype=str)
    +for y in range(ylen):
    +    for x in range(xlen):
    +        colors[y, x] = colortuple[(x + y) % len(colortuple)]
    +
    +# Plot the surface with face colors taken from the array we made.
    +surf = ax.plot_surface(X, Y, Z, facecolors=colors, linewidth=0)
    +
    +# Customize the z axis.
    +ax.set_zlim(-1, 1)
    +ax.zaxis.set_major_locator(LinearLocator(6))
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: color, styling: texture,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/surface3d_radial.py b/galleries/examples/mplot3d/surface3d_radial.py
    new file mode 100644
    index 000000000000..382734d98a96
    --- /dev/null
    +++ b/galleries/examples/mplot3d/surface3d_radial.py
    @@ -0,0 +1,42 @@
    +"""
    +=================================
    +3D surface with polar coordinates
    +=================================
    +
    +Demonstrates plotting a surface defined in polar coordinates.
    +Uses the reversed version of the YlGnBu colormap.
    +Also demonstrates writing axis labels with latex math mode.
    +
    +Example contributed by Armin Moser.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Create the mesh in polar coordinates and compute corresponding Z.
    +r = np.linspace(0, 1.25, 50)
    +p = np.linspace(0, 2*np.pi, 50)
    +R, P = np.meshgrid(r, p)
    +Z = ((R**2 - 1)**2)
    +
    +# Express the mesh in the cartesian system.
    +X, Y = R*np.cos(P), R*np.sin(P)
    +
    +# Plot the surface.
    +ax.plot_surface(X, Y, Z, cmap="YlGnBu_r")
    +
    +# Tweak the limits and add latex math labels.
    +ax.set_zlim(0, 1)
    +ax.set_xlabel(r'$\phi_\mathrm{real}$')
    +ax.set_ylabel(r'$\phi_\mathrm{im}$')
    +ax.set_zlabel(r'$V(\phi)$')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: polar,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/text3d.py b/galleries/examples/mplot3d/text3d.py
    new file mode 100644
    index 000000000000..881ecfaf406e
    --- /dev/null
    +++ b/galleries/examples/mplot3d/text3d.py
    @@ -0,0 +1,52 @@
    +"""
    +======================
    +Text annotations in 3D
    +======================
    +
    +Demonstrates the placement of text annotations on a 3D plot.
    +
    +Functionality shown:
    +
    +- Using the `~.Axes3D.text` function with three types of *zdir* values: None,
    +  an axis name (ex. 'x'), or a direction tuple (ex. (1, 1, 0)).
    +- Using the `~.Axes3D.text` function with the color keyword.
    +- Using the `.text2D` function to place text on a fixed position on the ax
    +  object.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +# Demo 1: zdir
    +zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1))
    +xs = (1, 4, 4, 9, 4, 1)
    +ys = (2, 5, 8, 10, 1, 2)
    +zs = (10, 3, 8, 9, 1, 8)
    +
    +for zdir, x, y, z in zip(zdirs, xs, ys, zs):
    +    label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir)
    +    ax.text(x, y, z, label, zdir)
    +
    +# Demo 2: color
    +ax.text(9, 0, 0, "red", color='red')
    +
    +# Demo 3: text2D
    +# Placement 0, 0 would be the bottom left, 1, 1 would be the top right.
    +ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes)
    +
    +# Tweaking display region and labels
    +ax.set_xlim(0, 10)
    +ax.set_ylim(0, 10)
    +ax.set_zlim(0, 10)
    +ax.set_xlabel('X axis')
    +ax.set_ylabel('Y axis')
    +ax.set_zlabel('Z axis')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: annotation,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/tricontour3d.py b/galleries/examples/mplot3d/tricontour3d.py
    new file mode 100644
    index 000000000000..d90852003536
    --- /dev/null
    +++ b/galleries/examples/mplot3d/tricontour3d.py
    @@ -0,0 +1,50 @@
    +"""
    +==========================
    +Triangular 3D contour plot
    +==========================
    +
    +Contour plots of unstructured triangular grids.
    +
    +The data used is the same as in the second plot of :doc:`trisurf3d_2`.
    +:doc:`tricontourf3d` shows the filled version of this example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +n_angles = 48
    +n_radii = 8
    +min_radius = 0.25
    +
    +# Create the mesh in polar coordinates and compute x, y, z.
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi/n_angles
    +
    +x = (radii*np.cos(angles)).flatten()
    +y = (radii*np.sin(angles)).flatten()
    +z = (np.cos(radii)*np.cos(3*angles)).flatten()
    +
    +# Create a custom triangulation.
    +triang = tri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.tricontour(triang, z, cmap="CMRmap")
    +
    +# Customize the view angle so it's easier to understand the plot.
    +ax.view_init(elev=45.)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: specialty,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/tricontourf3d.py b/galleries/examples/mplot3d/tricontourf3d.py
    new file mode 100644
    index 000000000000..49be57456d91
    --- /dev/null
    +++ b/galleries/examples/mplot3d/tricontourf3d.py
    @@ -0,0 +1,51 @@
    +"""
    +=================================
    +Triangular 3D filled contour plot
    +=================================
    +
    +Filled contour plots of unstructured triangular grids.
    +
    +The data used is the same as in the second plot of :doc:`trisurf3d_2`.
    +:doc:`tricontour3d` shows the unfilled version of this example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as tri
    +
    +# First create the x, y, z coordinates of the points.
    +n_angles = 48
    +n_radii = 8
    +min_radius = 0.25
    +
    +# Create the mesh in polar coordinates and compute x, y, z.
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi/n_angles
    +
    +x = (radii*np.cos(angles)).flatten()
    +y = (radii*np.sin(angles)).flatten()
    +z = (np.cos(radii)*np.cos(3*angles)).flatten()
    +
    +# Create a custom triangulation.
    +triang = tri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
    +                         y[triang.triangles].mean(axis=1))
    +                < min_radius)
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.tricontourf(triang, z, cmap="CMRmap")
    +
    +# Customize the view angle so it's easier to understand the plot.
    +ax.view_init(elev=45.)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: specialty,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/trisurf3d.py b/galleries/examples/mplot3d/trisurf3d.py
    new file mode 100644
    index 000000000000..f4e7444a4311
    --- /dev/null
    +++ b/galleries/examples/mplot3d/trisurf3d.py
    @@ -0,0 +1,37 @@
    +"""
    +======================
    +Triangular 3D surfaces
    +======================
    +
    +Plot a 3D surface with a triangular mesh.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +n_radii = 8
    +n_angles = 36
    +
    +# Make radii and angles spaces (radius r=0 omitted to eliminate duplication).
    +radii = np.linspace(0.125, 1.0, n_radii)
    +angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
    +
    +# Convert polar (radii, angles) coords to cartesian (x, y) coords.
    +# (0, 0) is manually added at this stage,  so there will be no duplicate
    +# points in the (x, y) plane.
    +x = np.append(0, (radii*np.cos(angles)).flatten())
    +y = np.append(0, (radii*np.sin(angles)).flatten())
    +
    +# Compute z to make the pringle surface.
    +z = np.sin(-x*y)
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +
    +ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/trisurf3d_2.py b/galleries/examples/mplot3d/trisurf3d_2.py
    new file mode 100644
    index 000000000000..0e757140c20e
    --- /dev/null
    +++ b/galleries/examples/mplot3d/trisurf3d_2.py
    @@ -0,0 +1,84 @@
    +"""
    +===========================
    +More triangular 3D surfaces
    +===========================
    +
    +Two additional examples of plotting surfaces with triangular mesh.
    +
    +The first demonstrates use of plot_trisurf's triangles argument, and the
    +second sets a `.Triangulation` object's mask and passes the object directly
    +to plot_trisurf.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.tri as mtri
    +
    +fig = plt.figure(figsize=plt.figaspect(0.5))
    +
    +# ==========
    +# First plot
    +# ==========
    +
    +# Make a mesh in the space of parameterisation variables u and v
    +u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=50)
    +v = np.linspace(-0.5, 0.5, endpoint=True, num=10)
    +u, v = np.meshgrid(u, v)
    +u, v = u.flatten(), v.flatten()
    +
    +# This is the Mobius mapping, taking a u, v pair and returning an x, y, z
    +# triple
    +x = (1 + 0.5 * v * np.cos(u / 2.0)) * np.cos(u)
    +y = (1 + 0.5 * v * np.cos(u / 2.0)) * np.sin(u)
    +z = 0.5 * v * np.sin(u / 2.0)
    +
    +# Triangulate parameter space to determine the triangles
    +tri = mtri.Triangulation(u, v)
    +
    +# Plot the surface.  The triangles in parameter space determine which x, y, z
    +# points are connected by an edge.
    +ax = fig.add_subplot(1, 2, 1, projection='3d')
    +ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap="Spectral")
    +ax.set_zlim(-1, 1)
    +
    +
    +# ===========
    +# Second plot
    +# ===========
    +
    +# Make parameter spaces radii and angles.
    +n_angles = 36
    +n_radii = 8
    +min_radius = 0.25
    +radii = np.linspace(min_radius, 0.95, n_radii)
    +
    +angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
    +angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    +angles[:, 1::2] += np.pi/n_angles
    +
    +# Map radius, angle pairs to x, y, z points.
    +x = (radii*np.cos(angles)).flatten()
    +y = (radii*np.sin(angles)).flatten()
    +z = (np.cos(radii)*np.cos(3*angles)).flatten()
    +
    +# Create the Triangulation; no triangles so Delaunay triangulation created.
    +triang = mtri.Triangulation(x, y)
    +
    +# Mask off unwanted triangles.
    +xmid = x[triang.triangles].mean(axis=1)
    +ymid = y[triang.triangles].mean(axis=1)
    +mask = xmid**2 + ymid**2 < min_radius**2
    +triang.set_mask(mask)
    +
    +# Plot the surface.
    +ax = fig.add_subplot(1, 2, 2, projection='3d')
    +ax.plot_trisurf(triang, z, cmap="CMRmap")
    +
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D, plot-type: specialty,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/view_planes_3d.py b/galleries/examples/mplot3d/view_planes_3d.py
    new file mode 100644
    index 000000000000..1cac9d61ad1f
    --- /dev/null
    +++ b/galleries/examples/mplot3d/view_planes_3d.py
    @@ -0,0 +1,63 @@
    +"""
    +======================
    +Primary 3D view planes
    +======================
    +
    +This example generates an "unfolded" 3D plot that shows each of the primary 3D
    +view planes. The elevation, azimuth, and roll angles required for each view are
    +labeled. You could print out this image and fold it into a box where each plane
    +forms a side of the box.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def annotate_axes(ax, text, fontsize=18):
    +    ax.text(x=0.5, y=0.5, z=0.5, s=text,
    +            va="center", ha="center", fontsize=fontsize, color="black")
    +
    +# (plane, (elev, azim, roll))
    +views = [('XY',   (90, -90, 0)),
    +         ('XZ',    (0, -90, 0)),
    +         ('YZ',    (0,   0, 0)),
    +         ('-XY', (-90,  90, 0)),
    +         ('-XZ',   (0,  90, 0)),
    +         ('-YZ',   (0, 180, 0))]
    +
    +layout = [['XY',  '.',   'L',   '.'],
    +          ['XZ', 'YZ', '-XZ', '-YZ'],
    +          ['.',   '.', '-XY',   '.']]
    +fig, axd = plt.subplot_mosaic(layout, subplot_kw={'projection': '3d'},
    +                              figsize=(12, 8.5))
    +for plane, angles in views:
    +    axd[plane].set_xlabel('x')
    +    axd[plane].set_ylabel('y')
    +    axd[plane].set_zlabel('z')
    +    axd[plane].set_proj_type('ortho')
    +    axd[plane].view_init(elev=angles[0], azim=angles[1], roll=angles[2])
    +    axd[plane].set_box_aspect(None, zoom=1.25)
    +
    +    label = f'{plane}\n{angles}'
    +    annotate_axes(axd[plane], label, fontsize=14)
    +
    +for plane in ('XY', '-XY'):
    +    axd[plane].set_zticklabels([])
    +    axd[plane].set_zlabel('')
    +for plane in ('XZ', '-XZ'):
    +    axd[plane].set_yticklabels([])
    +    axd[plane].set_ylabel('')
    +for plane in ('YZ', '-YZ'):
    +    axd[plane].set_xticklabels([])
    +    axd[plane].set_xlabel('')
    +
    +label = 'mplot3d primary view planes\n' + 'ax.view_init(elev, azim, roll)'
    +annotate_axes(axd['L'], label, fontsize=18)
    +axd['L'].set_axis_off()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: axes, component: subplot,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/voxels.py b/galleries/examples/mplot3d/voxels.py
    new file mode 100644
    index 000000000000..ec9f0f413f3a
    --- /dev/null
    +++ b/galleries/examples/mplot3d/voxels.py
    @@ -0,0 +1,39 @@
    +"""
    +==========================
    +3D voxel / volumetric plot
    +==========================
    +
    +Demonstrates plotting 3D volumetric objects with `.Axes3D.voxels`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# prepare some coordinates
    +x, y, z = np.indices((8, 8, 8))
    +
    +# draw cuboids in the top left and bottom right corners, and a link between
    +# them
    +cube1 = (x < 3) & (y < 3) & (z < 3)
    +cube2 = (x >= 5) & (y >= 5) & (z >= 5)
    +link = abs(x - y) + abs(y - z) + abs(z - x) <= 2
    +
    +# combine the objects into a single boolean array
    +voxelarray = cube1 | cube2 | link
    +
    +# set the colors of each object
    +colors = np.empty(voxelarray.shape, dtype=object)
    +colors[link] = 'red'
    +colors[cube1] = 'blue'
    +colors[cube2] = 'green'
    +
    +# and plot everything
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.voxels(voxelarray, facecolors=colors, edgecolor='k')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/voxels_numpy_logo.py b/galleries/examples/mplot3d/voxels_numpy_logo.py
    new file mode 100644
    index 000000000000..c128f055cbe6
    --- /dev/null
    +++ b/galleries/examples/mplot3d/voxels_numpy_logo.py
    @@ -0,0 +1,53 @@
    +"""
    +===============================
    +3D voxel plot of the NumPy logo
    +===============================
    +
    +Demonstrates using `.Axes3D.voxels` with uneven coordinates.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def explode(data):
    +    size = np.array(data.shape)*2
    +    data_e = np.zeros(size - 1, dtype=data.dtype)
    +    data_e[::2, ::2, ::2] = data
    +    return data_e
    +
    +# build up the numpy logo
    +n_voxels = np.zeros((4, 3, 4), dtype=bool)
    +n_voxels[0, 0, :] = True
    +n_voxels[-1, 0, :] = True
    +n_voxels[1, 0, 2] = True
    +n_voxels[2, 0, 1] = True
    +facecolors = np.where(n_voxels, '#FFD65DC0', '#7A88CCC0')
    +edgecolors = np.where(n_voxels, '#BFAB6E', '#7D84A6')
    +filled = np.ones(n_voxels.shape)
    +
    +# upscale the above voxel image, leaving gaps
    +filled_2 = explode(filled)
    +fcolors_2 = explode(facecolors)
    +ecolors_2 = explode(edgecolors)
    +
    +# Shrink the gaps
    +x, y, z = np.indices(np.array(filled_2.shape) + 1).astype(float) // 2
    +x[0::2, :, :] += 0.05
    +y[:, 0::2, :] += 0.05
    +z[:, :, 0::2] += 0.05
    +x[1::2, :, :] += 0.95
    +y[:, 1::2, :] += 0.95
    +z[:, :, 1::2] += 0.95
    +
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.voxels(x, y, z, filled_2, facecolors=fcolors_2, edgecolors=ecolors_2)
    +ax.set_aspect('equal')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner,
    +#    purpose: fun
    diff --git a/galleries/examples/mplot3d/voxels_rgb.py b/galleries/examples/mplot3d/voxels_rgb.py
    new file mode 100644
    index 000000000000..6f201b08b386
    --- /dev/null
    +++ b/galleries/examples/mplot3d/voxels_rgb.py
    @@ -0,0 +1,49 @@
    +"""
    +==========================================
    +3D voxel / volumetric plot with RGB colors
    +==========================================
    +
    +Demonstrates using `.Axes3D.voxels` to visualize parts of a color space.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def midpoints(x):
    +    sl = ()
    +    for _ in range(x.ndim):
    +        x = (x[sl + np.index_exp[:-1]] + x[sl + np.index_exp[1:]]) / 2.0
    +        sl += np.index_exp[:]
    +    return x
    +
    +# prepare some coordinates, and attach rgb values to each
    +r, g, b = np.indices((17, 17, 17)) / 16.0
    +rc = midpoints(r)
    +gc = midpoints(g)
    +bc = midpoints(b)
    +
    +# define a sphere about [0.5, 0.5, 0.5]
    +sphere = (rc - 0.5)**2 + (gc - 0.5)**2 + (bc - 0.5)**2 < 0.5**2
    +
    +# combine the color components
    +colors = np.zeros(sphere.shape + (3,))
    +colors[..., 0] = rc
    +colors[..., 1] = gc
    +colors[..., 2] = bc
    +
    +# and plot everything
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.voxels(r, g, b, sphere,
    +          facecolors=colors,
    +          edgecolors=np.clip(2*colors - 0.5, 0, 1),  # brighter
    +          linewidth=0.5)
    +ax.set(xlabel='r', ylabel='g', zlabel='b')
    +ax.set_aspect('equal')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: color
    diff --git a/galleries/examples/mplot3d/voxels_torus.py b/galleries/examples/mplot3d/voxels_torus.py
    new file mode 100644
    index 000000000000..db0fdbc6ea4d
    --- /dev/null
    +++ b/galleries/examples/mplot3d/voxels_torus.py
    @@ -0,0 +1,52 @@
    +"""
    +=======================================================
    +3D voxel / volumetric plot with cylindrical coordinates
    +=======================================================
    +
    +Demonstrates using the *x*, *y*, *z* parameters of `.Axes3D.voxels`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors
    +
    +
    +def midpoints(x):
    +    sl = ()
    +    for i in range(x.ndim):
    +        x = (x[sl + np.index_exp[:-1]] + x[sl + np.index_exp[1:]]) / 2.0
    +        sl += np.index_exp[:]
    +    return x
    +
    +# prepare some coordinates, and attach rgb values to each
    +r, theta, z = np.mgrid[0:1:11j, 0:np.pi*2:25j, -0.5:0.5:11j]
    +x = r*np.cos(theta)
    +y = r*np.sin(theta)
    +
    +rc, thetac, zc = midpoints(r), midpoints(theta), midpoints(z)
    +
    +# define a wobbly torus about [0.7, *, 0]
    +sphere = (rc - 0.7)**2 + (zc + 0.2*np.cos(thetac*2))**2 < 0.2**2
    +
    +# combine the color components
    +hsv = np.zeros(sphere.shape + (3,))
    +hsv[..., 0] = thetac / (np.pi*2)
    +hsv[..., 1] = rc
    +hsv[..., 2] = zc + 0.5
    +colors = matplotlib.colors.hsv_to_rgb(hsv)
    +
    +# and plot everything
    +ax = plt.figure().add_subplot(projection='3d')
    +ax.voxels(x, y, z, sphere,
    +          facecolors=colors,
    +          edgecolors=np.clip(2*colors - 0.5, 0, 1),  # brighter
    +          linewidth=0.5)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    styling: color,
    +#    level: intermediate
    diff --git a/galleries/examples/mplot3d/wire3d.py b/galleries/examples/mplot3d/wire3d.py
    new file mode 100644
    index 000000000000..357234f51174
    --- /dev/null
    +++ b/galleries/examples/mplot3d/wire3d.py
    @@ -0,0 +1,27 @@
    +"""
    +=================
    +3D wireframe plot
    +=================
    +
    +A very basic demonstration of a wireframe plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Grab some test data.
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +# Plot a basic wireframe.
    +ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/wire3d_animation_sgskip.py b/galleries/examples/mplot3d/wire3d_animation_sgskip.py
    new file mode 100644
    index 000000000000..903ff4918586
    --- /dev/null
    +++ b/galleries/examples/mplot3d/wire3d_animation_sgskip.py
    @@ -0,0 +1,47 @@
    +"""
    +===========================
    +Animate a 3D wireframe plot
    +===========================
    +
    +A very simple "animation" of a 3D plot.  See also :doc:`rotate_axes3d_sgskip`.
    +
    +(This example is skipped when building the documentation gallery because it
    +intentionally takes a long time to run.)
    +"""
    +
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='3d')
    +
    +# Make the X, Y meshgrid.
    +xs = np.linspace(-1, 1, 50)
    +ys = np.linspace(-1, 1, 50)
    +X, Y = np.meshgrid(xs, ys)
    +
    +# Set the z axis limits, so they aren't recalculated each frame.
    +ax.set_zlim(-1, 1)
    +
    +# Begin plotting.
    +wframe = None
    +tstart = time.time()
    +for phi in np.linspace(0, 180. / np.pi, 100):
    +    # If a line collection is already remove it before drawing.
    +    if wframe:
    +        wframe.remove()
    +    # Generate data.
    +    Z = np.cos(2 * np.pi * X + phi) * (1 - np.hypot(X, Y))
    +    # Plot the new wireframe and pause briefly before continuing.
    +    wframe = ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)
    +    plt.pause(.001)
    +
    +print('Average FPS: %f' % (100 / (time.time() - tstart)))
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    component: animation,
    +#    level: beginner
    diff --git a/galleries/examples/mplot3d/wire3d_zero_stride.py b/galleries/examples/mplot3d/wire3d_zero_stride.py
    new file mode 100644
    index 000000000000..ff6a14984b5d
    --- /dev/null
    +++ b/galleries/examples/mplot3d/wire3d_zero_stride.py
    @@ -0,0 +1,34 @@
    +"""
    +===================================
    +3D wireframe plots in one direction
    +===================================
    +
    +Demonstrates that setting *rstride* or *cstride* to 0 causes wires to not be
    +generated in the corresponding direction.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from mpl_toolkits.mplot3d import axes3d
    +
    +fig, (ax1, ax2) = plt.subplots(
    +    2, 1, figsize=(8, 12), subplot_kw={'projection': '3d'})
    +
    +# Get the test data
    +X, Y, Z = axes3d.get_test_data(0.05)
    +
    +# Give the first plot only wireframes of the type y = c
    +ax1.plot_wireframe(X, Y, Z, rstride=10, cstride=0)
    +ax1.set_title("Column (x) stride set to 0")
    +
    +# Give the second plot only wireframes of the type x = c
    +ax2.plot_wireframe(X, Y, Z, rstride=0, cstride=10)
    +ax2.set_title("Row (y) stride set to 0")
    +
    +plt.tight_layout()
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#    plot-type: 3D,
    +#    level: intermediate
    diff --git a/galleries/examples/pie_and_polar_charts/README.txt b/galleries/examples/pie_and_polar_charts/README.txt
    new file mode 100644
    index 000000000000..31f87c586495
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/README.txt
    @@ -0,0 +1,4 @@
    +.. _pie_and_polar_charts:
    +
    +Pie and polar charts
    +====================
    diff --git a/galleries/examples/pie_and_polar_charts/bar_of_pie.py b/galleries/examples/pie_and_polar_charts/bar_of_pie.py
    new file mode 100644
    index 000000000000..7c703976db2e
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/bar_of_pie.py
    @@ -0,0 +1,91 @@
    +"""
    +==========
    +Bar of pie
    +==========
    +
    +Make a "bar of pie" chart where the first slice of the pie is
    +"exploded" into a bar chart with a further breakdown of said slice's
    +characteristics. The example demonstrates using a figure with multiple
    +sets of Axes and using the Axes patches list to add two ConnectionPatches
    +to link the subplot charts.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import ConnectionPatch
    +
    +# make figure and assign axis objects
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 5))
    +fig.subplots_adjust(wspace=0)
    +
    +# pie chart parameters
    +overall_ratios = [.27, .56, .17]
    +labels = ['Approve', 'Disapprove', 'Undecided']
    +explode = [0.1, 0, 0]
    +# rotate so that first wedge is split by the x-axis
    +angle = -180 * overall_ratios[0]
    +pie = ax1.pie(overall_ratios, autopct='%1.1f%%', startangle=angle,
    +              labels=labels, explode=explode)
    +
    +# bar chart parameters
    +age_ratios = [.33, .54, .07, .06]
    +age_labels = ['Under 35', '35-49', '50-65', 'Over 65']
    +bottom = 1
    +width = .2
    +
    +# Adding from the top matches the legend.
    +for j, (height, label) in enumerate(reversed([*zip(age_ratios, age_labels)])):
    +    bottom -= height
    +    bc = ax2.bar(0, height, width, bottom=bottom, color='C0', label=label,
    +                 alpha=0.1 + 0.25 * j)
    +    ax2.bar_label(bc, labels=[f"{height:.0%}"], label_type='center')
    +
    +ax2.set_title('Age of approvers')
    +ax2.legend()
    +ax2.axis('off')
    +ax2.set_xlim(- 2.5 * width, 2.5 * width)
    +
    +# use ConnectionPatch to draw lines between the two plots
    +theta1, theta2 = pie.wedges[0].theta1, pie.wedges[0].theta2
    +center, r = pie.wedges[0].center, pie.wedges[0].r
    +bar_height = sum(age_ratios)
    +
    +# draw top connecting line
    +x = r * np.cos(np.pi / 180 * theta2) + center[0]
    +y = r * np.sin(np.pi / 180 * theta2) + center[1]
    +con = ConnectionPatch(xyA=(-width / 2, bar_height), coordsA=ax2.transData,
    +                      xyB=(x, y), coordsB=ax1.transData)
    +con.set_color([0, 0, 0])
    +con.set_linewidth(4)
    +ax2.add_artist(con)
    +
    +# draw bottom connecting line
    +x = r * np.cos(np.pi / 180 * theta1) + center[0]
    +y = r * np.sin(np.pi / 180 * theta1) + center[1]
    +con = ConnectionPatch(xyA=(-width / 2, 0), coordsA=ax2.transData,
    +                      xyB=(x, y), coordsB=ax1.transData)
    +con.set_color([0, 0, 0])
    +ax2.add_artist(con)
    +con.set_linewidth(4)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.axes.Axes.pie` / `matplotlib.pyplot.pie`
    +#    - `matplotlib.patches.ConnectionPatch`
    +#
    +# .. tags::
    +#
    +#    component: subplot
    +#    plot-type: pie
    +#    plot-type: bar
    +#    level: intermediate
    +#    purpose: showcase
    diff --git a/galleries/examples/pie_and_polar_charts/nested_pie.py b/galleries/examples/pie_and_polar_charts/nested_pie.py
    new file mode 100644
    index 000000000000..699360a1e3fa
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/nested_pie.py
    @@ -0,0 +1,98 @@
    +"""
    +=================
    +Nested pie charts
    +=================
    +
    +The following examples show two ways to build a nested pie chart
    +in Matplotlib. Such charts are often referred to as donut charts.
    +
    +See also the :doc:`/gallery/specialty_plots/leftventricle_bullseye` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +# The most straightforward way to build a pie chart is to use the
    +# `~matplotlib.axes.Axes.pie` method.
    +#
    +# In this case, pie takes values corresponding to counts in a group.
    +# We'll first generate some fake data, corresponding to three groups.
    +# In the inner circle, we'll treat each number as belonging to its
    +# own group. In the outer circle, we'll plot them as members of their
    +# original 3 groups.
    +#
    +# The effect of the donut shape is achieved by setting a ``width`` to
    +# the pie's wedges through the *wedgeprops* argument.
    +
    +
    +fig, ax = plt.subplots()
    +
    +size = 0.3
    +vals = np.array([[60., 32.], [37., 40.], [29., 10.]])
    +
    +tab20c = plt.color_sequences["tab20c"]
    +outer_colors = [tab20c[i] for i in [0, 4, 8]]
    +inner_colors = [tab20c[i] for i in [1, 2, 5, 6, 9, 10]]
    +
    +ax.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
    +       wedgeprops=dict(width=size, edgecolor='w'))
    +
    +ax.pie(vals.flatten(), radius=1-size, colors=inner_colors,
    +       wedgeprops=dict(width=size, edgecolor='w'))
    +
    +ax.set(aspect="equal", title='Pie plot with `ax.pie`')
    +plt.show()
    +
    +# %%
    +# However, you can accomplish the same output by using a bar plot on
    +# Axes with a polar coordinate system. This may give more flexibility on
    +# the exact design of the plot.
    +#
    +# In this case, we need to map x-values of the bar chart onto radians of
    +# a circle. The cumulative sum of the values are used as the edges
    +# of the bars.
    +
    +fig, ax = plt.subplots(subplot_kw=dict(projection="polar"))
    +
    +size = 0.3
    +vals = np.array([[60., 32.], [37., 40.], [29., 10.]])
    +# Normalize vals to 2 pi
    +valsnorm = vals/np.sum(vals)*2*np.pi
    +# Obtain the ordinates of the bar edges
    +valsleft = np.cumsum(np.append(0, valsnorm.flatten()[:-1])).reshape(vals.shape)
    +
    +cmap = plt.colormaps["tab20c"]
    +outer_colors = cmap(np.arange(3)*4)
    +inner_colors = cmap([1, 2, 5, 6, 9, 10])
    +
    +ax.bar(x=valsleft[:, 0],
    +       width=valsnorm.sum(axis=1), bottom=1-size, height=size,
    +       color=outer_colors, edgecolor='w', linewidth=1, align="edge")
    +
    +ax.bar(x=valsleft.flatten(),
    +       width=valsnorm.flatten(), bottom=1-2*size, height=size,
    +       color=inner_colors, edgecolor='w', linewidth=1, align="edge")
    +
    +ax.set(title="Pie plot with `ax.bar` and polar coordinates")
    +ax.set_axis_off()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pie` / `matplotlib.pyplot.pie`
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.projections.polar`
    +#    - ``Axes.set`` (`matplotlib.artist.Artist.set`)
    +#    - `matplotlib.axes.Axes.set_axis_off`
    +#
    +# .. tags::
    +#
    +#    plot-type: pie
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py b/galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py
    new file mode 100644
    index 000000000000..78e884128d1e
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/pie_and_donut_labels.py
    @@ -0,0 +1,139 @@
    +"""
    +=============================
    +A pie and a donut with labels
    +=============================
    +
    +Welcome to the Matplotlib bakery. We will create a pie and a donut
    +chart through the `pie method ` and
    +show how to label them with a `legend `
    +as well as with the `pie_label method ` and
    +`annotations `.
    +"""
    +
    +# %%
    +# As usual we would start by defining the imports and create a figure with
    +# subplots.
    +# Now it's time for the pie. Starting with a pie recipe, we create the data
    +# and a list of labels from it.
    +#
    +# We then create the pie and store the returned `~matplotlib.container.PieContainer`
    +# object for later.
    +#
    +# We can provide the `~matplotlib.container.PieContainer` and a format string to
    +# the `~matplotlib.axes.Axes.pie_label` method to automatically label each
    +# ingredient's wedge with its weight in grams and percentages.
    +#
    +# The `~.PieContainer` has a list of patches as one of its attributes.  Those are
    +# `matplotlib.patches.Wedge` patches, which can directly be used as the handles
    +# for a legend. We can use the legend's ``bbox_to_anchor`` argument to position
    +# the legend outside of the pie. Here we use the axes coordinates ``(1, 0, 0.5,
    +# 1)`` together with the location ``"center left"``; i.e. the left central
    +# point of the legend will be at the left central point of the bounding box,
    +# spanning from ``(1, 0)`` to ``(1.5, 1)`` in axes coordinates.
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, ax = plt.subplots(figsize=(6, 3))
    +
    +recipe = ["375 g flour",
    +          "75 g sugar",
    +          "250 g butter",
    +          "300 g berries"]
    +
    +data = [int(x.split()[0]) for x in recipe]
    +ingredients = [x.split()[-1] for x in recipe]
    +
    +pie = ax.pie(data)
    +
    +ax.pie_label(pie, '{frac:.1%}\n({absval:d}g)',
    +             textprops=dict(color="w", size=8, weight="bold"))
    +
    +ax.legend(pie.wedges, ingredients,
    +          title="Ingredients",
    +          loc="center left",
    +          bbox_to_anchor=(1, 0, 0.5, 1))
    +
    +ax.set_title("Matplotlib bakery: A pie")
    +
    +plt.show()
    +
    +
    +# %%
    +# Now it's time for the donut. Starting with a donut recipe, we transcribe
    +# the data to numbers (converting 1 egg to 50 g), and directly plot the pie.
    +# The pie? Wait... it's going to be donut, is it not?
    +# Well, as we see here, the donut is a pie, having a certain ``width`` set to
    +# the wedges, which is different from its radius. It's as easy as it gets.
    +# This is done via the ``wedgeprops`` argument.
    +#
    +# We then want to label the wedges via
    +# `annotations `. We first create some
    +# dictionaries of common properties, which we can later pass as keyword
    +# argument. We then iterate over all wedges and for each
    +#
    +# * calculate the angle of the wedge's center,
    +# * from that obtain the coordinates of the point at that angle on the
    +#   circumference,
    +# * determine the horizontal alignment of the text, depending on which side
    +#   of the circle the point lies,
    +# * update the connection style with the obtained angle to have the annotation
    +#   arrow point outwards from the donut,
    +# * finally, create the annotation with all the previously
    +#   determined parameters.
    +
    +
    +fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
    +
    +recipe = ["225 g flour",
    +          "90 g sugar",
    +          "1 egg",
    +          "60 g butter",
    +          "100 ml milk",
    +          "1/2 package of yeast"]
    +
    +data = [225, 90, 50, 60, 100, 5]
    +
    +pie = ax.pie(data, wedgeprops=dict(width=0.5), startangle=-40)
    +
    +bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
    +kw = dict(arrowprops=dict(arrowstyle="-"),
    +          bbox=bbox_props, zorder=0, va="center")
    +
    +for i, p in enumerate(pie.wedges):
    +    ang = (p.theta2 - p.theta1)/2. + p.theta1
    +    y = np.sin(np.deg2rad(ang))
    +    x = np.cos(np.deg2rad(ang))
    +    horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
    +    connectionstyle = f"angle,angleA=0,angleB={ang}"
    +    kw["arrowprops"].update({"connectionstyle": connectionstyle})
    +    ax.annotate(recipe[i], xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
    +                horizontalalignment=horizontalalignment, **kw)
    +
    +ax.set_title("Matplotlib bakery: A donut")
    +
    +plt.show()
    +
    +# %%
    +# And here it is, the donut. Note however, that if we were to use this recipe,
    +# the ingredients would suffice for around 6 donuts - producing one huge
    +# donut is untested and might result in kitchen errors.
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pie` / `matplotlib.pyplot.pie`
    +#    - `matplotlib.axes.Axes.pie_label` / `matplotlib.pyplot.pie_label`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#
    +# .. tags::
    +#
    +#    component: label
    +#    component: annotation
    +#    plot-type: pie
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/pie_features.py b/galleries/examples/pie_and_polar_charts/pie_features.py
    new file mode 100644
    index 000000000000..4c0eeaa4526e
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/pie_features.py
    @@ -0,0 +1,137 @@
    +"""
    +.. redirect-from:: gallery/pie_and_polar_charts/pie_demo2
    +
    +==========
    +Pie charts
    +==========
    +
    +Demo of plotting a pie chart.
    +
    +This example illustrates various parameters of `~matplotlib.axes.Axes.pie`.
    +"""
    +
    +# %%
    +# Label slices
    +# ------------
    +#
    +# Plot a pie chart of animals and label the slices. To add
    +# labels, pass a list of labels to the *labels* parameter
    +
    +import matplotlib.pyplot as plt
    +
    +labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
    +sizes = [15, 30, 45, 10]
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, labels=labels)
    +
    +# %%
    +# Each slice of the pie chart is a `.patches.Wedge` object; therefore in
    +# addition to the customizations shown here, each wedge can be customized using
    +# the *wedgeprops* argument, as demonstrated in
    +# :doc:`/gallery/pie_and_polar_charts/nested_pie`.
    +#
    +# Auto-label slices
    +# -----------------
    +#
    +# Pass a function or format string to *autopct* to label slices.
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, labels=labels, autopct='%1.1f%%')
    +
    +# %%
    +# By default, the label values are obtained from the percent size of the slice.
    +#
    +# Color slices
    +# ------------
    +#
    +# Pass a list of colors to *colors* to set the color of each slice.
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, labels=labels,
    +       colors=['olivedrab', 'rosybrown', 'gray', 'saddlebrown'])
    +
    +# %%
    +# Hatch slices
    +# ------------
    +#
    +# Pass a list of hatch patterns to *hatch* to set the pattern of each slice.
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, labels=labels, hatch=['**O', 'oO', 'O.O', '.||.'])
    +
    +# %%
    +# Swap label and autopct text positions
    +# -------------------------------------
    +# Use the *labeldistance* and *pctdistance* parameters to position the *labels*
    +# and *autopct* text respectively.
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, labels=labels, autopct='%1.1f%%',
    +       pctdistance=1.25, labeldistance=.6)
    +
    +# %%
    +# *labeldistance* and *pctdistance* are ratios of the radius; therefore they
    +# vary between ``0`` for the center of the pie and ``1`` for the edge of the
    +# pie, and can be set to greater than ``1`` to place text outside the pie.
    +#
    +# Explode, shade, and rotate slices
    +# ---------------------------------
    +#
    +# In addition to the basic pie chart, this demo shows a few optional features:
    +#
    +# * offsetting a slice using *explode*
    +# * add a drop-shadow using *shadow*
    +# * custom start angle using *startangle*
    +#
    +# This example orders the slices, separates (explodes) them, and rotates them.
    +
    +explode = (0, 0.1, 0, 0)  # only "explode" the 2nd slice (i.e. 'Hogs')
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
    +       shadow=True, startangle=90)
    +plt.show()
    +
    +# %%
    +# The default *startangle* is 0, which would start the first slice ("Frogs") on
    +# the positive x-axis. This example sets ``startangle = 90`` such that all the
    +# slices are rotated counter-clockwise by 90 degrees, and the frog slice starts
    +# on the positive y-axis.
    +#
    +# Controlling the size
    +# --------------------
    +#
    +# By changing the *radius* parameter, and often the text size for better visual
    +# appearance, the pie chart can be scaled.
    +
    +fig, ax = plt.subplots()
    +
    +ax.pie(sizes, labels=labels, autopct='%.0f%%',
    +       textprops={'size': 'small'}, radius=0.5)
    +plt.show()
    +
    +# %%
    +# Modifying the shadow
    +# --------------------
    +#
    +# The *shadow* parameter may optionally take a dictionary with arguments to
    +# the `.Shadow` patch. This can be used to modify the default shadow.
    +
    +fig, ax = plt.subplots()
    +ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
    +       shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, startangle=90)
    +plt.show()
    +
    +# %%
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pie` / `matplotlib.pyplot.pie`
    +#
    +# .. tags::
    +#
    +#    plot-type: pie
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/pie_label.py b/galleries/examples/pie_and_polar_charts/pie_label.py
    new file mode 100644
    index 000000000000..d7f690bd6f85
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/pie_label.py
    @@ -0,0 +1,100 @@
    +"""
    +===================
    +Labeling pie charts
    +===================
    +
    +This example illustrates some features of the `~matplotlib.axes.Axes.pie_label`
    +method, which adds labels to an existing pie chart created with
    +`~matplotlib.axes.Axes.pie`.
    +"""
    +
    +# %%
    +# The simplest option is to provide a list of strings to label each slice of the pie.
    +
    +import matplotlib.pyplot as plt
    +
    +data = [36, 24, 8, 12]
    +labels = ['spam', 'eggs', 'bacon', 'sausage']
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, labels)
    +
    +# %%
    +#
    +# If you want the labels outside the pie, set a *distance* greater than 1.
    +# This is the distance from the center of the pie as a fraction of its radius.
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, labels, distance=1.1)
    +
    +# %%
    +#
    +# You can also rotate the labels so they are oriented away from the pie center.
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, labels, rotate=True)
    +
    +# %%
    +#
    +# Instead of explicit labels, pass a format string to label slices with their values...
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, '{absval:.1f}')
    +
    +# %%
    +#
    +# ...or with their percentages...
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, '{frac:.1%}')
    +
    +# %%
    +#
    +# ...or both.
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, '{absval:d}\n{frac:.1%}')
    +
    +# %%
    +#
    +# Font styling can be configured by passing a dictionary to the *textprops* parameter.
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +ax.pie_label(pie, labels, textprops={'fontsize': 'large', 'color': 'white'})
    +
    +# %%
    +#
    +# `~matplotlib.axes.Axes.pie_label` can be called repeatedly to add multiple sets
    +# of labels.
    +
    +# sphinx_gallery_thumbnail_number = -1
    +
    +fig, ax = plt.subplots()
    +pie = ax.pie(data)
    +
    +ax.pie_label(pie, labels, distance=1.1)
    +ax.pie_label(pie, '{frac:.1%}', distance=0.7)
    +ax.pie_label(pie, '{absval:d}', distance=0.4)
    +
    +plt.show()
    +
    +# %%
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pie` / `matplotlib.pyplot.pie`
    +#    - `matplotlib.axes.Axes.pie_label` / `matplotlib.pyplot.pie_label`
    +#
    +# .. tags::
    +#
    +#    plot-type: pie
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/polar_bar.py b/galleries/examples/pie_and_polar_charts/polar_bar.py
    new file mode 100644
    index 000000000000..a50f18c0917a
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/polar_bar.py
    @@ -0,0 +1,41 @@
    +"""
    +=======================
    +Bar chart on polar axis
    +=======================
    +
    +Demo of bar plot on a polar axis.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# Compute pie slices
    +N = 20
    +theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
    +radii = 10 * np.random.rand(N)
    +width = np.pi / 4 * np.random.rand(N)
    +colors = plt.colormaps["viridis"](radii / 10.)
    +
    +ax = plt.subplot(projection='polar')
    +ax.bar(theta, radii, width=width, bottom=0.0, color=colors, alpha=0.5)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.projections.polar`
    +#
    +# .. tags::
    +#
    +#    plot-type: pie
    +#    plot-type: bar
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/pie_and_polar_charts/polar_demo.py b/galleries/examples/pie_and_polar_charts/polar_demo.py
    new file mode 100644
    index 000000000000..909fea094be5
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/polar_demo.py
    @@ -0,0 +1,63 @@
    +"""
    +==========
    +Polar plot
    +==========
    +
    +Demo of a line plot on a polar axis.
    +
    +The second plot shows the same data, but with the radial axis starting at r=1
    +and the angular axis starting at 0 degrees and ending at 225 degrees. Setting
    +the origin of the radial axis to 0 allows the radial ticks to be placed at the
    +same location as the first plot.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +r = np.arange(0, 2, 0.01)
    +theta = 2 * np.pi * r
    +
    +fig, axs = plt.subplots(2, 1, figsize=(5, 8), subplot_kw={'projection': 'polar'},
    +                        layout='constrained')
    +ax = axs[0]
    +ax.plot(theta, r)
    +ax.set_rmax(2)
    +ax.set_rticks([0.5, 1, 1.5, 2])  # Fewer radial ticks
    +ax.set_rlabel_position(-22.5)  # Move radial labels away from plotted line
    +ax.grid(True)
    +
    +ax.set_title("A line plot on a polar axis", va='bottom')
    +
    +ax = axs[1]
    +ax.plot(theta, r)
    +ax.set_rmax(2)
    +ax.set_rmin(1)  # Change the radial axis to only go from 1 to 2
    +ax.set_rorigin(0)  # Set the origin of the radial axis to 0
    +ax.set_thetamin(0)
    +ax.set_thetamax(225)
    +ax.set_rticks([1, 1.5, 2])  # Fewer radial ticks
    +ax.set_rlabel_position(-22.5)  # Move radial labels away from plotted line
    +
    +ax.grid(True)
    +ax.set_title("Same plot, but with reduced axis limits", va='bottom')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.projections.polar`
    +#    - `matplotlib.projections.polar.PolarAxes`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rticks`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rmin`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rorigin`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rmax`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rlabel_position`
    +#
    +# .. tags::
    +#
    +#    plot-type: polar
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/polar_error_caps.py b/galleries/examples/pie_and_polar_charts/polar_error_caps.py
    new file mode 100644
    index 000000000000..7f77a2c48834
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/polar_error_caps.py
    @@ -0,0 +1,60 @@
    +"""
    +=================================
    +Error bar rendering on polar axis
    +=================================
    +
    +Demo of error bar plot in polar coordinates.
    +Theta error bars are curved lines ended with caps oriented towards the
    +center.
    +Radius error bars are straight lines oriented towards center with
    +perpendicular caps.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +theta = np.arange(0, 2 * np.pi, np.pi / 4)
    +r = theta / np.pi / 2 + 0.5
    +
    +fig = plt.figure(figsize=(10, 10))
    +ax = fig.add_subplot(projection='polar')
    +ax.errorbar(theta, r, xerr=0.25, yerr=0.1, capsize=7, fmt="o", c="seagreen")
    +ax.set_title("Pretty polar error bars")
    +plt.show()
    +
    +# %%
    +# Please acknowledge that large theta error bars will be overlapping.
    +# This may reduce readability of the output plot. See example figure below:
    +
    +fig = plt.figure(figsize=(10, 10))
    +ax = fig.add_subplot(projection='polar')
    +ax.errorbar(theta, r, xerr=5.25, yerr=0.1, capsize=7, fmt="o", c="darkred")
    +ax.set_title("Overlapping theta error bars")
    +plt.show()
    +
    +# %%
    +# On the other hand, large radius error bars will never overlap, they just
    +# lead to unwanted scale in the data, reducing the displayed range.
    +
    +fig = plt.figure(figsize=(10, 10))
    +ax = fig.add_subplot(projection='polar')
    +ax.errorbar(theta, r, xerr=0.25, yerr=10.1, capsize=7, fmt="o", c="orangered")
    +ax.set_title("Large radius error bars")
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    +#    - `matplotlib.projections.polar`
    +#
    +# .. tags::
    +#
    +#    component: error
    +#    plot-type: errorbar
    +#    plot-type: polar
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/polar_legend.py b/galleries/examples/pie_and_polar_charts/polar_legend.py
    new file mode 100644
    index 000000000000..cef4bc8ccef6
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/polar_legend.py
    @@ -0,0 +1,46 @@
    +"""
    +============
    +Polar legend
    +============
    +
    +Using a legend on a polar-axis plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection="polar", facecolor="lightgoldenrodyellow")
    +
    +r = np.linspace(0, 3, 301)
    +theta = 2 * np.pi * r
    +ax.plot(theta, r, color="tab:orange", lw=3, label="a line")
    +ax.plot(0.5 * theta, r, color="tab:blue", ls="--", lw=3, label="another line")
    +ax.tick_params(grid_color="palegoldenrod")
    +# For polar Axes, it may be useful to move the legend slightly away from the
    +# Axes center, to avoid overlap between the legend and the Axes.  The following
    +# snippet places the legend's lower left corner just outside the polar Axes
    +# at an angle of 67.5 degrees in polar coordinates.
    +angle = np.deg2rad(67.5)
    +ax.legend(loc="lower left",
    +          bbox_to_anchor=(.5 + np.cos(angle)/2, .5 + np.sin(angle)/2))
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#    - `matplotlib.projections.polar`
    +#    - `matplotlib.projections.polar.PolarAxes`
    +#
    +# .. tags::
    +#
    +#    component: legend
    +#    plot-type: polar
    +#    level: beginner
    diff --git a/galleries/examples/pie_and_polar_charts/polar_scatter.py b/galleries/examples/pie_and_polar_charts/polar_scatter.py
    new file mode 100644
    index 000000000000..e3e4c4a87b1e
    --- /dev/null
    +++ b/galleries/examples/pie_and_polar_charts/polar_scatter.py
    @@ -0,0 +1,75 @@
    +"""
    +==========================
    +Scatter plot on polar axis
    +==========================
    +
    +Size increases radially in this example and color increases with angle
    +(just to verify the symbols are being scattered correctly).
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# Compute areas and colors
    +N = 150
    +r = 2 * np.random.rand(N)
    +theta = 2 * np.pi * np.random.rand(N)
    +area = 200 * r**2
    +colors = theta
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='polar')
    +c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75)
    +
    +# %%
    +# Scatter plot on polar axis, with offset origin
    +# ----------------------------------------------
    +#
    +# The main difference with the previous plot is the configuration of the origin
    +# radius, producing an annulus. Additionally, the theta zero location is set to
    +# rotate the plot.
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='polar')
    +c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75)
    +
    +ax.set_rorigin(-2.5)
    +ax.set_theta_zero_location('W', offset=10)
    +
    +# %%
    +# Scatter plot on polar axis confined to a sector
    +# -----------------------------------------------
    +#
    +# The main difference with the previous plots is the configuration of the
    +# theta start and end limits, producing a sector instead of a full circle.
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='polar')
    +c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75)
    +
    +ax.set_thetamin(45)
    +ax.set_thetamax(135)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter`
    +#    - `matplotlib.projections.polar`
    +#    - `matplotlib.projections.polar.PolarAxes.set_rorigin`
    +#    - `matplotlib.projections.polar.PolarAxes.set_theta_zero_location`
    +#    - `matplotlib.projections.polar.PolarAxes.set_thetamin`
    +#    - `matplotlib.projections.polar.PolarAxes.set_thetamax`
    +#
    +# .. tags::
    +#
    +#    plot-type: polar
    +#    plot-type: scatter
    +#    level: beginner
    diff --git a/galleries/examples/pyplots/README.txt b/galleries/examples/pyplots/README.txt
    new file mode 100644
    index 000000000000..493618b19e6c
    --- /dev/null
    +++ b/galleries/examples/pyplots/README.txt
    @@ -0,0 +1,4 @@
    +.. _pyplots_examples:
    +
    +Module - pyplot
    +===============
    diff --git a/doc/pyplots/matplotlibrc b/galleries/examples/pyplots/matplotlibrc
    similarity index 100%
    rename from doc/pyplots/matplotlibrc
    rename to galleries/examples/pyplots/matplotlibrc
    diff --git a/galleries/examples/pyplots/pyplot_simple.py b/galleries/examples/pyplots/pyplot_simple.py
    new file mode 100644
    index 000000000000..48a862c7fee3
    --- /dev/null
    +++ b/galleries/examples/pyplots/pyplot_simple.py
    @@ -0,0 +1,29 @@
    +"""
    +===========
    +Simple plot
    +===========
    +
    +A simple plot where a list of numbers are plotted against their index,
    +resulting in a straight line. Use a format string (here, 'o-r') to set the
    +markers (circles), linestyle (solid line) and color (red).
    +
    +.. redirect-from:: /gallery/pyplots/fig_axes_labels_simple
    +.. redirect-from:: /gallery/pyplots/pyplot_formatstr
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +plt.plot([1, 2, 3, 4], 'o-r')
    +plt.ylabel('some numbers')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.plot`
    +#    - `matplotlib.pyplot.ylabel`
    +#    - `matplotlib.pyplot.show`
    diff --git a/galleries/examples/pyplots/pyplot_text.py b/galleries/examples/pyplots/pyplot_text.py
    new file mode 100644
    index 000000000000..72f977c2f985
    --- /dev/null
    +++ b/galleries/examples/pyplots/pyplot_text.py
    @@ -0,0 +1,41 @@
    +"""
    +==============================
    +Text and mathtext using pyplot
    +==============================
    +
    +Set the special text objects `~.pyplot.title`, `~.pyplot.xlabel`, and
    +`~.pyplot.ylabel` through the dedicated pyplot functions.  Additional text
    +objects can be placed in the Axes using `~.pyplot.text`.
    +
    +You can use TeX-like mathematical typesetting in all texts; see also
    +:ref:`mathtext`.
    +
    +.. redirect-from:: /gallery/pyplots/pyplot_mathtext
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2.0, 0.01)
    +s = np.sin(2*np.pi*t)
    +
    +plt.plot(t, s)
    +plt.text(0, -1, r'Hello, world!', fontsize=15)
    +plt.title(r'$\mathcal{A}\sin(\omega t)$', fontsize=20)
    +plt.xlabel('Time [s]')
    +plt.ylabel('Voltage [mV]')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.hist`
    +#    - `matplotlib.pyplot.xlabel`
    +#    - `matplotlib.pyplot.ylabel`
    +#    - `matplotlib.pyplot.text`
    +#    - `matplotlib.pyplot.grid`
    +#    - `matplotlib.pyplot.show`
    diff --git a/galleries/examples/pyplots/pyplot_three.py b/galleries/examples/pyplots/pyplot_three.py
    new file mode 100644
    index 000000000000..b14998cca4c9
    --- /dev/null
    +++ b/galleries/examples/pyplots/pyplot_three.py
    @@ -0,0 +1,26 @@
    +"""
    +===========================
    +Multiple lines using pyplot
    +===========================
    +
    +Plot three datasets with a single call to `~matplotlib.pyplot.plot`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# evenly sampled time at 200ms intervals
    +t = np.arange(0., 5., 0.2)
    +
    +# red dashes, blue squares and green triangles
    +plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    diff --git a/galleries/examples/pyplots/pyplot_two_subplots.py b/galleries/examples/pyplots/pyplot_two_subplots.py
    new file mode 100644
    index 000000000000..2eb0237d5521
    --- /dev/null
    +++ b/galleries/examples/pyplots/pyplot_two_subplots.py
    @@ -0,0 +1,37 @@
    +"""
    +=========================
    +Two subplots using pyplot
    +=========================
    +
    +Create a figure with two subplots using `.pyplot.subplot`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def f(t):
    +    return np.exp(-t) * np.cos(2*np.pi*t)
    +
    +
    +t1 = np.arange(0.0, 5.0, 0.1)
    +t2 = np.arange(0.0, 5.0, 0.02)
    +
    +plt.figure()
    +plt.subplot(211)
    +plt.plot(t1, f(t1), color='tab:blue', marker='o')
    +plt.plot(t2, f(t2), color='black')
    +
    +plt.subplot(212)
    +plt.plot(t2, np.cos(2*np.pi*t2), color='tab:orange', linestyle='--')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.figure`
    +#    - `matplotlib.pyplot.subplot`
    diff --git a/galleries/examples/scales/README.txt b/galleries/examples/scales/README.txt
    new file mode 100644
    index 000000000000..031fe9c7834b
    --- /dev/null
    +++ b/galleries/examples/scales/README.txt
    @@ -0,0 +1,6 @@
    +.. _scales_examples:
    +
    +Scales
    +======
    +
    +These examples cover how different scales are handled in Matplotlib.
    diff --git a/galleries/examples/scales/asinh_demo.py b/galleries/examples/scales/asinh_demo.py
    new file mode 100644
    index 000000000000..bc8b010c47ce
    --- /dev/null
    +++ b/galleries/examples/scales/asinh_demo.py
    @@ -0,0 +1,109 @@
    +"""
    +===========
    +Asinh scale
    +===========
    +
    +Illustration of the `asinh <.scale.AsinhScale>` axis scaling,
    +which uses the transformation
    +
    +.. math::
    +
    +    a \\rightarrow a_0 \\sinh^{-1} (a / a_0)
    +
    +For coordinate values close to zero (i.e. much smaller than
    +the "linear width" :math:`a_0`), this leaves values essentially unchanged:
    +
    +.. math::
    +
    +    a \\rightarrow a + \\mathcal{O}(a^3)
    +
    +but for larger values (i.e. :math:`|a| \\gg a_0`, this is asymptotically
    +
    +.. math::
    +
    +    a \\rightarrow a_0 \\, \\mathrm{sgn}(a) \\ln |a| + \\mathcal{O}(1)
    +
    +As with the `symlog <.scale.SymmetricalLogScale>` scaling,
    +this allows one to plot quantities
    +that cover a very wide dynamic range that includes both positive
    +and negative values. However, ``symlog`` involves a transformation
    +that has discontinuities in its gradient because it is built
    +from *separate* linear and logarithmic transformations.
    +The ``asinh`` scaling uses a transformation that is smooth
    +for all (finite) values, which is both mathematically cleaner
    +and reduces visual artifacts associated with an abrupt
    +transition between linear and logarithmic regions of the plot.
    +
    +.. note::
    +   `.scale.AsinhScale` is experimental, and the API may change.
    +
    +See `~.scale.AsinhScale`, `~.scale.SymmetricalLogScale`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Prepare sample values for variations on y=x graph:
    +x = np.linspace(-3, 6, 500)
    +
    +# %%
    +# Compare "symlog" and "asinh" behaviour on sample y=x graph,
    +# where there is a discontinuous gradient in "symlog" near y=2:
    +fig1 = plt.figure()
    +ax0, ax1 = fig1.subplots(1, 2, sharex=True)
    +
    +ax0.plot(x, x)
    +ax0.set_yscale('symlog')
    +ax0.grid()
    +ax0.set_title('symlog')
    +
    +ax1.plot(x, x)
    +ax1.set_yscale('asinh')
    +ax1.grid()
    +ax1.set_title('asinh')
    +
    +
    +# %%
    +# Compare "asinh" graphs with different scale parameter "linear_width":
    +fig2 = plt.figure(layout='constrained')
    +axs = fig2.subplots(1, 3, sharex=True)
    +for ax, (a0, base) in zip(axs, ((0.2, 2), (1.0, 0), (5.0, 10))):
    +    ax.set_title(f'linear_width={a0:.3g}')
    +    ax.plot(x, x, label='y=x')
    +    ax.plot(x, 10*x, label='y=10x')
    +    ax.plot(x, 100*x, label='y=100x')
    +    ax.set_yscale('asinh', linear_width=a0, base=base)
    +    ax.grid()
    +    ax.legend(loc='best', fontsize='small')
    +
    +
    +# %%
    +# Compare "symlog" and "asinh" scalings
    +# on 2D Cauchy-distributed random numbers,
    +# where one may be able to see more subtle artifacts near y=2
    +# due to the gradient-discontinuity in "symlog":
    +fig3 = plt.figure()
    +ax = fig3.subplots(1, 1)
    +r = 3 * np.tan(np.random.uniform(-np.pi / 2.02, np.pi / 2.02,
    +                                 size=(5000,)))
    +th = np.random.uniform(0, 2*np.pi, size=r.shape)
    +
    +ax.scatter(r * np.cos(th), r * np.sin(th), s=4, alpha=0.5)
    +ax.set_xscale('asinh')
    +ax.set_yscale('symlog')
    +ax.set_xlabel('asinh')
    +ax.set_ylabel('symlog')
    +ax.set_title('2D Cauchy random deviates')
    +ax.set_xlim(-50, 50)
    +ax.set_ylim(-50, 50)
    +ax.grid()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    - `matplotlib.scale.AsinhScale`
    +#    - `matplotlib.ticker.AsinhLocator`
    +#    - `matplotlib.scale.SymmetricalLogScale`
    diff --git a/examples/pylab_examples/aspect_loglog.py b/galleries/examples/scales/aspect_loglog.py
    similarity index 89%
    rename from examples/pylab_examples/aspect_loglog.py
    rename to galleries/examples/scales/aspect_loglog.py
    index 8d02b0724708..420721b9b411 100644
    --- a/examples/pylab_examples/aspect_loglog.py
    +++ b/galleries/examples/scales/aspect_loglog.py
    @@ -1,3 +1,9 @@
    +"""
    +=============
    +Loglog aspect
    +=============
    +
    +"""
     import matplotlib.pyplot as plt
     
     fig, (ax1, ax2) = plt.subplots(1, 2)
    @@ -17,5 +23,4 @@
     ax2.set_aspect(1)
     ax2.set_title("adjustable = datalim")
     
    -plt.draw()
     plt.show()
    diff --git a/galleries/examples/scales/custom_scale.py b/galleries/examples/scales/custom_scale.py
    new file mode 100644
    index 000000000000..1b6bdd6f3e09
    --- /dev/null
    +++ b/galleries/examples/scales/custom_scale.py
    @@ -0,0 +1,184 @@
    +"""
    +.. _custom_scale:
    +
    +============
    +Custom scale
    +============
    +
    +Custom scales can be created in two ways
    +
    +#. For simple cases, use `~.scale.FuncScale` and the ``'function'`` option of
    +   `~.Axes.set_xscale` and `~.Axes.set_yscale`.  See the last example in
    +   :doc:`/gallery/scales/scales`.
    +
    +#. Create a custom scale class such as the one in this example, which implements
    +   the scaling use for latitude data in a Mercator Projection.  This more complicated
    +   approach is useful when
    +
    +   * You are making special use of the `.Transform` class, such as the special
    +     handling of values beyond the threshold in ``MercatorLatitudeTransform``
    +     below.
    +
    +   * You want to override the default locators and formatters for the axis
    +     (``set_default_locators_and_formatters`` below).
    +
    +   * You want to limit the range of the axis (``limit_range_for_scale`` below).
    +
    +"""
    +
    +import numpy as np
    +from numpy import ma
    +
    +from matplotlib import scale as mscale
    +from matplotlib import transforms as mtransforms
    +from matplotlib.ticker import FixedLocator, FuncFormatter
    +
    +
    +class MercatorLatitudeScale(mscale.ScaleBase):
    +    """
    +    Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using
    +    the system used to scale latitudes in a Mercator__ projection.
    +
    +    The scale function:
    +      ln(tan(y) + sec(y))
    +
    +    The inverse scale function:
    +      atan(sinh(y))
    +
    +    Since the Mercator scale tends to infinity at +/- 90 degrees,
    +    there is user-defined threshold, above and below which nothing
    +    will be plotted.  This defaults to +/- 85 degrees.
    +
    +    __ https://en.wikipedia.org/wiki/Mercator_projection
    +    """
    +
    +    # The scale class must have a member ``name`` that defines the string used
    +    # to select the scale.  For example, ``ax.set_yscale("mercator")`` would be
    +    # used to select this scale.
    +    name = 'mercator'
    +
    +    def __init__(self, axis, *, thresh=np.deg2rad(85), **kwargs):
    +        """
    +        Any keyword arguments passed to ``set_xscale`` and ``set_yscale`` will
    +        be passed along to the scale's constructor.
    +
    +        thresh: The degree above which to crop the data.
    +        """
    +        super().__init__(axis)
    +        if thresh >= np.pi / 2:
    +            raise ValueError("thresh must be less than pi/2")
    +        self.thresh = thresh
    +
    +    def get_transform(self):
    +        """
    +        Override this method to return a new instance that does the
    +        actual transformation of the data.
    +
    +        The MercatorLatitudeTransform class is defined below as a
    +        nested class of this one.
    +        """
    +        return self.MercatorLatitudeTransform(self.thresh)
    +
    +    def set_default_locators_and_formatters(self, axis):
    +        """
    +        Override to set up the locators and formatters to use with the
    +        scale.  This is only required if the scale requires custom
    +        locators and formatters.  Writing custom locators and
    +        formatters is rather outside the scope of this example, but
    +        there are many helpful examples in :mod:`.ticker`.
    +
    +        In our case, the Mercator example uses a fixed locator from -90 to 90
    +        degrees and a custom formatter to convert the radians to degrees and
    +        put a degree symbol after the value.
    +        """
    +        fmt = FuncFormatter(
    +            lambda x, pos=None: f"{np.degrees(x):.0f}\N{DEGREE SIGN}")
    +        axis.set(major_locator=FixedLocator(np.radians(range(-90, 90, 10))),
    +                 major_formatter=fmt, minor_formatter=fmt)
    +
    +    def limit_range_for_scale(self, vmin, vmax, minpos):
    +        """
    +        Override to limit the bounds of the axis to the domain of the
    +        transform.  In the case of Mercator, the bounds should be
    +        limited to the threshold that was passed in.  Unlike the
    +        autoscaling provided by the tick locators, this range limiting
    +        will always be adhered to, whether the axis range is set
    +        manually, determined automatically or changed through panning
    +        and zooming.
    +        """
    +        return max(vmin, -self.thresh), min(vmax, self.thresh)
    +
    +    class MercatorLatitudeTransform(mtransforms.Transform):
    +        # There are two value members that must be defined.
    +        # ``input_dims`` and ``output_dims`` specify number of input
    +        # dimensions and output dimensions to the transformation.
    +        # These are used by the transformation framework to do some
    +        # error checking and prevent incompatible transformations from
    +        # being connected together.  When defining transforms for a
    +        # scale, which are, by definition, separable and have only one
    +        # dimension, these members should always be set to 1.
    +        input_dims = output_dims = 1
    +
    +        def __init__(self, thresh):
    +            mtransforms.Transform.__init__(self)
    +            self.thresh = thresh
    +
    +        def transform_non_affine(self, a):
    +            """
    +            This transform takes a numpy array and returns a transformed copy.
    +            Since the range of the Mercator scale is limited by the
    +            user-specified threshold, the input array must be masked to
    +            contain only valid values.  Matplotlib will handle masked arrays
    +            and remove the out-of-range data from the plot.  However, the
    +            returned array *must* have the same shape as the input array, since
    +            these values need to remain synchronized with values in the other
    +            dimension.
    +            """
    +            masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a)
    +            if masked.mask.any():
    +                return ma.log(np.abs(ma.tan(masked) + 1 / ma.cos(masked)))
    +            else:
    +                return np.log(np.abs(np.tan(a) + 1 / np.cos(a)))
    +
    +        def inverted(self):
    +            """
    +            Override this method so Matplotlib knows how to get the
    +            inverse transform for this transform.
    +            """
    +            return MercatorLatitudeScale.InvertedMercatorLatitudeTransform(
    +                self.thresh)
    +
    +    class InvertedMercatorLatitudeTransform(mtransforms.Transform):
    +        input_dims = output_dims = 1
    +
    +        def __init__(self, thresh):
    +            mtransforms.Transform.__init__(self)
    +            self.thresh = thresh
    +
    +        def transform_non_affine(self, a):
    +            return np.arctan(np.sinh(a))
    +
    +        def inverted(self):
    +            return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh)
    +
    +
    +# Now that the Scale class has been defined, it must be registered so
    +# that Matplotlib can find it.
    +mscale.register_scale(MercatorLatitudeScale)
    +
    +
    +if __name__ == '__main__':
    +    import matplotlib.pyplot as plt
    +
    +    t = np.arange(-180.0, 180.0, 0.1)
    +    s = np.radians(t)/2.
    +
    +    plt.plot(t, s, '-', lw=2)
    +    plt.yscale('mercator')
    +
    +    plt.xlabel('Longitude')
    +    plt.ylabel('Latitude')
    +    plt.title('Mercator projection')
    +    plt.grid(True)
    +
    +    plt.show()
    diff --git a/galleries/examples/scales/log_demo.py b/galleries/examples/scales/log_demo.py
    new file mode 100644
    index 000000000000..ce0c77551c0a
    --- /dev/null
    +++ b/galleries/examples/scales/log_demo.py
    @@ -0,0 +1,83 @@
    +"""
    +=========
    +Log scale
    +=========
    +
    +Examples of plots with logarithmic axes.
    +
    +You can set the x/y axes to be logarithmic by passing "log" to `~.Axes.set_xscale` /
    +`~.Axes.set_yscale`.
    +
    +Convenience functions ``semilogx``, ``semilogy``, and ``loglog``
    +----------------------------------------------------------------
    +Since plotting data on semi-logarithmic or double-logarithmic scales is very common,
    +the functions `~.Axes.semilogx`, `~.Axes.semilogy`, and `~.Axes.loglog` are shortcuts
    +for setting the scale and plotting data; e.g. ``ax.semilogx(x, y)`` is equivalent to
    +``ax.set_xscale('log'); ax.plot(x, y)``.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(1, 3, layout='constrained', figsize=(7, 7/3))
    +# log x axis
    +t = np.arange(0.01, 10.0, 0.01)
    +ax1.semilogx(t, np.sin(2 * np.pi * t))
    +ax1.set(title='semilogx')
    +ax1.grid()
    +ax1.grid(which="minor", color="0.9")
    +
    +# log y axis
    +x = np.arange(4)
    +ax2.semilogy(4*x, 10**x, 'o--')
    +ax2.set(title='semilogy')
    +ax2.grid()
    +ax2.grid(which="minor", color="0.9")
    +
    +# log x and y axis
    +x = np.array([1, 10, 100, 1000])
    +ax3.loglog(x, 5 * x, 'o--')
    +ax3.set(title='loglog')
    +ax3.grid()
    +ax3.grid(which="minor", color="0.9")
    +
    +# %%
    +# Logarithms with other bases
    +# ---------------------------
    +# By default, the log scale is to the base 10. One can change this via the *base*
    +# parameter.
    +fig, ax = plt.subplots()
    +ax.bar(["L1 cache", "L2 cache", "L3 cache", "RAM", "SSD"],
    +       [32, 1_000, 32_000, 16_000_000, 512_000_000])
    +ax.set_yscale('log', base=2)
    +ax.set_yticks([1, 2**10, 2**20, 2**30], labels=['kB', 'MB', 'GB', 'TB'])
    +ax.set_title("Typical memory sizes")
    +ax.yaxis.grid()
    +
    +# %%
    +# Dealing with negative values
    +# ----------------------------
    +# Non-positive values cannot be displayed on a log scale. The scale has two options
    +# to handle these. Either mask the values so that they are ignored, or clip them
    +# to a small positive value. Which one is more suited depends on the type of the
    +# data and the visualization.
    +#
    +# The following example contains errorbars going negative. If we mask these values,
    +# the bar vanishes, which is not desirable. In contrast, clipping makes the value
    +# small positive (but well below the used scale) so that the error bar is drawn
    +# to the edge of the Axes.
    +x = np.linspace(0.0, 2.0, 10)
    +y = 10**x
    +yerr = 1.75 + 0.75*y
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, layout="constrained", figsize=(6, 3))
    +fig.suptitle("errorbars going negative")
    +ax1.set_yscale("log", nonpositive='mask')
    +ax1.set_title('nonpositive="mask"')
    +ax1.errorbar(x, y, yerr=yerr, fmt='o', capsize=5)
    +
    +ax2.set_yscale("log", nonpositive='clip')
    +ax2.set_title('nonpositive="clip"')
    +ax2.errorbar(x, y, yerr=yerr, fmt='o', capsize=5)
    +
    +plt.show()
    diff --git a/galleries/examples/scales/logit_demo.py b/galleries/examples/scales/logit_demo.py
    new file mode 100644
    index 000000000000..e8d42fc35711
    --- /dev/null
    +++ b/galleries/examples/scales/logit_demo.py
    @@ -0,0 +1,75 @@
    +"""
    +===========
    +Logit scale
    +===========
    +
    +Examples of plots with logit axes.
    +
    +This example visualises how ``set_yscale("logit")`` works on probability plots
    +by generating three distributions: normal, laplacian, and cauchy in one plot.
    +
    +The advantage of logit scale is that it effectively spreads out values close to 0 and 1.
    +
    +In a linear scale plot, probability values near 0 and 1 appear compressed,
    +making it difficult to see differences in those regions.
    +
    +In a logit scale plot, the transformation expands these regions,
    +making the graph cleaner and easier to compare across different probability values.
    +
    +This makes the logit scale especially useful when visalising probabilities in logistic
    +regression, classification models, and cumulative distribution functions.
    +"""
    +
    +import math
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +xmax = 10
    +x = np.linspace(-xmax, xmax, 10000)
    +cdf_norm = [math.erf(w / np.sqrt(2)) / 2 + 1 / 2 for w in x]
    +cdf_laplacian = np.where(x < 0, 1 / 2 * np.exp(x), 1 - 1 / 2 * np.exp(-x))
    +cdf_cauchy = np.arctan(x) / np.pi + 1 / 2
    +
    +fig, axs = plt.subplots(nrows=3, ncols=2, figsize=(6.4, 8.5))
    +
    +# Common part, for the example, we will do the same plots on all graphs
    +for i in range(3):
    +    for j in range(2):
    +        axs[i, j].plot(x, cdf_norm, label=r"$\mathcal{N}$")
    +        axs[i, j].plot(x, cdf_laplacian, label=r"$\mathcal{L}$")
    +        axs[i, j].plot(x, cdf_cauchy, label="Cauchy")
    +        axs[i, j].legend()
    +        axs[i, j].grid()
    +
    +# First line, logitscale, with standard notation
    +axs[0, 0].set(title="logit scale")
    +axs[0, 0].set_yscale("logit")
    +axs[0, 0].set_ylim(1e-5, 1 - 1e-5)
    +
    +axs[0, 1].set(title="logit scale")
    +axs[0, 1].set_yscale("logit")
    +axs[0, 1].set_xlim(0, xmax)
    +axs[0, 1].set_ylim(0.8, 1 - 5e-3)
    +
    +# Second line, logitscale, with survival notation (with `use_overline`), and
    +# other format display 1/2
    +axs[1, 0].set(title="logit scale")
    +axs[1, 0].set_yscale("logit", one_half="1/2", use_overline=True)
    +axs[1, 0].set_ylim(1e-5, 1 - 1e-5)
    +
    +axs[1, 1].set(title="logit scale")
    +axs[1, 1].set_yscale("logit", one_half="1/2", use_overline=True)
    +axs[1, 1].set_xlim(0, xmax)
    +axs[1, 1].set_ylim(0.8, 1 - 5e-3)
    +
    +# Third line, linear scale
    +axs[2, 0].set(title="linear scale")
    +axs[2, 0].set_ylim(0, 1)
    +
    +axs[2, 1].set(title="linear scale")
    +axs[2, 1].set_xlim(0, xmax)
    +axs[2, 1].set_ylim(0.8, 1)
    +
    +fig.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/scales/power_norm.py b/galleries/examples/scales/power_norm.py
    new file mode 100644
    index 000000000000..e53e015393b0
    --- /dev/null
    +++ b/galleries/examples/scales/power_norm.py
    @@ -0,0 +1,49 @@
    +"""
    +========================
    +Exploring normalizations
    +========================
    +
    +Various normalization on a multivariate normal distribution.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +from numpy.random import multivariate_normal
    +
    +import matplotlib.colors as mcolors
    +
    +# Fixing random state for reproducibility.
    +np.random.seed(19680801)
    +
    +data = np.vstack([
    +    multivariate_normal([10, 10], [[3, 2], [2, 3]], size=100000),
    +    multivariate_normal([30, 20], [[3, 1], [1, 3]], size=1000)
    +])
    +
    +gammas = [0.8, 0.5, 0.3]
    +
    +fig, axs = plt.subplots(nrows=2, ncols=2)
    +
    +axs[0, 0].set_title('Linear normalization')
    +axs[0, 0].hist2d(data[:, 0], data[:, 1], bins=100)
    +
    +for ax, gamma in zip(axs.flat[1:], gammas):
    +    ax.set_title(r'Power law $(\gamma=%1.1f)$' % gamma)
    +    ax.hist2d(data[:, 0], data[:, 1], bins=100, norm=mcolors.PowerNorm(gamma))
    +
    +fig.tight_layout()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colors`
    +#    - `matplotlib.colors.PowerNorm`
    +#    - `matplotlib.axes.Axes.hist2d`
    +#    - `matplotlib.pyplot.hist2d`
    diff --git a/galleries/examples/scales/scales.py b/galleries/examples/scales/scales.py
    new file mode 100644
    index 000000000000..6c4556c9c1d3
    --- /dev/null
    +++ b/galleries/examples/scales/scales.py
    @@ -0,0 +1,76 @@
    +"""
    +===============
    +Scales overview
    +===============
    +
    +Illustrate the scale transformations applied to axes, e.g. log, symlog, logit.
    +
    +See `matplotlib.scale` for a full list of built-in scales, and
    +:doc:`/gallery/scales/custom_scale` for how to create your own scale.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.arange(400)
    +y = np.linspace(0.002, 1, 400)
    +
    +fig, axs = plt.subplots(3, 2, figsize=(6, 8), layout='constrained')
    +
    +axs[0, 0].plot(x, y)
    +axs[0, 0].set_yscale('linear')
    +axs[0, 0].set_title('linear')
    +axs[0, 0].grid(True)
    +
    +axs[0, 1].plot(x, y)
    +axs[0, 1].set_yscale('log')
    +axs[0, 1].set_title('log')
    +axs[0, 1].grid(True)
    +
    +axs[1, 0].plot(x, y - y.mean())
    +axs[1, 0].set_yscale('symlog', linthresh=0.02)
    +axs[1, 0].set_title('symlog')
    +axs[1, 0].grid(True)
    +
    +axs[1, 1].plot(x, y)
    +axs[1, 1].set_yscale('logit')
    +axs[1, 1].set_title('logit')
    +axs[1, 1].grid(True)
    +
    +axs[2, 0].plot(x, y - y.mean())
    +axs[2, 0].set_yscale('asinh', linear_width=0.01)
    +axs[2, 0].set_title('asinh')
    +axs[2, 0].grid(True)
    +
    +
    +# Function x**(1/2)
    +def forward(x):
    +    return x**(1/2)
    +
    +
    +def inverse(x):
    +    return x**2
    +
    +
    +axs[2, 1].plot(x, y)
    +axs[2, 1].set_yscale('function', functions=(forward, inverse))
    +axs[2, 1].set_title('function: $x^{1/2}$')
    +axs[2, 1].grid(True)
    +axs[2, 1].set_yticks(np.arange(0, 1.2, 0.2))
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.set_xscale`
    +#    - `matplotlib.axes.Axes.set_yscale`
    +#    - `matplotlib.scale.LinearScale`
    +#    - `matplotlib.scale.LogScale`
    +#    - `matplotlib.scale.SymmetricalLogScale`
    +#    - `matplotlib.scale.LogitScale`
    +#    - `matplotlib.scale.FuncScale`
    diff --git a/galleries/examples/scales/symlog_demo.py b/galleries/examples/scales/symlog_demo.py
    new file mode 100644
    index 000000000000..47742b853cc9
    --- /dev/null
    +++ b/galleries/examples/scales/symlog_demo.py
    @@ -0,0 +1,123 @@
    +"""
    +============
    +Symlog scale
    +============
    +
    +The symmetric logarithmic scale is an extension of the logarithmic scale that
    +also covers negative values. As with the logarithmic scale, it is particularly
    +useful for numerical data that spans a broad range of values, especially when there
    +are significant differences between the magnitudes of the numbers involved.
    +
    +Example use of symlog (symmetric log) axis scaling.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +dt = 0.01
    +x = np.arange(-50.0, 50.0, dt)
    +y = np.arange(0, 100.0, dt)
    +
    +fig, (ax0, ax1, ax2) = plt.subplots(nrows=3)
    +
    +ax0.plot(x, y)
    +ax0.set_xscale('symlog')
    +ax0.set_ylabel('symlogx')
    +ax0.grid()
    +ax0.xaxis.grid(which='minor')  # minor grid on too
    +
    +ax1.plot(y, x)
    +ax1.set_yscale('symlog')
    +ax1.set_ylabel('symlogy')
    +
    +ax2.plot(x, np.sin(x / 3.0))
    +ax2.set_xscale('symlog')
    +ax2.set_yscale('symlog', linthresh=0.015)
    +ax2.grid()
    +ax2.set_ylabel('symlog both')
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +# Linear threshold
    +# ----------------
    +# Since each decade on a logarithmic scale covers the same amount of visual space
    +# and there are infinitely many decades between a given number and zero, the symlog
    +# scale must deviate from logarithmic mapping in a small range
    +# *(-linthresh, linthresh)*, so that the range is mapped to a finite visual space.
    +
    +
    +def format_axes(ax, title=None):
    +    """A helper function to better visualize properties of the symlog scale."""
    +    ax.xaxis.get_minor_locator().set_params(subs=[2, 3, 4, 5, 6, 7, 8, 9])
    +    ax.grid()
    +    ax.xaxis.grid(which='minor')  # minor grid on too
    +    linthresh = ax.xaxis.get_transform().linthresh
    +    linscale = ax.xaxis.get_transform().linscale
    +    ax.axvspan(-linthresh, linthresh, color='0.9')
    +    if title:
    +        ax.set_title(title.format(linthresh=linthresh, linscale=linscale))
    +
    +
    +x = np.linspace(-60, 60, 201)
    +y = np.linspace(0, 100.0, 201)
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, layout="constrained")
    +
    +ax1.plot(x, y)
    +ax1.set_xscale('symlog', linthresh=1)
    +format_axes(ax1, title='Linear region: linthresh={linthresh}')
    +
    +ax2.plot(x, y)
    +ax2.set_xscale('symlog', linthresh=5)
    +format_axes(ax2, title='Linear region: linthresh={linthresh}')
    +
    +# %%
    +# Generally, *linthresh* should be chosen so that no or only a few
    +# data points are in the linear region. As a rule of thumb,
    +# :math:`linthresh \approx \mathrm{min} |x|`.
    +#
    +#
    +# Linear scale
    +# ------------
    +# Additionally, the *linscale* parameter determines how much visual space should be
    +# used for the linear range. More precisely, it defines the ratio of visual space
    +# of the region (0, linthresh) relative to one decade.
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, layout="constrained")
    +
    +ax1.plot(x, y)
    +ax1.set_xscale('symlog', linthresh=1)
    +format_axes(ax1, title='Linear region: linthresh={linthresh}, linscale={linscale}')
    +
    +ax2.plot(x, y)
    +ax2.set_xscale('symlog', linthresh=1, linscale=0.1)
    +format_axes(ax2, title='Linear region: linthresh={linthresh}, linscale={linscale}')
    +
    +# %%
    +# The suitable value for linscale depends on the dynamic range of data. As most data
    +# will be outside the linear region, you typically the linear region only to cover
    +# a small fraction of the visual area.
    +#
    +# Limitations and alternatives
    +# ----------------------------
    +# The coordinate transform used by ``symlog`` has a discontinuous gradient at the
    +# transition between its linear and logarithmic regions. Depending on data and
    +# scaling, this will be more or less obvious in the plot.
    +
    +fig, ax = plt.subplots()
    +ax.plot(x, y)
    +ax.set_xscale('symlog', linscale=0.05)
    +format_axes(ax, title="Discontinuous gradient at linear/log transition")
    +
    +# %%
    +# The ``asinh`` axis scale is an alternative transformation that supports a wide
    +# dynamic range with a smooth gradient and thus may avoid such visual artifacts.
    +# See :doc:`/gallery/scales/asinh_demo`.
    +#
    +#
    +# .. admonition:: References
    +#
    +#    - `matplotlib.scale.SymmetricalLogScale`
    +#    - `matplotlib.ticker.SymmetricalLogLocator`
    +#    - `matplotlib.scale.AsinhScale`
    diff --git a/galleries/examples/shapes_and_collections/README.txt b/galleries/examples/shapes_and_collections/README.txt
    new file mode 100644
    index 000000000000..bce3cbe038e3
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/README.txt
    @@ -0,0 +1,5 @@
    +.. _shapes_and_collections_examples:
    +
    +
    +Shapes and collections
    +======================
    diff --git a/galleries/examples/shapes_and_collections/arrow_guide.py b/galleries/examples/shapes_and_collections/arrow_guide.py
    new file mode 100644
    index 000000000000..d9fad893a873
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/arrow_guide.py
    @@ -0,0 +1,127 @@
    +"""
    +===========
    +Arrow guide
    +===========
    +
    +Adding arrow patches to plots.
    +
    +Arrows are often used to annotate plots. This tutorial shows how to plot arrows
    +that behave differently when the data limits on a plot are changed. In general,
    +points on a plot can either be fixed in "data space" or "display space".
    +Something plotted in data space moves when the data limits are altered - an
    +example would be the points in a scatter plot. Something plotted in display
    +space stays static when data limits are altered - an example would be a
    +figure title or the axis labels.
    +
    +Arrows consist of a head (and possibly a tail) and a stem drawn between a
    +start point and end point, called 'anchor points' from now on.
    +Here we show three use cases for plotting arrows, depending on whether the
    +head or anchor points need to be fixed in data or display space:
    +
    +1. Head shape fixed in display space, anchor points fixed in data space
    +2. Head shape and anchor points fixed in display space
    +3. Entire patch fixed in data space
    +
    +Below each use case is presented in turn.
    +
    +.. redirect-from:: /gallery/text_labels_and_annotations/arrow_simple_demo
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.patches as mpatches
    +
    +x_tail = 0.1
    +y_tail = 0.5
    +x_head = 0.9
    +y_head = 0.8
    +dx = x_head - x_tail
    +dy = y_head - y_tail
    +
    +
    +# %%
    +# Head shape fixed in display space and anchor points fixed in data space
    +# -----------------------------------------------------------------------
    +#
    +# This is useful if you are annotating a plot, and don't want the arrow
    +# to change shape or position if you pan or scale the plot.
    +#
    +# In this case we use `.patches.FancyArrowPatch`.
    +#
    +# Note that when the axis limits are changed, the arrow shape stays the same,
    +# but the anchor points move.
    +
    +fig, axs = plt.subplots(nrows=2)
    +arrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),
    +                                 mutation_scale=100)
    +axs[0].add_patch(arrow)
    +
    +arrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),
    +                                 mutation_scale=100)
    +axs[1].add_patch(arrow)
    +axs[1].set(xlim=(0, 2), ylim=(0, 2))
    +
    +# %%
    +# Head shape and anchor points fixed in display space
    +# ---------------------------------------------------
    +#
    +# This is useful if you are annotating a plot, and don't want the arrow to
    +# change shape or position if you pan or scale the plot.
    +#
    +# In this case we use `.patches.FancyArrowPatch`, and pass the keyword argument
    +# ``transform=ax.transAxes`` where ``ax`` is the Axes we are adding the patch
    +# to.
    +#
    +# Note that when the axis limits are changed, the arrow shape and location
    +# stay the same.
    +
    +fig, axs = plt.subplots(nrows=2)
    +arrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),
    +                                 mutation_scale=100,
    +                                 transform=axs[0].transAxes)
    +axs[0].add_patch(arrow)
    +
    +arrow = mpatches.FancyArrowPatch((x_tail, y_tail), (x_head, y_head),
    +                                 mutation_scale=100,
    +                                 transform=axs[1].transAxes)
    +axs[1].add_patch(arrow)
    +axs[1].set(xlim=(0, 2), ylim=(0, 2))
    +
    +
    +# %%
    +# Head shape and anchor points fixed in data space
    +# ------------------------------------------------
    +#
    +# In this case we use `.patches.Arrow`, or `.patches.FancyArrow` (the latter is
    +# in orange).
    +#
    +# Note that when the axis limits are changed, the arrow shape and location
    +# change.
    +#
    +# `.FancyArrow`'s API is relatively awkward, and requires in particular passing
    +# ``length_includes_head=True`` so that the arrow *tip* is ``(dx, dy)`` away
    +# from the arrow start.  It is only included in this reference because it is
    +# the arrow class returned by `.Axes.arrow` (in green).
    +
    +fig, axs = plt.subplots(nrows=2)
    +
    +arrow = mpatches.Arrow(x_tail, y_tail, dx, dy)
    +axs[0].add_patch(arrow)
    +arrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy,
    +                            width=.1, length_includes_head=True, color="C1")
    +axs[0].add_patch(arrow)
    +axs[0].arrow(x_tail + 1, y_tail - .4, dx, dy,
    +             width=.1, length_includes_head=True, color="C2")
    +
    +arrow = mpatches.Arrow(x_tail, y_tail, dx, dy)
    +axs[1].add_patch(arrow)
    +arrow = mpatches.FancyArrow(x_tail, y_tail - .4, dx, dy,
    +                            width=.1, length_includes_head=True, color="C1")
    +axs[1].add_patch(arrow)
    +axs[1].arrow(x_tail + 1, y_tail - .4, dx, dy,
    +             width=.1, length_includes_head=True, color="C2")
    +axs[1].set(xlim=(0, 2), ylim=(0, 2))
    +
    +# %%
    +
    +plt.show()
    diff --git a/galleries/examples/shapes_and_collections/artist_reference.py b/galleries/examples/shapes_and_collections/artist_reference.py
    new file mode 100644
    index 000000000000..048018a9898e
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/artist_reference.py
    @@ -0,0 +1,80 @@
    +"""
    +.. _artist_reference:
    +
    +================================
    +Reference for Matplotlib artists
    +================================
    +
    +This example displays several of Matplotlib's graphics primitives (artists).
    +A full list of artists is documented at :ref:`the artist API `.
    +
    +See also :doc:`/gallery/shapes_and_collections/patch_collection`, which groups
    +all artists into a single `.PatchCollection` instead.
    +
    +Copyright (c) 2010, Bartosz Telenczuk
    +BSD License
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib as mpl
    +import matplotlib.lines as mlines
    +import matplotlib.patches as mpatches
    +import matplotlib.path as mpath
    +
    +# Prepare the data for the PathPatch below.
    +Path = mpath.Path
    +codes, verts = zip(*[
    +    (Path.MOVETO, [0.018, -0.11]),
    +    (Path.CURVE4, [-0.031, -0.051]),
    +    (Path.CURVE4, [-0.115, 0.073]),
    +    (Path.CURVE4, [-0.03, 0.073]),
    +    (Path.LINETO, [-0.011, 0.039]),
    +    (Path.CURVE4, [0.043, 0.121]),
    +    (Path.CURVE4, [0.075, -0.005]),
    +    (Path.CURVE4, [0.035, -0.027]),
    +    (Path.CLOSEPOLY, [0.018, -0.11])])
    +
    +artists = [
    +    mpatches.Circle((0, 0), 0.1, ec="none"),
    +    mpatches.Rectangle((-0.025, -0.05), 0.05, 0.1, ec="none"),
    +    mpatches.Wedge((0, 0), 0.1, 30, 270, ec="none"),
    +    mpatches.RegularPolygon((0, 0), 5, radius=0.1),
    +    mpatches.Ellipse((0, 0), 0.2, 0.1),
    +    mpatches.Arrow(-0.05, -0.05, 0.1, 0.1, width=0.1),
    +    mpatches.PathPatch(mpath.Path(verts, codes), ec="none"),
    +    mpatches.FancyBboxPatch((-0.025, -0.05), 0.05, 0.1, ec="none",
    +                            boxstyle=mpatches.BoxStyle("Round", pad=0.02)),
    +    mlines.Line2D([-0.06, 0.0, 0.1], [0.05, -0.05, 0.05], lw=5),
    +]
    +
    +axs = plt.figure(figsize=(6, 6), layout="constrained").subplots(3, 3)
    +for i, (ax, artist) in enumerate(zip(axs.flat, artists)):
    +    artist.set(color=mpl.colormaps["hsv"](i / len(artists)))
    +    ax.add_artist(artist)
    +    ax.set(title=type(artist).__name__,
    +           aspect=1, xlim=(-.2, .2), ylim=(-.2, .2))
    +    ax.set_axis_off()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.lines`
    +#    - `matplotlib.lines.Line2D`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.patches.Ellipse`
    +#    - `matplotlib.patches.Wedge`
    +#    - `matplotlib.patches.Rectangle`
    +#    - `matplotlib.patches.Arrow`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.patches.FancyBboxPatch`
    +#    - `matplotlib.patches.RegularPolygon`
    +#    - `matplotlib.axes.Axes.add_artist`
    diff --git a/galleries/examples/shapes_and_collections/collections.py b/galleries/examples/shapes_and_collections/collections.py
    new file mode 100644
    index 000000000000..a5b25fd8d2bb
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/collections.py
    @@ -0,0 +1,115 @@
    +"""
    +=====================================
    +Line, Poly and RegularPoly Collection
    +=====================================
    +
    +For the first two subplots, we will use spirals.  Their size will be set in
    +plot units, not data units.  Their positions will be set in data units by using
    +the *offsets* and *offset_transform* keyword arguments of the `.LineCollection`
    +and `.PolyCollection`.
    +
    +The third subplot will make regular polygons, with the same
    +type of scaling and positioning as in the first two.
    +
    +The last subplot illustrates the use of ``offsets=(xo, yo)``,
    +that is, a single tuple instead of a list of tuples, to generate
    +successively offset curves, with the offset given in data
    +units.  This behavior is available only for the LineCollection.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import collections, transforms
    +
    +nverts = 50
    +npts = 100
    +
    +# Make some spirals
    +r = np.arange(nverts)
    +theta = np.linspace(0, 2*np.pi, nverts)
    +xx = r * np.sin(theta)
    +yy = r * np.cos(theta)
    +spiral = np.column_stack([xx, yy])
    +
    +# Fixing random state for reproducibility
    +rs = np.random.RandomState(19680801)
    +
    +# Make some offsets
    +xyo = rs.randn(npts, 2)
    +
    +# Make a list of colors from the default color cycle.
    +colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
    +
    +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
    +fig.subplots_adjust(top=0.92, left=0.07, right=0.97,
    +                    hspace=0.3, wspace=0.3)
    +
    +
    +col = collections.LineCollection(
    +    [spiral], offsets=xyo, offset_transform=ax1.transData, color=colors)
    +# transform the line segments such that their size is given in points
    +trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0/72.0)
    +col.set_transform(trans)  # the points to pixels transform
    +ax1.add_collection(col)
    +ax1.set_title('LineCollection using offsets')
    +
    +
    +# The same data as above, but fill the curves.
    +col = collections.PolyCollection(
    +    [spiral], offsets=xyo, offset_transform=ax2.transData, color=colors)
    +trans = transforms.Affine2D().scale(fig.dpi/72.0)
    +col.set_transform(trans)  # the points to pixels transform
    +ax2.add_collection(col)
    +ax2.set_title('PolyCollection using offsets')
    +
    +
    +# 7-sided regular polygons
    +col = collections.RegularPolyCollection(
    +    7, sizes=np.abs(xx) * 10.0, offsets=xyo, offset_transform=ax3.transData,
    +    color=colors)
    +trans = transforms.Affine2D().scale(fig.dpi / 72.0)
    +col.set_transform(trans)  # the points to pixels transform
    +ax3.add_collection(col)
    +ax3.set_title('RegularPolyCollection using offsets')
    +
    +
    +# Simulate a series of ocean current profiles, successively
    +# offset by 0.1 m/s so that they form what is sometimes called
    +# a "waterfall" plot or a "stagger" plot.
    +nverts = 60
    +ncurves = 20
    +offs = (0.1, 0.0)
    +
    +yy = np.linspace(0, 2*np.pi, nverts)
    +ym = np.max(yy)
    +xx = (0.2 + (ym - yy) / ym) ** 2 * np.cos(yy - 0.4) * 0.5
    +segs = []
    +for i in range(ncurves):
    +    xxx = xx + 0.02*rs.randn(nverts)
    +    curve = np.column_stack([xxx, yy * 100])
    +    segs.append(curve)
    +
    +col = collections.LineCollection(segs, offsets=offs, color=colors)
    +ax4.add_collection(col)
    +ax4.set_title('Successive data offsets')
    +ax4.set_xlabel('Zonal velocity component (m/s)')
    +ax4.set_ylabel('Depth (m)')
    +ax4.invert_yaxis()  # so that depth increases downward
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure`
    +#    - `matplotlib.collections`
    +#    - `matplotlib.collections.LineCollection`
    +#    - `matplotlib.collections.RegularPolyCollection`
    +#    - `matplotlib.axes.Axes.add_collection`
    +#    - `matplotlib.transforms.Affine2D`
    +#    - `matplotlib.transforms.Affine2D.scale`
    diff --git a/galleries/examples/shapes_and_collections/compound_path.py b/galleries/examples/shapes_and_collections/compound_path.py
    new file mode 100644
    index 000000000000..0afee13cb2fc
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/compound_path.py
    @@ -0,0 +1,49 @@
    +"""
    +=============
    +Compound path
    +=============
    +
    +Make a compound path -- in this case two simple polygons, a rectangle
    +and a triangle.  Use ``CLOSEPOLY`` and ``MOVETO`` for the different parts of
    +the compound path
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.patches import PathPatch
    +from matplotlib.path import Path
    +
    +vertices = []
    +codes = []
    +
    +codes = [Path.MOVETO] + [Path.LINETO]*3 + [Path.CLOSEPOLY]
    +vertices = [(1, 1), (1, 2), (2, 2), (2, 1), (0, 0)]
    +
    +codes += [Path.MOVETO] + [Path.LINETO]*2 + [Path.CLOSEPOLY]
    +vertices += [(4, 4), (5, 5), (5, 4), (0, 0)]
    +
    +path = Path(vertices, codes)
    +
    +pathpatch = PathPatch(path, facecolor='none', edgecolor='green')
    +
    +fig, ax = plt.subplots()
    +ax.add_patch(pathpatch)
    +ax.set_title('A compound path')
    +
    +ax.autoscale_view()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.axes.Axes.autoscale_view`
    diff --git a/galleries/examples/shapes_and_collections/dolphin.py b/galleries/examples/shapes_and_collections/dolphin.py
    new file mode 100644
    index 000000000000..7c40eddcedd4
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/dolphin.py
    @@ -0,0 +1,127 @@
    +"""
    +========
    +Dolphins
    +========
    +
    +This example shows how to draw, and manipulate shapes given vertices
    +and nodes using the `~.path.Path`, `~.patches.PathPatch` and
    +`~matplotlib.transforms` classes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Circle, PathPatch
    +from matplotlib.path import Path
    +from matplotlib.transforms import Affine2D
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +r = np.random.rand(50)
    +t = np.random.rand(50) * np.pi * 2.0
    +x = r * np.cos(t)
    +y = r * np.sin(t)
    +
    +fig, ax = plt.subplots(figsize=(6, 6))
    +circle = Circle((0, 0), 1, facecolor='none',
    +                edgecolor=(0, 0.8, 0.8), linewidth=3, alpha=0.5)
    +ax.add_patch(circle)
    +
    +im = plt.imshow(np.random.random((100, 100)),
    +                origin='lower', cmap="winter",
    +                interpolation='spline36',
    +                extent=(-1, 1, -1, 1))
    +im.set_clip_path(circle)
    +
    +plt.plot(x, y, 'o', color=(0.9, 0.9, 1.0), alpha=0.8)
    +
    +# Dolphin from OpenClipart library by Andy Fitzsimon
    +#   
    +#     
    +#     
    +#     
    +#   
    +
    +dolphin = """
    +M -0.59739425,160.18173 C -0.62740401,160.18885 -0.57867129,160.11183
    +-0.57867129,160.11183 C -0.57867129,160.11183 -0.5438361,159.89315
    +-0.39514638,159.81496 C -0.24645668,159.73678 -0.18316813,159.71981
    +-0.18316813,159.71981 C -0.18316813,159.71981 -0.10322971,159.58124
    +-0.057804323,159.58725 C -0.029723983,159.58913 -0.061841603,159.60356
    +-0.071265813,159.62815 C -0.080250183,159.65325 -0.082918513,159.70554
    +-0.061841203,159.71248 C -0.040763903,159.7194 -0.0066711426,159.71091
    +0.077336307,159.73612 C 0.16879567,159.76377 0.28380306,159.86448
    +0.31516668,159.91533 C 0.3465303,159.96618 0.5011127,160.1771
    +0.5011127,160.1771 C 0.63668998,160.19238 0.67763022,160.31259
    +0.66556395,160.32668 C 0.65339985,160.34212 0.66350443,160.33642
    +0.64907098,160.33088 C 0.63463742,160.32533 0.61309688,160.297
    +0.5789627,160.29339 C 0.54348657,160.28968 0.52329693,160.27674
    +0.50728856,160.27737 C 0.49060916,160.27795 0.48965803,160.31565
    +0.46114204,160.33673 C 0.43329696,160.35786 0.4570711,160.39871
    +0.43309565,160.40685 C 0.4105108,160.41442 0.39416631,160.33027
    +0.3954995,160.2935 C 0.39683269,160.25672 0.43807996,160.21522
    +0.44567915,160.19734 C 0.45327833,160.17946 0.27946869,159.9424
    +-0.061852613,159.99845 C -0.083965233,160.0427 -0.26176109,160.06683
    +-0.26176109,160.06683 C -0.30127962,160.07028 -0.21167141,160.09731
    +-0.24649368,160.1011 C -0.32642366,160.11569 -0.34521187,160.06895
    +-0.40622293,160.0819 C -0.467234,160.09485 -0.56738444,160.17461
    +-0.59739425,160.18173
    +"""
    +
    +vertices = []
    +codes = []
    +parts = dolphin.split()
    +i = 0
    +code_map = {
    +    'M': Path.MOVETO,
    +    'C': Path.CURVE4,
    +    'L': Path.LINETO,
    +}
    +
    +while i < len(parts):
    +    path_code = code_map[parts[i]]
    +    npoints = Path.NUM_VERTICES_FOR_CODE[path_code]
    +    codes.extend([path_code] * npoints)
    +    vertices.extend([[*map(float, y.split(','))]
    +                     for y in parts[i + 1:][:npoints]])
    +    i += npoints + 1
    +vertices = np.array(vertices)
    +vertices[:, 1] -= 160
    +
    +dolphin_path = Path(vertices, codes)
    +dolphin_patch = PathPatch(dolphin_path, facecolor=(0.6, 0.6, 0.6),
    +                          edgecolor=(0.0, 0.0, 0.0))
    +ax.add_patch(dolphin_patch)
    +
    +vertices = Affine2D().rotate_deg(60).transform(vertices)
    +dolphin_path2 = Path(vertices, codes)
    +dolphin_patch2 = PathPatch(dolphin_path2, facecolor=(0.5, 0.5, 0.5),
    +                           edgecolor=(0.0, 0.0, 0.0))
    +ax.add_patch(dolphin_patch2)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.transforms`
    +#    - `matplotlib.transforms.Affine2D`
    +#    - `matplotlib.transforms.Affine2D.rotate_deg`
    +#
    +# .. tags::
    +#
    +#    component: patch
    +#    styling: shape
    +#    purpose: fun
    diff --git a/galleries/examples/shapes_and_collections/donut.py b/galleries/examples/shapes_and_collections/donut.py
    new file mode 100644
    index 000000000000..3aa5b0b1a75c
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/donut.py
    @@ -0,0 +1,80 @@
    +r"""
    +=============
    +Mmh Donuts!!!
    +=============
    +
    +Draw donuts (miam!) using `~.path.Path`\s and `~.patches.PathPatch`\es.
    +This example shows the effect of the path's orientations in a compound path.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.patches as mpatches
    +import matplotlib.path as mpath
    +
    +
    +def wise(v):
    +    return {+1: "CCW", -1: "CW"}[v]
    +
    +
    +def make_circle(r):
    +    t = np.arange(0, np.pi * 2.0, 0.01)
    +    x = r * np.cos(t)
    +    y = r * np.sin(t)
    +    return np.column_stack((x, y))
    +
    +
    +fig, ax = plt.subplots()
    +
    +inside_vertices = make_circle(0.5)
    +outside_vertices = make_circle(1.0)
    +codes = np.full(len(inside_vertices), mpath.Path.LINETO)
    +codes[0] = mpath.Path.MOVETO
    +
    +for i, (inside, outside) in enumerate(((1, 1), (1, -1), (-1, 1), (-1, -1))):
    +    # Concatenate the inside and outside subpaths together, changing their
    +    # order as needed
    +    vertices = np.concatenate((outside_vertices[::outside],
    +                               inside_vertices[::inside]))
    +    # Shift the path
    +    vertices += (i * 2.5, 0)
    +    # The codes will be all "LINETO" commands, except for "MOVETO"s at the
    +    # beginning of each subpath
    +    all_codes = np.concatenate((codes, codes))
    +    # Create the Path object
    +    path = mpath.Path(vertices, all_codes)
    +    # And plot it
    +    patch = mpatches.PathPatch(path, facecolor='#885500', edgecolor='black')
    +    ax.add_patch(patch)
    +
    +    ax.annotate(f"Outside {wise(outside)},\nInside {wise(inside)}",
    +                (i * 2.5, -1.5), va="top", ha="center")
    +
    +ax.set(xlim=(-2, 10), ylim=(-3, 2), aspect=1, title="Mmm, donuts!")
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.axes.Axes.annotate`
    +#    - `matplotlib.axes.Axes.set_aspect`
    +#    - `matplotlib.axes.Axes.set_xlim`
    +#    - `matplotlib.axes.Axes.set_ylim`
    +#    - `matplotlib.axes.Axes.set_title`
    +#
    +# .. tags::
    +#
    +#    component: patch
    +#    styling: shape
    +#    purpose: fun
    diff --git a/galleries/examples/shapes_and_collections/ellipse_arrow.py b/galleries/examples/shapes_and_collections/ellipse_arrow.py
    new file mode 100644
    index 000000000000..4bcdc016faa6
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/ellipse_arrow.py
    @@ -0,0 +1,53 @@
    +"""
    +===================================
    +Ellipse with orientation arrow demo
    +===================================
    +
    +This demo shows how to draw an ellipse with
    +an orientation arrow (clockwise or counterclockwise).
    +Compare this to the :doc:`Ellipse collection example
    +`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.markers import MarkerStyle
    +from matplotlib.patches import Ellipse
    +from matplotlib.transforms import Affine2D
    +
    +# Create a figure and axis
    +fig, ax = plt.subplots(subplot_kw={"aspect": "equal"})
    +
    +ellipse = Ellipse(
    +    xy=(2, 4),
    +    width=30,
    +    height=20,
    +    angle=35,
    +    facecolor="none",
    +    edgecolor="b"
    +)
    +ax.add_patch(ellipse)
    +
    +# Plot an arrow marker at the end point of minor axis
    +vertices = ellipse.get_co_vertices()
    +t = Affine2D().rotate_deg(ellipse.angle)
    +ax.plot(
    +    vertices[0][0],
    +    vertices[0][1],
    +    color="b",
    +    marker=MarkerStyle(">", "full", t),
    +    markersize=10
    +)
    +# Note: To reverse the orientation arrow, switch the marker type from > to <.
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Ellipse`
    diff --git a/galleries/examples/shapes_and_collections/ellipse_collection.py b/galleries/examples/shapes_and_collections/ellipse_collection.py
    new file mode 100644
    index 000000000000..39f0cb7dcb6a
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/ellipse_collection.py
    @@ -0,0 +1,49 @@
    +"""
    +==================
    +Ellipse Collection
    +==================
    +
    +Drawing a collection of ellipses. While this would equally be possible using
    +a `~.collections.EllipseCollection` or `~.collections.PathCollection`, the use
    +of an `~.collections.EllipseCollection` allows for much shorter code.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import EllipseCollection
    +
    +x = np.arange(10)
    +y = np.arange(15)
    +X, Y = np.meshgrid(x, y)
    +
    +XY = np.column_stack((X.ravel(), Y.ravel()))
    +
    +ww = X / 10.0
    +hh = Y / 15.0
    +aa = X * 9
    +
    +
    +fig, ax = plt.subplots()
    +
    +ec = EllipseCollection(ww, hh, aa, units='x', offsets=XY,
    +                       offset_transform=ax.transData)
    +ec.set_array((X + Y).ravel())
    +ax.add_collection(ec)
    +ax.set_xlabel('X')
    +ax.set_ylabel('y')
    +cbar = plt.colorbar(ec)
    +cbar.set_label('X+Y')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.collections`
    +#    - `matplotlib.collections.EllipseCollection`
    +#    - `matplotlib.axes.Axes.add_collection`
    +#    - `matplotlib.cm.ScalarMappable.set_array`
    diff --git a/galleries/examples/shapes_and_collections/ellipse_demo.py b/galleries/examples/shapes_and_collections/ellipse_demo.py
    new file mode 100644
    index 000000000000..0d7e72a1100d
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/ellipse_demo.py
    @@ -0,0 +1,68 @@
    +"""
    +============
    +Ellipse Demo
    +============
    +
    +Draw many ellipses. Here individual ellipses are drawn. Compare this
    +to the :doc:`Ellipse collection example
    +`.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Ellipse
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +NUM = 250
    +
    +ells = [Ellipse(xy=np.random.rand(2) * 10,
    +                width=np.random.rand(), height=np.random.rand(),
    +                angle=np.random.rand() * 360)
    +        for i in range(NUM)]
    +
    +fig, ax = plt.subplots()
    +ax.set(xlim=(0, 10), ylim=(0, 10), aspect="equal")
    +
    +for e in ells:
    +    ax.add_artist(e)
    +    e.set_clip_box(ax.bbox)
    +    e.set_alpha(np.random.rand())
    +    e.set_facecolor(np.random.rand(3))
    +
    +plt.show()
    +
    +# %%
    +# ===============
    +# Ellipse Rotated
    +# ===============
    +#
    +# Draw many ellipses with different angles.
    +#
    +
    +angle_step = 45  # degrees
    +angles = np.arange(0, 180, angle_step)
    +
    +fig, ax = plt.subplots()
    +ax.set(xlim=(-2.2, 2.2), ylim=(-2.2, 2.2), aspect="equal")
    +
    +for angle in angles:
    +    ellipse = Ellipse((0, 0), 4, 2, angle=angle, alpha=0.1)
    +    ax.add_artist(ellipse)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Ellipse`
    +#    - `matplotlib.axes.Axes.add_artist`
    +#    - `matplotlib.artist.Artist.set_clip_box`
    +#    - `matplotlib.artist.Artist.set_alpha`
    +#    - `matplotlib.patches.Patch.set_facecolor`
    diff --git a/galleries/examples/shapes_and_collections/fancybox_demo.py b/galleries/examples/shapes_and_collections/fancybox_demo.py
    new file mode 100644
    index 000000000000..8d36a5a14d9d
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/fancybox_demo.py
    @@ -0,0 +1,169 @@
    +"""
    +===================
    +Drawing fancy boxes
    +===================
    +
    +The following examples show how to plot boxes (`.FancyBboxPatch`) with different
    +visual properties.
    +"""
    +
    +import inspect
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.patches as mpatch
    +from matplotlib.patches import FancyBboxPatch
    +import matplotlib.transforms as mtransforms
    +
    +# %%
    +# Box styles
    +# ----------
    +# `.FancyBboxPatch` supports different `.BoxStyle`\s. Note that `~.Axes.text`
    +# allows to draw a box around the text by adding the ``bbox`` parameter. Therefore,
    +# you don't see explicit `.FancyBboxPatch` and `.BoxStyle` calls in the following
    +# example.
    +
    +styles = mpatch.BoxStyle.get_styles()
    +ncol = 2
    +nrow = (len(styles) + 1) // ncol
    +axs = (plt.figure(figsize=(3 * ncol, 1 + nrow))
    +       .add_gridspec(1 + nrow, ncol, wspace=.5).subplots())
    +for ax in axs.flat:
    +    ax.set_axis_off()
    +for ax in axs[0, :]:
    +    ax.text(.2, .5, "boxstyle",
    +            transform=ax.transAxes, size="large", color="tab:blue",
    +            horizontalalignment="right", verticalalignment="center")
    +    ax.text(.4, .5, "default parameters",
    +            transform=ax.transAxes,
    +            horizontalalignment="left", verticalalignment="center")
    +for ax, (stylename, stylecls) in zip(axs[1:, :].T.flat, styles.items()):
    +    ax.text(.2, .5, stylename, bbox=dict(boxstyle=stylename, fc="w", ec="k"),
    +            transform=ax.transAxes, size="large", color="tab:blue",
    +            horizontalalignment="right", verticalalignment="center")
    +    ax.text(.4, .5, str(inspect.signature(stylecls))[1:-1].replace(", ", "\n"),
    +            transform=ax.transAxes,
    +            horizontalalignment="left", verticalalignment="center")
    +
    +
    +# %%
    +# Parameters for modifying the box
    +# --------------------------------
    +# `.BoxStyle`\s have additional parameters to configure their appearance.
    +# For example, "round" boxes can have ``pad`` and ``rounding``.
    +#
    +# Additionally, the `.FancyBboxPatch` parameters ``mutation_scale`` and
    +# ``mutation_aspect`` scale the box appearance.
    +
    +def add_fancy_patch_around(ax, bb, **kwargs):
    +    kwargs = {
    +        'facecolor': (1, 0.8, 1, 0.5),
    +        'edgecolor': (1, 0.5, 1, 0.5),
    +        **kwargs
    +    }
    +    fancy = FancyBboxPatch(bb.p0, bb.width, bb.height, **kwargs)
    +    ax.add_patch(fancy)
    +    return fancy
    +
    +
    +def draw_control_points_for_patches(ax):
    +    for patch in ax.patches:
    +        patch.axes.plot(*patch.get_path().vertices.T, ".",
    +                        c=patch.get_edgecolor())
    +
    +
    +fig, axs = plt.subplots(2, 2, figsize=(8, 8))
    +
    +# Bbox object around which the fancy box will be drawn.
    +bb = mtransforms.Bbox([[0.3, 0.4], [0.7, 0.6]])
    +
    +ax = axs[0, 0]
    +# a fancy box with round corners. pad=0.1
    +add_fancy_patch_around(ax, bb, boxstyle="round,pad=0.1")
    +ax.set(xlim=(0, 1), ylim=(0, 1), aspect=1,
    +       title='boxstyle="round,pad=0.1"')
    +
    +ax = axs[0, 1]
    +# bbox=round has two optional arguments: pad and rounding_size.
    +# They can be set during the initialization.
    +fancy = add_fancy_patch_around(ax, bb, boxstyle="round,pad=0.1")
    +# The boxstyle and its argument can be later modified with set_boxstyle().
    +# Note that the old attributes are simply forgotten even if the boxstyle name
    +# is same.
    +fancy.set_boxstyle("round,pad=0.1,rounding_size=0.2")
    +# or: fancy.set_boxstyle("round", pad=0.1, rounding_size=0.2)
    +ax.set(xlim=(0, 1), ylim=(0, 1), aspect=1,
    +       title='boxstyle="round,pad=0.1,rounding_size=0.2"')
    +
    +ax = axs[1, 0]
    +# mutation_scale determines the overall scale of the mutation, i.e. both pad
    +# and rounding_size is scaled according to this value.
    +add_fancy_patch_around(ax, bb, boxstyle="round,pad=0.1", mutation_scale=2)
    +ax.set(xlim=(0, 1), ylim=(0, 1), aspect=1,
    +       title='boxstyle="round,pad=0.1"\n mutation_scale=2')
    +
    +ax = axs[1, 1]
    +# mutation_aspect scales the vertical influence of the parameters (technically,
    +# it scales the height of the box down by mutation_aspect, applies the box parameters
    +# and scales the result back up). In effect, the vertical pad is scaled to
    +# pad * mutation_aspect, e.g. mutation_aspect=0.5 halves the vertical pad.
    +add_fancy_patch_around(ax, bb, boxstyle="round,pad=0.1", mutation_aspect=0.5)
    +ax.set(xlim=(0, 1), ylim=(0, 1),
    +       title='boxstyle="round,pad=0.1"\nmutation_aspect=0.5')
    +
    +for ax in axs.flat:
    +    draw_control_points_for_patches(ax)
    +    # Draw the original bbox (using boxstyle=square with pad=0).
    +    add_fancy_patch_around(ax, bb, boxstyle="square,pad=0",
    +                           edgecolor="black", facecolor="none", zorder=10)
    +
    +fig.tight_layout()
    +
    +
    +plt.show()
    +
    +# %%
    +# Creating visually constant padding on non-equal aspect Axes
    +# -----------------------------------------------------------
    +# Since padding is in box coordinates, i.e. usually data coordinates,
    +# a given padding is rendered to different visual sizes if the
    +# Axes aspect is not 1.
    +# To get visually equal vertical and horizontal padding, set the
    +# mutation_aspect to the inverse of the Axes aspect. This scales
    +# the vertical padding appropriately.
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6.5, 5))
    +
    +# original boxes
    +bb = mtransforms.Bbox([[-0.5, -0.5], [0.5, 0.5]])
    +add_fancy_patch_around(ax1, bb, boxstyle="square,pad=0",
    +                       edgecolor="black", facecolor="none", zorder=10)
    +add_fancy_patch_around(ax2, bb, boxstyle="square,pad=0",
    +                       edgecolor="black", facecolor="none", zorder=10)
    +ax1.set(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5), aspect=2)
    +ax2.set(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5), aspect=2)
    +
    +
    +fancy = add_fancy_patch_around(
    +    ax1, bb, boxstyle="round,pad=0.5")
    +ax1.set_title("aspect=2\nmutation_aspect=1")
    +
    +fancy = add_fancy_patch_around(
    +    ax2, bb, boxstyle="round,pad=0.5", mutation_aspect=0.5)
    +ax2.set_title("aspect=2\nmutation_aspect=0.5")
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.FancyBboxPatch`
    +#    - `matplotlib.patches.BoxStyle`
    +#    - ``matplotlib.patches.BoxStyle.get_styles``
    +#    - `matplotlib.transforms.Bbox`
    +#    - `matplotlib.figure.Figure.text`
    +#    - `matplotlib.axes.Axes.text`
    diff --git a/galleries/examples/shapes_and_collections/hatch_demo.py b/galleries/examples/shapes_and_collections/hatch_demo.py
    new file mode 100644
    index 000000000000..8d44dba5489b
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/hatch_demo.py
    @@ -0,0 +1,61 @@
    +"""
    +==========
    +Hatch demo
    +==========
    +
    +Hatches can be added to most polygons in Matplotlib, including `~.Axes.bar`,
    +`~.Axes.fill_between`, `~.Axes.contourf`, and children of `~.patches.Polygon`.
    +They are currently supported in the PS, PDF, SVG, macosx, and Agg backends. The WX
    +and Cairo backends do not currently support hatching.
    +
    +See also :doc:`/gallery/images_contours_and_fields/contourf_hatching` for
    +an example using `~.Axes.contourf`, and
    +:doc:`/gallery/shapes_and_collections/hatch_style_reference` for swatches
    +of the existing hatches.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Ellipse, Polygon
    +
    +x = np.arange(1, 5)
    +y1 = np.arange(1, 5)
    +y2 = np.ones(y1.shape) * 4
    +
    +fig = plt.figure()
    +axs = fig.subplot_mosaic([['bar1', 'patches'], ['bar2', 'patches']])
    +
    +axs['bar1'].bar(x, y1, edgecolor='black', hatch="/")
    +axs['bar1'].bar(x, y2, bottom=y1, edgecolor='black', hatch='//')
    +
    +axs['bar2'].bar(x, y1, edgecolor='black', hatch=['--', '+', 'x', '\\'])
    +axs['bar2'].bar(x, y2, bottom=y1, edgecolor='black',
    +                hatch=['*', 'o', 'O', '.'])
    +
    +x = np.arange(0, 40, 0.2)
    +axs['patches'].fill_between(x, np.sin(x) * 4 + 30, y2=0,
    +                            hatch='///', zorder=2, fc='c')
    +axs['patches'].add_patch(Ellipse((4, 50), 10, 10, fill=True,
    +                                 hatch='*', facecolor='y'))
    +axs['patches'].add_patch(Polygon([(10, 20), (30, 50), (50, 10)],
    +                                 hatch='\\/...', facecolor='g'))
    +axs['patches'].set_xlim(0, 40)
    +axs['patches'].set_ylim(10, 60)
    +axs['patches'].set_aspect(1)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Ellipse`
    +#    - `matplotlib.patches.Polygon`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.patches.Patch.set_hatch`
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    diff --git a/galleries/examples/shapes_and_collections/hatch_style_reference.py b/galleries/examples/shapes_and_collections/hatch_style_reference.py
    new file mode 100644
    index 000000000000..58f3207cb9d8
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/hatch_style_reference.py
    @@ -0,0 +1,70 @@
    +"""
    +.. _hatch_def:
    +
    +=====================
    +Hatch style reference
    +=====================
    +
    +Hatches can be added to most polygons in Matplotlib, including `~.Axes.bar`,
    +`~.Axes.fill_between`, `~.Axes.contourf`, and children of `~.patches.Polygon`.
    +They are currently supported in the PS, PDF, SVG, macosx, and Agg backends. The WX
    +and Cairo backends do not currently support hatching.
    +
    +See also :doc:`/gallery/images_contours_and_fields/contourf_hatching` for
    +an example using `~.Axes.contourf`, and
    +:doc:`/gallery/shapes_and_collections/hatch_demo` for more usage examples.
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.patches import Rectangle
    +
    +fig, axs = plt.subplots(2, 5, layout='constrained', figsize=(6.4, 3.2))
    +
    +hatches = ['/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    +
    +
    +def hatches_plot(ax, h):
    +    ax.add_patch(Rectangle((0, 0), 2, 2, fill=False, hatch=h))
    +    ax.text(1, -0.5, f"' {h} '", size=15, ha="center")
    +    ax.axis('equal')
    +    ax.axis('off')
    +
    +for ax, h in zip(axs.flat, hatches):
    +    hatches_plot(ax, h)
    +
    +# %%
    +# Hatching patterns can be repeated to increase the density.
    +
    +fig, axs = plt.subplots(2, 5, layout='constrained', figsize=(6.4, 3.2))
    +
    +hatches = ['//', '\\\\', '||', '--', '++', 'xx', 'oo', 'OO', '..', '**']
    +
    +for ax, h in zip(axs.flat, hatches):
    +    hatches_plot(ax, h)
    +
    +# %%
    +# Hatching patterns can be combined to create additional patterns.
    +
    +fig, axs = plt.subplots(2, 5, layout='constrained', figsize=(6.4, 3.2))
    +
    +hatches = ['/o', '\\|', '|*', '-\\', '+o', 'x*', 'o-', 'O|', 'O.', '*-']
    +
    +for ax, h in zip(axs.flat, hatches):
    +    hatches_plot(ax, h)
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Rectangle`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.axes.Axes.text`
    +#
    +# .. tags::
    +#
    +#    purpose: reference
    diff --git a/galleries/examples/shapes_and_collections/hatchcolor_demo.py b/galleries/examples/shapes_and_collections/hatchcolor_demo.py
    new file mode 100644
    index 000000000000..7a51248ffc14
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/hatchcolor_demo.py
    @@ -0,0 +1,93 @@
    +"""
    +===============
    +Hatchcolor Demo
    +===============
    +
    +The color of the hatch can be set using the *hatchcolor* parameter. The following
    +examples show how to use the *hatchcolor* parameter to set the color of the hatch
    +in `~.patches.Patch` and `~.collections.Collection`.
    +
    +See also :doc:`/gallery/shapes_and_collections/hatch_demo` for more usage examples
    +of hatching.
    +
    +Patch Hatchcolor
    +----------------
    +
    +This example shows how to use the *hatchcolor* parameter to set the color of
    +the hatch in a rectangle and a bar plot. The *hatchcolor* parameter is available for
    +`~.patches.Patch`, child classes of Patch, and methods that pass through to Patch.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cm as cm
    +from matplotlib.patches import Rectangle
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +
    +# Rectangle with red hatch color and black edge color
    +ax1.add_patch(Rectangle((0.1, 0.5), 0.8, 0.3, hatch=".", hatchcolor='red',
    +                        edgecolor='black', lw=2))
    +# If hatchcolor is not passed, the hatch will match the edge color
    +ax1.add_patch(Rectangle((0.1, 0.1), 0.8, 0.3, hatch='x', edgecolor='orange', lw=2))
    +
    +x = np.arange(1, 5)
    +y = np.arange(1, 5)
    +
    +ax2.bar(x, y, facecolor='none', edgecolor='red', hatch='//', hatchcolor='blue')
    +ax2.set_xlim(0, 5)
    +ax2.set_ylim(0, 5)
    +
    +# %%
    +# Collection Hatchcolor
    +# ---------------------
    +#
    +# The following example shows how to use the *hatchcolor* parameter to set the color of
    +# the hatch in a scatter plot. The *hatchcolor* parameter can also be passed to
    +# `~.collections.Collection`, child classes of Collection, and methods that pass
    +# through to Collection.
    +
    +fig, ax = plt.subplots()
    +
    +num_points_x = 10
    +num_points_y = 9
    +x = np.linspace(0, 1, num_points_x)
    +y = np.linspace(0, 1, num_points_y)
    +
    +X, Y = np.meshgrid(x, y)
    +X[1::2, :] += (x[1] - x[0]) / 2  # stagger every alternate row
    +
    +# As ax.scatter (PathCollection) is drawn row by row, setting hatchcolors to the
    +# first row is enough, as the colors will be cycled through for the next rows.
    +colors = [cm.rainbow(val) for val in x]
    +
    +ax.scatter(
    +    X.ravel(),
    +    Y.ravel(),
    +    s=1700,
    +    facecolor="none",
    +    edgecolor="gray",
    +    linewidth=2,
    +    marker="h",  # Use hexagon as marker
    +    hatch="xxx",
    +    hatchcolor=colors,
    +)
    +ax.set_xlim(0, 1)
    +ax.set_ylim(0, 1)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Polygon`
    +#    - `matplotlib.axes.Axes.add_patch`
    +#    - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
    +#    - `matplotlib.collections`
    +#    - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter`
    diff --git a/galleries/examples/shapes_and_collections/line_collection.py b/galleries/examples/shapes_and_collections/line_collection.py
    new file mode 100644
    index 000000000000..d8b3fd655133
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/line_collection.py
    @@ -0,0 +1,70 @@
    +"""
    +==========================================
    +Plot multiple lines using a LineCollection
    +==========================================
    +
    +Matplotlib can efficiently draw multiple lines at once using a `~.LineCollection`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import LineCollection
    +
    +colors = ["indigo", "blue", "green", "yellow", "orange", "red"]
    +
    +# create a list of half-circles with varying radii
    +theta = np.linspace(0, np.pi, 36)
    +radii = np.linspace(4, 5, num=len(colors))
    +arcs = [np.column_stack([r * np.cos(theta), r * np.sin(theta)]) for r in radii]
    +
    +fig, ax = plt.subplots(figsize=(6.4, 3.2))
    +# set axes limits manually because Collections do not take part in autoscaling
    +ax.set_xlim(-6, 6)
    +ax.set_ylim(0, 6)
    +ax.set_aspect("equal")  # to make the arcs look circular
    +
    +# create a LineCollection with the half-circles
    +# its properties can be set per line by passing a sequence (here used for *colors*)
    +# or they can be set for all lines by passing a scalar (here used for *linewidths*)
    +line_collection = LineCollection(arcs, colors=colors, linewidths=4)
    +ax.add_collection(line_collection)
    +
    +plt.show()
    +
    +# %%
    +# Instead of passing a list of colors (``colors=colors``), we can alternatively use
    +# colormapping. The lines are then color-coded based on an additional array of values
    +# passed to the *array* parameter. In the below example, we color the lines based on
    +# their radius by passing ``array=radii``.
    +
    +num_arcs = 15
    +theta = np.linspace(0, np.pi, 36)
    +radii = np.linspace(4, 5.5, num=num_arcs)
    +arcs = [np.column_stack([r * np.cos(theta), r * np.sin(theta)]) for r in radii]
    +
    +fig, ax = plt.subplots(figsize=(6.4, 3))
    +# set axes limits manually because Collections do not take part in autoscaling
    +ax.set_xlim(-6, 6)
    +ax.set_ylim(0, 6)
    +ax.set_aspect("equal")  # to make the arcs look circular
    +
    +# create a LineCollection with the half-circles and color mapping
    +line_collection = LineCollection(arcs, array=radii, cmap="rainbow")
    +ax.add_collection(line_collection)
    +
    +fig.colorbar(line_collection, label="Radius")
    +ax.set_title("Line Collection with mapped colors")
    +
    +plt.show()
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.collections.LineCollection`
    +#    - `matplotlib.collections.Collection.set_array`
    +#    - `matplotlib.axes.Axes.add_collection`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    diff --git a/galleries/examples/shapes_and_collections/patch_collection.py b/galleries/examples/shapes_and_collections/patch_collection.py
    new file mode 100644
    index 000000000000..ca0dd8e1045d
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/patch_collection.py
    @@ -0,0 +1,77 @@
    +"""
    +============================
    +Circles, Wedges and Polygons
    +============================
    +
    +This example demonstrates how to use `.collections.PatchCollection`.
    +
    +See also :doc:`/gallery/shapes_and_collections/artist_reference`, which instead
    +adds each artist separately to its own Axes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import PatchCollection
    +from matplotlib.patches import Circle, Polygon, Wedge
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig, ax = plt.subplots()
    +
    +resolution = 50  # the number of vertices
    +N = 3
    +x = np.random.rand(N)
    +y = np.random.rand(N)
    +radii = 0.1*np.random.rand(N)
    +patches = []
    +for x1, y1, r in zip(x, y, radii):
    +    circle = Circle((x1, y1), r)
    +    patches.append(circle)
    +
    +x = np.random.rand(N)
    +y = np.random.rand(N)
    +radii = 0.1*np.random.rand(N)
    +theta1 = 360.0*np.random.rand(N)
    +theta2 = 360.0*np.random.rand(N)
    +for x1, y1, r, t1, t2 in zip(x, y, radii, theta1, theta2):
    +    wedge = Wedge((x1, y1), r, t1, t2)
    +    patches.append(wedge)
    +
    +# Some limiting conditions on Wedge
    +patches += [
    +    Wedge((.3, .7), .1, 0, 360),             # Full circle
    +    Wedge((.7, .8), .2, 0, 360, width=0.05),  # Full ring
    +    Wedge((.8, .3), .2, 0, 45),              # Full sector
    +    Wedge((.8, .3), .2, 45, 90, width=0.10),  # Ring sector
    +]
    +
    +for i in range(N):
    +    polygon = Polygon(np.random.rand(N, 2), closed=True)
    +    patches.append(polygon)
    +
    +colors = 100 * np.random.rand(len(patches))
    +p = PatchCollection(patches, alpha=0.4)
    +p.set_array(colors)
    +ax.add_collection(p)
    +fig.colorbar(p, ax=ax)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.patches.Wedge`
    +#    - `matplotlib.patches.Polygon`
    +#    - `matplotlib.collections.PatchCollection`
    +#    - `matplotlib.collections.Collection.set_array`
    +#    - `matplotlib.axes.Axes.add_collection`
    +#    - `matplotlib.figure.Figure.colorbar`
    diff --git a/galleries/examples/shapes_and_collections/path_patch.py b/galleries/examples/shapes_and_collections/path_patch.py
    new file mode 100644
    index 000000000000..13acd35ea861
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/path_patch.py
    @@ -0,0 +1,53 @@
    +r"""
    +================
    +PathPatch object
    +================
    +
    +This example shows how to create `~.path.Path` and `~.patches.PathPatch`
    +objects through Matplotlib's API.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.patches as mpatches
    +import matplotlib.path as mpath
    +
    +fig, ax = plt.subplots()
    +
    +Path = mpath.Path
    +path_data = [
    +    (Path.MOVETO, (1.58, -2.57)),
    +    (Path.CURVE4, (0.35, -1.1)),
    +    (Path.CURVE4, (-1.75, 2.0)),
    +    (Path.CURVE4, (0.375, 2.0)),
    +    (Path.LINETO, (0.85, 1.15)),
    +    (Path.CURVE4, (2.2, 3.2)),
    +    (Path.CURVE4, (3, 0.05)),
    +    (Path.CURVE4, (2.0, -0.5)),
    +    (Path.CLOSEPOLY, (1.58, -2.57)),
    +    ]
    +codes, verts = zip(*path_data)
    +path = mpath.Path(verts, codes)
    +patch = mpatches.PathPatch(path, facecolor='r', alpha=0.5)
    +ax.add_patch(patch)
    +
    +# plot control points and connecting lines
    +x, y = zip(*path.vertices)
    +line, = ax.plot(x, y, 'go-')
    +
    +ax.grid()
    +ax.axis('equal')
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.axes.Axes.add_patch`
    diff --git a/galleries/examples/shapes_and_collections/quad_bezier.py b/galleries/examples/shapes_and_collections/quad_bezier.py
    new file mode 100644
    index 000000000000..f4a688233ba9
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/quad_bezier.py
    @@ -0,0 +1,40 @@
    +"""
    +============
    +Bezier curve
    +============
    +
    +This example showcases the `~.patches.PathPatch` object to create a Bezier
    +polycurve path patch.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.patches as mpatches
    +import matplotlib.path as mpath
    +
    +Path = mpath.Path
    +
    +fig, ax = plt.subplots()
    +pp1 = mpatches.PathPatch(
    +    Path([(0, 0), (1, 0), (1, 1), (0, 0)],
    +         [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]),
    +    fc="none", transform=ax.transData)
    +
    +ax.add_patch(pp1)
    +ax.plot([0.75], [0.25], "ro")
    +ax.set_title('The red point should be on the path')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.PathPatch`
    +#    - `matplotlib.axes.Axes.add_patch`
    diff --git a/galleries/examples/shapes_and_collections/scatter.py b/galleries/examples/shapes_and_collections/scatter.py
    new file mode 100644
    index 000000000000..277eade77efd
    --- /dev/null
    +++ b/galleries/examples/shapes_and_collections/scatter.py
    @@ -0,0 +1,31 @@
    +"""
    +============
    +Scatter plot
    +============
    +
    +This example showcases a simple scatter plot.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +N = 50
    +x = np.random.rand(N)
    +y = np.random.rand(N)
    +colors = np.random.rand(N)
    +area = (30 * np.random.rand(N))**2  # 0 to 15 point radii
    +
    +plt.scatter(x, y, s=area, c=colors, alpha=0.5)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter`
    diff --git a/galleries/examples/showcase/README.txt b/galleries/examples/showcase/README.txt
    new file mode 100644
    index 000000000000..28028ad666a2
    --- /dev/null
    +++ b/galleries/examples/showcase/README.txt
    @@ -0,0 +1,4 @@
    +.. _showcase_examples:
    +
    +Showcase
    +========
    diff --git a/galleries/examples/showcase/anatomy.py b/galleries/examples/showcase/anatomy.py
    new file mode 100644
    index 000000000000..798e4204cad3
    --- /dev/null
    +++ b/galleries/examples/showcase/anatomy.py
    @@ -0,0 +1,121 @@
    +"""
    +===================
    +Anatomy of a figure
    +===================
    +
    +This figure shows the name of several matplotlib elements composing a figure
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Circle
    +from matplotlib.patheffects import withStroke
    +from matplotlib.ticker import AutoMinorLocator, MultipleLocator
    +
    +royal_blue = [0, 20/256, 82/256]
    +
    +
    +# make the figure
    +
    +np.random.seed(19680801)
    +
    +X = np.linspace(0.5, 3.5, 100)
    +Y1 = 3+np.cos(X)
    +Y2 = 1+np.cos(1+X/0.75)/2
    +Y3 = np.random.uniform(Y1, Y2, len(X))
    +
    +fig = plt.figure(figsize=(7.5, 7.5))
    +ax = fig.add_axes((0.2, 0.17, 0.68, 0.7), aspect=1)
    +
    +ax.xaxis.set_major_locator(MultipleLocator(1.000))
    +ax.xaxis.set_minor_locator(AutoMinorLocator(4))
    +ax.yaxis.set_major_locator(MultipleLocator(1.000))
    +ax.yaxis.set_minor_locator(AutoMinorLocator(4))
    +ax.xaxis.set_minor_formatter("{x:.2f}")
    +
    +ax.set_xlim(0, 4)
    +ax.set_ylim(0, 4)
    +
    +ax.tick_params(which='major', width=1.0, length=10, labelsize=14)
    +ax.tick_params(which='minor', width=1.0, length=5, labelsize=10,
    +               labelcolor='0.25')
    +
    +ax.grid(linestyle="--", linewidth=0.5, color='.25', zorder=-10)
    +
    +ax.plot(X, Y1, c='C0', lw=2.5, label="Blue signal", zorder=10)
    +ax.plot(X, Y2, c='C1', lw=2.5, label="Orange signal")
    +ax.plot(X[::3], Y3[::3], linewidth=0, markersize=9,
    +        marker='s', markerfacecolor='none', markeredgecolor='C4',
    +        markeredgewidth=2.5)
    +
    +ax.set_title("Anatomy of a figure", fontsize=20, verticalalignment='bottom')
    +ax.set_xlabel("x Axis label", fontsize=14)
    +ax.set_ylabel("y Axis label", fontsize=14)
    +ax.legend(loc="upper right", fontsize=14)
    +
    +
    +# Annotate the figure
    +
    +def annotate(x, y, text, code):
    +    # Circle marker
    +    c = Circle((x, y), radius=0.15, clip_on=False, zorder=10, linewidth=2.5,
    +               edgecolor=royal_blue + [0.6], facecolor='none',
    +               path_effects=[withStroke(linewidth=7, foreground='white')])
    +    ax.add_artist(c)
    +
    +    # use path_effects as a background for the texts
    +    # draw the path_effects and the colored text separately so that the
    +    # path_effects cannot clip other texts
    +    for path_effects in [[withStroke(linewidth=7, foreground='white')], []]:
    +        color = 'white' if path_effects else royal_blue
    +        ax.text(x, y-0.2, text, zorder=100,
    +                ha='center', va='top', weight='bold', color=color,
    +                style='italic', fontfamily='monospace',
    +                path_effects=path_effects)
    +
    +        color = 'white' if path_effects else 'black'
    +        ax.text(x, y-0.33, code, zorder=100,
    +                ha='center', va='top', weight='normal', color=color,
    +                fontfamily='monospace', fontsize='medium',
    +                path_effects=path_effects)
    +
    +
    +annotate(3.5, -0.13, "Minor tick label", "ax.xaxis.set_minor_formatter")
    +annotate(-0.03, 1.0, "Major tick", "ax.yaxis.set_major_locator")
    +annotate(0.00, 3.75, "Minor tick", "ax.yaxis.set_minor_locator")
    +annotate(-0.15, 3.00, "Major tick label", "ax.yaxis.set_major_formatter")
    +annotate(1.68, -0.39, "xlabel", "ax.set_xlabel")
    +annotate(-0.38, 1.67, "ylabel", "ax.set_ylabel")
    +annotate(1.52, 4.15, "Title", "ax.set_title")
    +annotate(1.75, 2.80, "Line", "ax.plot")
    +annotate(2.25, 1.54, "Markers", "ax.scatter")
    +annotate(3.00, 3.00, "Grid", "ax.grid")
    +annotate(3.60, 3.58, "Legend", "ax.legend")
    +annotate(2.5, 0.55, "Axes", "fig.subplots")
    +annotate(4, 4.5, "Figure", "plt.figure")
    +annotate(0.65, 0.01, "x Axis", "ax.xaxis")
    +annotate(0, 0.36, "y Axis", "ax.yaxis")
    +annotate(4.0, 0.7, "Spine", "ax.spines")
    +
    +# frame around figure
    +fig.patch.set(linewidth=4, edgecolor='0.5')
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.figure`
    +#    - `matplotlib.axes.Axes.text`
    +#    - `matplotlib.axis.Axis.set_minor_formatter`
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.axis.Axis.set_minor_locator`
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.patheffects.withStroke`
    +#    - `matplotlib.ticker.FuncFormatter`
    diff --git a/galleries/examples/showcase/firefox.py b/galleries/examples/showcase/firefox.py
    new file mode 100644
    index 000000000000..2026d253f6b6
    --- /dev/null
    +++ b/galleries/examples/showcase/firefox.py
    @@ -0,0 +1,61 @@
    +"""
    +=======
    +Firefox
    +=======
    +
    +This example shows how to create the Firefox logo with path and patches.
    +"""
    +
    +import re
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.patches as patches
    +from matplotlib.path import Path
    +
    +# From: https://dmitrybaranovskiy.github.io/raphael/icons/#firefox
    +firefox = "M28.4,22.469c0.479-0.964,0.851-1.991,1.095-3.066c0.953-3.661,0.666-6.854,0.666-6.854l-0.327,2.104c0,0-0.469-3.896-1.044-5.353c-0.881-2.231-1.273-2.214-1.274-2.21c0.542,1.379,0.494,2.169,0.483,2.288c-0.01-0.016-0.019-0.032-0.027-0.047c-0.131-0.324-0.797-1.819-2.225-2.878c-2.502-2.481-5.943-4.014-9.745-4.015c-4.056,0-7.705,1.745-10.238,4.525C5.444,6.5,5.183,5.938,5.159,5.317c0,0-0.002,0.002-0.006,0.005c0-0.011-0.003-0.021-0.003-0.031c0,0-1.61,1.247-1.436,4.612c-0.299,0.574-0.56,1.172-0.777,1.791c-0.375,0.817-0.75,2.004-1.059,3.746c0,0,0.133-0.422,0.399-0.988c-0.064,0.482-0.103,0.971-0.116,1.467c-0.09,0.845-0.118,1.865-0.039,3.088c0,0,0.032-0.406,0.136-1.021c0.834,6.854,6.667,12.165,13.743,12.165l0,0c1.86,0,3.636-0.37,5.256-1.036C24.938,27.771,27.116,25.196,28.4,22.469zM16.002,3.356c2.446,0,4.73,0.68,6.68,1.86c-2.274-0.528-3.433-0.261-3.423-0.248c0.013,0.015,3.384,0.589,3.981,1.411c0,0-1.431,0-2.856,0.41c-0.065,0.019,5.242,0.663,6.327,5.966c0,0-0.582-1.213-1.301-1.42c0.473,1.439,0.351,4.17-0.1,5.528c-0.058,0.174-0.118-0.755-1.004-1.155c0.284,2.037-0.018,5.268-1.432,6.158c-0.109,0.07,0.887-3.189,0.201-1.93c-4.093,6.276-8.959,2.539-10.934,1.208c1.585,0.388,3.267,0.108,4.242-0.559c0.982-0.672,1.564-1.162,2.087-1.047c0.522,0.117,0.87-0.407,0.464-0.872c-0.405-0.466-1.392-1.105-2.725-0.757c-0.94,0.247-2.107,1.287-3.886,0.233c-1.518-0.899-1.507-1.63-1.507-2.095c0-0.366,0.257-0.88,0.734-1.028c0.58,0.062,1.044,0.214,1.537,0.466c0.005-0.135,0.006-0.315-0.001-0.519c0.039-0.077,0.015-0.311-0.047-0.596c-0.036-0.287-0.097-0.582-0.19-0.851c0.01-0.002,0.017-0.007,0.021-0.021c0.076-0.344,2.147-1.544,2.299-1.659c0.153-0.114,0.55-0.378,0.506-1.183c-0.015-0.265-0.058-0.294-2.232-0.286c-0.917,0.003-1.425-0.894-1.589-1.245c0.222-1.231,0.863-2.11,1.919-2.704c0.02-0.011,0.015-0.021-0.008-0.027c0.219-0.127-2.524-0.006-3.76,1.604C9.674,8.045,9.219,7.95,8.71,7.95c-0.638,0-1.139,0.07-1.603,0.187c-0.05,0.013-0.122,0.011-0.208-0.001C6.769,8.04,6.575,7.88,6.365,7.672c0.161-0.18,0.324-0.356,0.495-0.526C9.201,4.804,12.43,3.357,16.002,3.356z"  # noqa
    +
    +
    +def svg_parse(path):
    +    commands = {'M': (Path.MOVETO,),
    +                'L': (Path.LINETO,),
    +                'Q': (Path.CURVE3,)*2,
    +                'C': (Path.CURVE4,)*3,
    +                'Z': (Path.CLOSEPOLY,)}
    +    vertices = []
    +    codes = []
    +    cmd_values = re.split("([A-Za-z])", path)[1:]  # Split over commands.
    +    for cmd, values in zip(cmd_values[::2], cmd_values[1::2]):
    +        # Numbers are separated either by commas, or by +/- signs (but not at
    +        # the beginning of the string).
    +        points = ([*map(float, re.split(",|(?'), xytext=(15, -10))
    +
    +    ax.plot(data)
    +
    +    ax.set_xlabel('time')
    +    ax.set_ylabel('my overall health')
    +    fig.text(
    +        0.5, 0.05,
    +        '"Stove Ownership" from xkcd by Randall Munroe',
    +        ha='center')
    +
    +# %%
    +
    +with plt.xkcd():
    +    # Based on "The Data So Far" from XKCD by Randall Munroe
    +    # https://xkcd.com/373/
    +
    +    fig = plt.figure()
    +    ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
    +    ax.bar([0, 1], [0, 100], 0.25)
    +    ax.spines[['top', 'right']].set_visible(False)
    +    ax.xaxis.set_ticks_position('bottom')
    +    ax.set_xticks([0, 1])
    +    ax.set_xticklabels(['CONFIRMED BY\nEXPERIMENT', 'REFUTED BY\nEXPERIMENT'])
    +    ax.set_xlim(-0.5, 1.5)
    +    ax.set_yticks([])
    +    ax.set_ylim(0, 110)
    +
    +    ax.set_title("CLAIMS OF SUPERNATURAL POWERS")
    +
    +    fig.text(
    +        0.5, 0.05,
    +        '"The Data So Far" from xkcd by Randall Munroe',
    +        ha='center')
    +
    +plt.show()
    +
    +#
    +# %%
    +# .. tags::
    +#
    +#    plot-type: line
    +#    plot-type: bar
    +#    purpose: fun
    diff --git a/galleries/examples/specialty_plots/README.txt b/galleries/examples/specialty_plots/README.txt
    new file mode 100644
    index 000000000000..fc1bf44d3b92
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/README.txt
    @@ -0,0 +1,4 @@
    +.. _specialty_plots_examples:
    +
    +Specialty plots
    +===============
    diff --git a/examples/specialty_plots/advanced_hillshading.py b/galleries/examples/specialty_plots/advanced_hillshading.py
    similarity index 87%
    rename from examples/specialty_plots/advanced_hillshading.py
    rename to galleries/examples/specialty_plots/advanced_hillshading.py
    index 34ff41be71d3..a542fe409359 100644
    --- a/examples/specialty_plots/advanced_hillshading.py
    +++ b/galleries/examples/specialty_plots/advanced_hillshading.py
    @@ -1,8 +1,13 @@
     """
    +===========
    +Hillshading
    +===========
    +
     Demonstrates a few common tricks with shaded plots.
     """
    -import numpy as np
     import matplotlib.pyplot as plt
    +import numpy as np
    +
     from matplotlib.colors import LightSource, Normalize
     
     
    @@ -11,7 +16,7 @@ def display_colorbar():
         y, x = np.mgrid[-4:2:200j, -4:2:200j]
         z = 10 * np.cos(x**2 + y**2)
     
    -    cmap = plt.cm.copper
    +    cmap = plt.colormaps["copper"]
         ls = LightSource(315, 45)
         rgb = ls.shade(z, cmap)
     
    @@ -21,7 +26,7 @@ def display_colorbar():
         # Use a proxy artist for the colorbar...
         im = ax.imshow(z, cmap=cmap)
         im.remove()
    -    fig.colorbar(im)
    +    fig.colorbar(im, ax=ax)
     
         ax.set_title('Using a colorbar with a shaded plot', size='x-large')
     
    @@ -38,11 +43,11 @@ def avoid_outliers():
         ls = LightSource(315, 45)
         fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4.5))
     
    -    rgb = ls.shade(z, plt.cm.copper)
    +    rgb = ls.shade(z, plt.colormaps["copper"])
         ax1.imshow(rgb, interpolation='bilinear')
         ax1.set_title('Full range of data')
     
    -    rgb = ls.shade(z, plt.cm.copper, vmin=-10, vmax=10)
    +    rgb = ls.shade(z, plt.colormaps["copper"], vmin=-10, vmax=10)
         ax2.imshow(rgb, interpolation='bilinear')
         ax2.set_title('Manually set range')
     
    @@ -56,7 +61,7 @@ def shade_other_data():
         z2 = np.cos(x**2 + y**2)  # Data to color
     
         norm = Normalize(z2.min(), z2.max())
    -    cmap = plt.cm.RdBu
    +    cmap = plt.colormaps["RdBu"]
     
         ls = LightSource(315, 45)
         rgb = ls.shade_rgb(cmap(norm(z2)), z1)
    diff --git a/galleries/examples/specialty_plots/anscombe.py b/galleries/examples/specialty_plots/anscombe.py
    new file mode 100644
    index 000000000000..333eb6ef42c2
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/anscombe.py
    @@ -0,0 +1,65 @@
    +"""
    +==================
    +Anscombe's quartet
    +==================
    +
    +`Anscombe's quartet`_ is a group of datasets (x, y) that have the same mean,
    +standard deviation, and regression line, but which are qualitatively different.
    +
    +It is often used to illustrate the importance of looking at a set of data
    +graphically and not only relying on basic statistic properties.
    +
    +.. _Anscombe's quartet: https://en.wikipedia.org/wiki/Anscombe%27s_quartet
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]
    +y1 = [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]
    +y2 = [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]
    +y3 = [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]
    +x4 = [8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8]
    +y4 = [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89]
    +
    +datasets = {
    +    'I': (x, y1),
    +    'II': (x, y2),
    +    'III': (x, y3),
    +    'IV': (x4, y4)
    +}
    +
    +fig, axs = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(6, 6),
    +                        gridspec_kw={'wspace': 0.08, 'hspace': 0.08})
    +axs[0, 0].set(xlim=(0, 20), ylim=(2, 14))
    +axs[0, 0].set(xticks=(0, 10, 20), yticks=(4, 8, 12))
    +
    +for ax, (label, (x, y)) in zip(axs.flat, datasets.items()):
    +    ax.text(0.1, 0.9, label, fontsize=20, transform=ax.transAxes, va='top')
    +    ax.tick_params(direction='in', top=True, right=True)
    +    ax.plot(x, y, 'o')
    +
    +    # linear regression
    +    p1, p0 = np.polyfit(x, y, deg=1)  # slope, intercept
    +    ax.axline(xy1=(0, p0), slope=p1, color='r', lw=2)
    +
    +    # add text box for the statistics
    +    stats = (f'$\\mu$ = {np.mean(y):.2f}\n'
    +             f'$\\sigma$ = {np.std(y):.2f}\n'
    +             f'$r$ = {np.corrcoef(x, y)[0][1]:.2f}')
    +    bbox = dict(boxstyle='round', fc='blanchedalmond', ec='orange', alpha=0.5)
    +    ax.text(0.95, 0.07, stats, fontsize=9, bbox=bbox,
    +            transform=ax.transAxes, horizontalalignment='right')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.axline` / `matplotlib.pyplot.axline`
    +#    - `matplotlib.axes.Axes.text` / `matplotlib.pyplot.text`
    +#    - `matplotlib.axes.Axes.tick_params` / matplotlib.pyplot.tick_params`
    diff --git a/examples/specialty_plots/hinton_demo.py b/galleries/examples/specialty_plots/hinton_demo.py
    similarity index 82%
    rename from examples/specialty_plots/hinton_demo.py
    rename to galleries/examples/specialty_plots/hinton_demo.py
    index e022c079393d..c47b79fb3acb 100644
    --- a/examples/specialty_plots/hinton_demo.py
    +++ b/galleries/examples/specialty_plots/hinton_demo.py
    @@ -1,5 +1,7 @@
     """
    -Demo of a function to create Hinton diagrams.
    +===============
    +Hinton diagrams
    +===============
     
     Hinton diagrams are useful for visualizing the values of a 2D array (e.g.
     a weight matrix): Positive and negative values are represented by white and
    @@ -8,8 +10,8 @@
     
     Initial idea from David Warde-Farley on the SciPy Cookbook
     """
    -import numpy as np
     import matplotlib.pyplot as plt
    +import numpy as np
     
     
     def hinton(matrix, max_weight=None, ax=None):
    @@ -17,7 +19,7 @@ def hinton(matrix, max_weight=None, ax=None):
         ax = ax if ax is not None else plt.gca()
     
         if not max_weight:
    -        max_weight = 2 ** np.ceil(np.log(np.abs(matrix).max()) / np.log(2))
    +        max_weight = 2 ** np.ceil(np.log2(np.abs(matrix).max()))
     
         ax.patch.set_facecolor('gray')
         ax.set_aspect('equal', 'box')
    @@ -26,7 +28,7 @@ def hinton(matrix, max_weight=None, ax=None):
     
         for (x, y), w in np.ndenumerate(matrix):
             color = 'white' if w > 0 else 'black'
    -        size = np.sqrt(np.abs(w) / max_weight)
    +        size = np.sqrt(abs(w) / max_weight)
             rect = plt.Rectangle([x - size / 2, y - size / 2], size, size,
                                  facecolor=color, edgecolor=color)
             ax.add_patch(rect)
    @@ -36,5 +38,8 @@ def hinton(matrix, max_weight=None, ax=None):
     
     
     if __name__ == '__main__':
    +    # Fixing random state for reproducibility
    +    np.random.seed(19680801)
    +
         hinton(np.random.rand(20, 20) - 0.5)
         plt.show()
    diff --git a/galleries/examples/specialty_plots/ishikawa_diagram.py b/galleries/examples/specialty_plots/ishikawa_diagram.py
    new file mode 100644
    index 000000000000..072d7b463c00
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/ishikawa_diagram.py
    @@ -0,0 +1,196 @@
    +"""
    +================
    +Ishikawa Diagram
    +================
    +
    +Ishikawa Diagrams, fishbone diagrams, herringbone diagrams, or cause-and-effect
    +diagrams are used to identify problems in a system by showing how causes and
    +effects are linked.
    +Source: https://en.wikipedia.org/wiki/Ishikawa_diagram
    +
    +"""
    +import math
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.patches import Polygon, Wedge
    +
    +fig, ax = plt.subplots(figsize=(10, 6), layout='constrained')
    +ax.set_xlim(-5, 5)
    +ax.set_ylim(-5, 5)
    +ax.axis('off')
    +
    +
    +def problems(data: str,
    +             problem_x: float, problem_y: float,
    +             angle_x: float, angle_y: float):
    +    """
    +    Draw each problem section of the Ishikawa plot.
    +
    +    Parameters
    +    ----------
    +    data : str
    +        The name of the problem category.
    +    problem_x, problem_y : float, optional
    +        The `X` and `Y` positions of the problem arrows (`Y` defaults to zero).
    +    angle_x, angle_y : float, optional
    +        The angle of the problem annotations. They are always angled towards
    +        the tail of the plot.
    +
    +    Returns
    +    -------
    +    None.
    +
    +    """
    +    ax.annotate(str.upper(data), xy=(problem_x, problem_y),
    +                xytext=(angle_x, angle_y),
    +                fontsize=10,
    +                color='white',
    +                weight='bold',
    +                xycoords='data',
    +                verticalalignment='center',
    +                horizontalalignment='center',
    +                textcoords='offset fontsize',
    +                arrowprops=dict(arrowstyle="->", facecolor='black'),
    +                bbox=dict(boxstyle='square',
    +                          facecolor='tab:blue',
    +                          pad=0.8))
    +
    +
    +def causes(data: list,
    +           cause_x: float, cause_y: float,
    +           cause_xytext=(-9, -0.3), top: bool = True):
    +    """
    +    Place each cause to a position relative to the problems
    +    annotations.
    +
    +    Parameters
    +    ----------
    +    data : indexable object
    +        The input data. IndexError is
    +        raised if more than six arguments are passed.
    +    cause_x, cause_y : float
    +        The `X` and `Y` position of the cause annotations.
    +    cause_xytext : tuple, optional
    +        Adjust to set the distance of the cause text from the problem
    +        arrow in fontsize units.
    +    top : bool, default: True
    +        Determines whether the next cause annotation will be
    +        plotted above or below the previous one.
    +
    +    Returns
    +    -------
    +    None.
    +
    +    """
    +    for index, cause in enumerate(data):
    +        # [, ]
    +        coords = [[0.02, 0],
    +                  [0.23, 0.5],
    +                  [-0.46, -1],
    +                  [0.69, 1.5],
    +                  [-0.92, -2],
    +                  [1.15, 2.5]]
    +
    +        # First 'cause' annotation is placed in the middle of the 'problems' arrow
    +        # and each subsequent cause is plotted above or below it in succession.
    +        cause_x -= coords[index][0]
    +        cause_y += coords[index][1] if top else -coords[index][1]
    +
    +        ax.annotate(cause, xy=(cause_x, cause_y),
    +                    horizontalalignment='center',
    +                    xytext=cause_xytext,
    +                    fontsize=9,
    +                    xycoords='data',
    +                    textcoords='offset fontsize',
    +                    arrowprops=dict(arrowstyle="->",
    +                                    facecolor='black'))
    +
    +
    +def draw_body(data: dict):
    +    """
    +    Place each problem section in its correct place by changing
    +    the coordinates on each loop.
    +
    +    Parameters
    +    ----------
    +    data : dict
    +        The input data (can be a dict of lists or tuples). ValueError
    +        is raised if more than six arguments are passed.
    +
    +    Returns
    +    -------
    +    None.
    +
    +    """
    +    # Set the length of the spine according to the number of 'problem' categories.
    +    length = (math.ceil(len(data) / 2)) - 1
    +    draw_spine(-2 - length, 2 + length)
    +
    +    # Change the coordinates of the 'problem' annotations after each one is rendered.
    +    offset = 0
    +    prob_section = [1.55, 0.8]
    +    for index, problem in enumerate(data.values()):
    +        plot_above = index % 2 == 0
    +        cause_arrow_y = 1.7 if plot_above else -1.7
    +        y_prob_angle = 16 if plot_above else -16
    +
    +        # Plot each section in pairs along the main spine.
    +        prob_arrow_x = prob_section[0] + length + offset
    +        cause_arrow_x = prob_section[1] + length + offset
    +        if not plot_above:
    +            offset -= 2.5
    +        if index > 5:
    +            raise ValueError(f'Maximum number of problems is 6, you have entered '
    +                             f'{len(data)}')
    +
    +        problems(list(data.keys())[index], prob_arrow_x, 0, -12, y_prob_angle)
    +        causes(problem, cause_arrow_x, cause_arrow_y, top=plot_above)
    +
    +
    +def draw_spine(xmin: int, xmax: int):
    +    """
    +    Draw main spine, head and tail.
    +
    +    Parameters
    +    ----------
    +    xmin : int
    +        The default position of the head of the spine's
    +        x-coordinate.
    +    xmax : int
    +        The default position of the tail of the spine's
    +        x-coordinate.
    +
    +    Returns
    +    -------
    +    None.
    +
    +    """
    +    # draw main spine
    +    ax.plot([xmin - 0.1, xmax], [0, 0], color='tab:blue', linewidth=2)
    +    # draw fish head
    +    ax.text(xmax + 0.1, - 0.05, 'PROBLEM', fontsize=10,
    +            weight='bold', color='white')
    +    semicircle = Wedge((xmax, 0), 1, 270, 90, fc='tab:blue')
    +    ax.add_patch(semicircle)
    +    # draw fish tail
    +    tail_pos = [[xmin - 0.8, 0.8], [xmin - 0.8, -0.8], [xmin, -0.01]]
    +    triangle = Polygon(tail_pos, fc='tab:blue')
    +    ax.add_patch(triangle)
    +
    +
    +# Input data
    +categories = {
    +    'Method': ['Time consumption', 'Cost', 'Procedures', 'Inefficient process',
    +               'Sampling'],
    +    'Machine': ['Faulty equipment', 'Compatibility'],
    +    'Material': ['Poor-quality input', 'Raw materials', 'Supplier',
    +                 'Shortage'],
    +    'Measurement': ['Calibration', 'Performance', 'Wrong measurements'],
    +    'Environment': ['Bad conditions'],
    +    'People': ['Lack of training', 'Managers', 'Labor shortage',
    +               'Procedures', 'Sales strategy']
    +}
    +
    +draw_body(categories)
    +plt.show()
    diff --git a/galleries/examples/specialty_plots/leftventricle_bullseye.py b/galleries/examples/specialty_plots/leftventricle_bullseye.py
    new file mode 100644
    index 000000000000..285fcdaecc5e
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/leftventricle_bullseye.py
    @@ -0,0 +1,153 @@
    +"""
    +=======================
    +Left ventricle bullseye
    +=======================
    +
    +This example demonstrates how to create the 17 segment model for the left
    +ventricle recommended by the American Heart Association (AHA).
    +
    +.. redirect-from:: /gallery/specialty_plots/leftventricle_bulleye
    +
    +See also the :doc:`/gallery/pie_and_polar_charts/nested_pie` example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib as mpl
    +
    +
    +def bullseye_plot(ax, data, seg_bold=None, cmap="viridis", norm=None):
    +    """
    +    Bullseye representation for the left ventricle.
    +
    +    Parameters
    +    ----------
    +    ax : Axes
    +    data : list[float]
    +        The intensity values for each of the 17 segments.
    +    seg_bold : list[int], optional
    +        A list with the segments to highlight.
    +    cmap : colormap, default: "viridis"
    +        Colormap for the data.
    +    norm : Normalize or None, optional
    +        Normalizer for the data.
    +
    +    Notes
    +    -----
    +    This function creates the 17 segment model for the left ventricle according
    +    to the American Heart Association (AHA) [1]_
    +
    +    References
    +    ----------
    +    .. [1] M. D. Cerqueira, N. J. Weissman, V. Dilsizian, A. K. Jacobs,
    +        S. Kaul, W. K. Laskey, D. J. Pennell, J. A. Rumberger, T. Ryan,
    +        and M. S. Verani, "Standardized myocardial segmentation and
    +        nomenclature for tomographic imaging of the heart",
    +        Circulation, vol. 105, no. 4, pp. 539-542, 2002.
    +    """
    +
    +    data = np.ravel(data)
    +    if seg_bold is None:
    +        seg_bold = []
    +    if norm is None:
    +        norm = mpl.colors.Normalize(vmin=data.min(), vmax=data.max())
    +
    +    r = np.linspace(0.2, 1, 4)
    +
    +    ax.set(ylim=(0, 1), xticklabels=[], yticklabels=[])
    +    ax.grid(False)  # Remove grid
    +
    +    # Fill segments 1-6, 7-12, 13-16.
    +    for start, stop, r_in, r_out in [
    +            (0, 6, r[2], r[3]),
    +            (6, 12, r[1], r[2]),
    +            (12, 16, r[0], r[1]),
    +            (16, 17, 0, r[0]),
    +    ]:
    +        n = stop - start
    +        dtheta = 2*np.pi / n
    +        ax.bar(np.arange(n) * dtheta + np.pi/2, r_out - r_in, dtheta, r_in,
    +               color=cmap(norm(data[start:stop])))
    +
    +    # Now, draw the segment borders.  In order for the outer bold borders not
    +    # to be covered by inner segments, the borders are all drawn separately
    +    # after the segments have all been filled.  We also disable clipping, which
    +    # would otherwise affect the outermost segment edges.
    +    # Draw edges of segments 1-6, 7-12, 13-16.
    +    for start, stop, r_in, r_out in [
    +            (0, 6, r[2], r[3]),
    +            (6, 12, r[1], r[2]),
    +            (12, 16, r[0], r[1]),
    +    ]:
    +        n = stop - start
    +        dtheta = 2*np.pi / n
    +        ax.bar(np.arange(n) * dtheta + np.pi/2, r_out - r_in, dtheta, r_in,
    +               clip_on=False, color="none", edgecolor="k", linewidth=[
    +                   4 if i + 1 in seg_bold else 2 for i in range(start, stop)])
    +    # Draw edge of segment 17 -- here; the edge needs to be drawn differently,
    +    # using plot().
    +    ax.plot(np.linspace(0, 2*np.pi), np.linspace(r[0], r[0]), "k",
    +            linewidth=(4 if 17 in seg_bold else 2))
    +
    +
    +# Create the fake data
    +data = np.arange(17) + 1
    +
    +
    +# Make a figure and Axes with dimensions as desired.
    +fig = plt.figure(figsize=(10, 5), layout="constrained")
    +fig.get_layout_engine().set(wspace=.1, w_pad=.2)
    +axs = fig.subplots(1, 3, subplot_kw=dict(projection='polar'))
    +fig.canvas.manager.set_window_title('Left Ventricle Bulls Eyes (AHA)')
    +
    +
    +# Set the colormap and norm to correspond to the data for which
    +# the colorbar will be used.
    +cmap = mpl.colormaps["viridis"]
    +norm = mpl.colors.Normalize(vmin=1, vmax=17)
    +# Create an empty ScalarMappable to set the colorbar's colormap and norm.
    +# The following gives a basic continuous colorbar with ticks and labels.
    +fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
    +             cax=axs[0].inset_axes([0, -.15, 1, .1]),
    +             orientation='horizontal', label='Some units')
    +
    +
    +# And again for the second colorbar.
    +cmap2 = mpl.colormaps["cool"]
    +norm2 = mpl.colors.Normalize(vmin=1, vmax=17)
    +fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap2, norm=norm2),
    +             cax=axs[1].inset_axes([0, -.15, 1, .1]),
    +             orientation='horizontal', label='Some other units')
    +
    +
    +# The second example illustrates the use of a ListedColormap, a
    +# BoundaryNorm, and extended ends to show the "over" and "under"
    +# value colors.
    +cmap3 = mpl.colors.ListedColormap(['r', 'g', 'b', 'c'], over='0.35', under='0.75')
    +# If a ListedColormap is used, the length of the bounds array must be
    +# one greater than the length of the color list.  The bounds must be
    +# monotonically increasing.
    +bounds = [2, 3, 7, 9, 15]
    +norm3 = mpl.colors.BoundaryNorm(bounds, cmap3.N)
    +fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap3, norm=norm3),
    +             cax=axs[2].inset_axes([0, -.15, 1, .1]),
    +             extend='both',
    +             ticks=bounds,  # optional
    +             spacing='proportional',
    +             orientation='horizontal',
    +             label='Discrete intervals, some other units')
    +
    +
    +# Create the 17 segment model
    +bullseye_plot(axs[0], data, cmap=cmap, norm=norm)
    +axs[0].set_title('Bulls Eye (AHA)')
    +
    +bullseye_plot(axs[1], data, cmap=cmap2, norm=norm2)
    +axs[1].set_title('Bulls Eye (AHA)')
    +
    +bullseye_plot(axs[2], data, seg_bold=[3, 5, 6, 11, 12, 16],
    +              cmap=cmap3, norm=norm3)
    +axs[2].set_title('Segments [3, 5, 6, 11, 12, 16] in bold')
    +
    +plt.show()
    diff --git a/galleries/examples/specialty_plots/mri_with_eeg.py b/galleries/examples/specialty_plots/mri_with_eeg.py
    new file mode 100644
    index 000000000000..8197250ddbe6
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/mri_with_eeg.py
    @@ -0,0 +1,57 @@
    +"""
    +============
    +MRI with EEG
    +============
    +
    +Displays a set of subplots with an MRI image, its intensity
    +histogram and some EEG traces.
    +
    +.. redirect-from:: /gallery/specialty_plots/mri_demo
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +
    +fig, axd = plt.subplot_mosaic(
    +    [["image", "density"],
    +     ["EEG", "EEG"]],
    +    layout="constrained",
    +    # "image" will contain a square image. We fine-tune the width so that
    +    # there is no excess horizontal or vertical margin around the image.
    +    width_ratios=[1.05, 2],
    +)
    +
    +# Load the MRI data (256x256 16-bit integers)
    +with cbook.get_sample_data('s1045.ima.gz') as dfile:
    +    im = np.frombuffer(dfile.read(), np.uint16).reshape((256, 256))
    +
    +# Plot the MRI image
    +axd["image"].imshow(im, cmap="gray")
    +axd["image"].axis('off')
    +
    +# Plot the histogram of MRI intensity
    +im = im[im.nonzero()]  # Ignore the background
    +axd["density"].hist(im, bins=np.arange(0, 2**16+1, 512))
    +axd["density"].set(xlabel='Intensity (a.u.)', xlim=(0, 2**16),
    +                   ylabel='MRI density', yticks=[])
    +axd["density"].minorticks_on()
    +
    +# Load the EEG data
    +n_samples, n_rows = 800, 4
    +with cbook.get_sample_data('eeg.dat') as eegfile:
    +    data = np.fromfile(eegfile, dtype=float).reshape((n_samples, n_rows))
    +t = 10 * np.arange(n_samples) / n_samples
    +
    +# Plot the EEG
    +axd["EEG"].set_xlabel('Time (s)')
    +axd["EEG"].set_xlim(0, 10)
    +dy = (data.min() - data.max()) * 0.7  # Crowd them a bit.
    +axd["EEG"].set_ylim(-dy, n_rows * dy)
    +axd["EEG"].set_yticks([0, dy, 2*dy, 3*dy], labels=['PG3', 'PG5', 'PG7', 'PG9'])
    +
    +for i, data_col in enumerate(data.T):
    +    axd["EEG"].plot(t, data_col + i*dy, color="C0")
    +
    +plt.show()
    diff --git a/galleries/examples/specialty_plots/radar_chart.py b/galleries/examples/specialty_plots/radar_chart.py
    new file mode 100644
    index 000000000000..a2f6df717544
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/radar_chart.py
    @@ -0,0 +1,216 @@
    +"""
    +======================================
    +Radar chart (aka spider or star chart)
    +======================================
    +
    +This example creates a radar chart, also known as a spider or star chart [1]_.
    +
    +Although this example allows a frame of either 'circle' or 'polygon', polygon
    +frames don't have proper gridlines (the lines are circles instead of polygons).
    +It's possible to get a polygon grid by setting GRIDLINE_INTERPOLATION_STEPS in
    +`matplotlib.axis` to the desired number of vertices, but the orientation of the
    +polygon is not aligned with the radial axis.
    +
    +.. [1] https://en.wikipedia.org/wiki/Radar_chart
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Circle, RegularPolygon
    +from matplotlib.path import Path
    +from matplotlib.projections import register_projection
    +from matplotlib.projections.polar import PolarAxes
    +from matplotlib.spines import Spine
    +from matplotlib.transforms import Affine2D
    +
    +
    +def radar_factory(num_vars, frame='circle'):
    +    """
    +    Create a radar chart with `num_vars` Axes.
    +
    +    This function creates a RadarAxes projection and registers it.
    +
    +    Parameters
    +    ----------
    +    num_vars : int
    +        Number of variables for radar chart.
    +    frame : {'circle', 'polygon'}
    +        Shape of frame surrounding Axes.
    +
    +    """
    +    # calculate evenly-spaced axis angles
    +    theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)
    +
    +    class RadarTransform(PolarAxes.PolarTransform):
    +
    +        def transform_path_non_affine(self, path):
    +            # Paths with non-unit interpolation steps correspond to gridlines,
    +            # in which case we force interpolation (to defeat PolarTransform's
    +            # autoconversion to circular arcs).
    +            if path._interpolation_steps > 1:
    +                path = path.interpolated(num_vars)
    +            return Path(self.transform(path.vertices), path.codes)
    +
    +    class RadarAxes(PolarAxes):
    +
    +        name = 'radar'
    +        PolarTransform = RadarTransform
    +
    +        def __init__(self, *args, **kwargs):
    +            super().__init__(*args, **kwargs)
    +            # rotate plot such that the first axis is at the top
    +            self.set_theta_zero_location('N')
    +
    +        def fill(self, *args, closed=True, **kwargs):
    +            """Override fill so that line is closed by default"""
    +            return super().fill(closed=closed, *args, **kwargs)
    +
    +        def plot(self, *args, **kwargs):
    +            """Override plot so that line is closed by default"""
    +            lines = super().plot(*args, **kwargs)
    +            for line in lines:
    +                self._close_line(line)
    +
    +        def _close_line(self, line):
    +            x, y = line.get_data()
    +            # FIXME: markers at x[0], y[0] get doubled-up
    +            if x[0] != x[-1]:
    +                x = np.append(x, x[0])
    +                y = np.append(y, y[0])
    +                line.set_data(x, y)
    +
    +        def set_varlabels(self, labels):
    +            self.set_thetagrids(np.degrees(theta), labels)
    +
    +        def _gen_axes_patch(self):
    +            # The Axes patch must be centered at (0.5, 0.5) and of radius 0.5
    +            # in axes coordinates.
    +            if frame == 'circle':
    +                return Circle((0.5, 0.5), 0.5)
    +            elif frame == 'polygon':
    +                return RegularPolygon((0.5, 0.5), num_vars,
    +                                      radius=.5, edgecolor="k")
    +            else:
    +                raise ValueError("Unknown value for 'frame': %s" % frame)
    +
    +        def _gen_axes_spines(self):
    +            if frame == 'circle':
    +                return super()._gen_axes_spines()
    +            elif frame == 'polygon':
    +                # spine_type must be 'left'/'right'/'top'/'bottom'/'circle'.
    +                spine = Spine(axes=self,
    +                              spine_type='circle',
    +                              path=Path.unit_regular_polygon(num_vars))
    +                # unit_regular_polygon gives a polygon of radius 1 centered at
    +                # (0, 0) but we want a polygon of radius 0.5 centered at (0.5,
    +                # 0.5) in axes coordinates.
    +                spine.set_transform(Affine2D().scale(.5).translate(.5, .5)
    +                                    + self.transAxes)
    +                return {'polar': spine}
    +            else:
    +                raise ValueError("Unknown value for 'frame': %s" % frame)
    +
    +    register_projection(RadarAxes)
    +    return theta
    +
    +
    +def example_data():
    +    # The following data is from the Denver Aerosol Sources and Health study.
    +    # See doi:10.1016/j.atmosenv.2008.12.017
    +    #
    +    # The data are pollution source profile estimates for five modeled
    +    # pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical
    +    # species. The radar charts are experimented with here to see if we can
    +    # nicely visualize how the modeled source profiles change across four
    +    # scenarios:
    +    #  1) No gas-phase species present, just seven particulate counts on
    +    #     Sulfate
    +    #     Nitrate
    +    #     Elemental Carbon (EC)
    +    #     Organic Carbon fraction 1 (OC)
    +    #     Organic Carbon fraction 2 (OC2)
    +    #     Organic Carbon fraction 3 (OC3)
    +    #     Pyrolyzed Organic Carbon (OP)
    +    #  2)Inclusion of gas-phase specie carbon monoxide (CO)
    +    #  3)Inclusion of gas-phase specie ozone (O3).
    +    #  4)Inclusion of both gas-phase species is present...
    +    data = [
    +        ['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'],
    +        ('Basecase', [
    +            [0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],
    +            [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],
    +            [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],
    +            [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],
    +            [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]),
    +        ('With CO', [
    +            [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00],
    +            [0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00],
    +            [0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00],
    +            [0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00],
    +            [0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]),
    +        ('With O3', [
    +            [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03],
    +            [0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00],
    +            [0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00],
    +            [0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95],
    +            [0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]),
    +        ('CO & O3', [
    +            [0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01],
    +            [0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00],
    +            [0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00],
    +            [0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88],
    +            [0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]])
    +    ]
    +    return data
    +
    +
    +if __name__ == '__main__':
    +    N = 9
    +    theta = radar_factory(N, frame='polygon')
    +
    +    data = example_data()
    +    spoke_labels = data.pop(0)
    +
    +    fig, axs = plt.subplots(figsize=(9, 9), nrows=2, ncols=2,
    +                            subplot_kw=dict(projection='radar'))
    +    fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05)
    +
    +    colors = ['b', 'r', 'g', 'm', 'y']
    +    # Plot the four cases from the example data on separate Axes
    +    for ax, (title, case_data) in zip(axs.flat, data):
    +        ax.set_rgrids([0.2, 0.4, 0.6, 0.8])
    +        ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1),
    +                     horizontalalignment='center', verticalalignment='center')
    +        for d, color in zip(case_data, colors):
    +            ax.plot(theta, d, color=color)
    +            ax.fill(theta, d, facecolor=color, alpha=0.25, label='_nolegend_')
    +        ax.set_varlabels(spoke_labels)
    +
    +    # add legend relative to top-left plot
    +    labels = ('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4', 'Factor 5')
    +    legend = axs[0, 0].legend(labels, loc=(0.9, .95),
    +                              labelspacing=0.1, fontsize='small')
    +
    +    fig.text(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios',
    +             horizontalalignment='center', color='black', weight='bold',
    +             size='large')
    +
    +    plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.path`
    +#    - `matplotlib.path.Path`
    +#    - `matplotlib.spines`
    +#    - `matplotlib.spines.Spine`
    +#    - `matplotlib.projections`
    +#    - `matplotlib.projections.polar`
    +#    - `matplotlib.projections.polar.PolarAxes`
    +#    - `matplotlib.projections.register_projection`
    diff --git a/galleries/examples/specialty_plots/sankey_basics.py b/galleries/examples/specialty_plots/sankey_basics.py
    new file mode 100644
    index 000000000000..dd12b9430709
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/sankey_basics.py
    @@ -0,0 +1,113 @@
    +"""
    +================
    +The Sankey class
    +================
    +
    +Demonstrate the Sankey class by producing three basic diagrams.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.sankey import Sankey
    +
    +# %%
    +# Example 1 -- Mostly defaults
    +#
    +# This demonstrates how to create a simple diagram by implicitly calling the
    +# Sankey.add() method and by appending finish() to the call to the class.
    +
    +Sankey(flows=[0.25, 0.15, 0.60, -0.20, -0.15, -0.05, -0.50, -0.10],
    +       labels=['', '', '', 'First', 'Second', 'Third', 'Fourth', 'Fifth'],
    +       orientations=[-1, 1, 0, 1, 1, 1, 0, -1]).finish()
    +plt.title("The default settings produce a diagram like this.")
    +
    +# %%
    +# Notice:
    +#
    +# 1. Axes weren't provided when Sankey() was instantiated, so they were
    +#    created automatically.
    +# 2. The scale argument wasn't necessary since the data was already
    +#    normalized.
    +# 3. By default, the lengths of the paths are justified.
    +
    +
    +# %%
    +# Example 2
    +#
    +# This demonstrates:
    +#
    +# 1. Setting one path longer than the others
    +# 2. Placing a label in the middle of the diagram
    +# 3. Using the scale argument to normalize the flows
    +# 4. Implicitly passing keyword arguments to PathPatch()
    +# 5. Changing the angle of the arrow heads
    +# 6. Changing the offset between the tips of the paths and their labels
    +# 7. Formatting the numbers in the path labels and the associated unit
    +# 8. Changing the appearance of the patch and the labels after the figure is
    +#    created
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[],
    +                     title="Flow Diagram of a Widget")
    +sankey = Sankey(ax=ax, scale=0.01, offset=0.2, head_angle=180,
    +                format='%.0f', unit='%')
    +sankey.add(flows=[25, 0, 60, -10, -20, -5, -15, -10, -40],
    +           labels=['', '', '', 'First', 'Second', 'Third', 'Fourth',
    +                   'Fifth', 'Hurray!'],
    +           orientations=[-1, 1, 0, 1, 1, 1, -1, -1, 0],
    +           pathlengths=[0.25, 0.25, 0.25, 0.25, 0.25, 0.6, 0.25, 0.25,
    +                        0.25],
    +           patchlabel="Widget\nA")  # Arguments to matplotlib.patches.PathPatch
    +diagrams = sankey.finish()
    +diagrams[0].texts[-1].set_color('r')
    +diagrams[0].text.set_fontweight('bold')
    +
    +# %%
    +# Notice:
    +#
    +# 1. Since the sum of the flows is nonzero, the width of the trunk isn't
    +#    uniform.  The matplotlib logging system logs this at the DEBUG level.
    +# 2. The second flow doesn't appear because its value is zero.  Again, this is
    +#    logged at the DEBUG level.
    +
    +
    +# %%
    +# Example 3
    +#
    +# This demonstrates:
    +#
    +# 1. Connecting two systems
    +# 2. Turning off the labels of the quantities
    +# 3. Adding a legend
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[], title="Two Systems")
    +flows = [0.25, 0.15, 0.60, -0.10, -0.05, -0.25, -0.15, -0.10, -0.35]
    +sankey = Sankey(ax=ax, unit=None)
    +sankey.add(flows=flows, label='one',
    +           orientations=[-1, 1, 0, 1, 1, 1, -1, -1, 0])
    +sankey.add(flows=[-0.25, 0.15, 0.1], label='two',
    +           orientations=[-1, -1, -1], prior=0, connect=(0, 0))
    +diagrams = sankey.finish()
    +diagrams[-1].patch.set_hatch('/')
    +plt.legend()
    +
    +# %%
    +# Notice that only one connection is specified, but the systems form a
    +# circuit since: (1) the lengths of the paths are justified and (2) the
    +# orientation and ordering of the flows is mirrored.
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.sankey`
    +#    - `matplotlib.sankey.Sankey`
    +#    - `matplotlib.sankey.Sankey.add`
    +#    - `matplotlib.sankey.Sankey.finish`
    diff --git a/galleries/examples/specialty_plots/sankey_links.py b/galleries/examples/specialty_plots/sankey_links.py
    new file mode 100644
    index 000000000000..283cde86e3bf
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/sankey_links.py
    @@ -0,0 +1,70 @@
    +"""
    +======================================
    +Long chain of connections using Sankey
    +======================================
    +
    +Demonstrate/test the Sankey class by producing a long chain of connections.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.sankey import Sankey
    +
    +links_per_side = 6
    +
    +
    +def side(sankey, n=1):
    +    """Generate a side chain."""
    +    prior = len(sankey.diagrams)
    +    for i in range(0, 2*n, 2):
    +        sankey.add(flows=[1, -1], orientations=[-1, -1],
    +                   patchlabel=str(prior + i),
    +                   prior=prior + i - 1, connect=(1, 0), alpha=0.5)
    +        sankey.add(flows=[1, -1], orientations=[1, 1],
    +                   patchlabel=str(prior + i + 1),
    +                   prior=prior + i, connect=(1, 0), alpha=0.5)
    +
    +
    +def corner(sankey):
    +    """Generate a corner link."""
    +    prior = len(sankey.diagrams)
    +    sankey.add(flows=[1, -1], orientations=[0, 1],
    +               patchlabel=str(prior), facecolor='k',
    +               prior=prior - 1, connect=(1, 0), alpha=0.5)
    +
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[],
    +                     title="Why would you want to do this?\n(But you could.)")
    +sankey = Sankey(ax=ax, unit=None)
    +sankey.add(flows=[1, -1], orientations=[0, 1],
    +           patchlabel="0", facecolor='k',
    +           rotation=45)
    +side(sankey, n=links_per_side)
    +corner(sankey)
    +side(sankey, n=links_per_side)
    +corner(sankey)
    +side(sankey, n=links_per_side)
    +corner(sankey)
    +side(sankey, n=links_per_side)
    +sankey.finish()
    +# Notice:
    +# 1. The alignment doesn't drift significantly (if at all; with 16007
    +#    subdiagrams there is still closure).
    +# 2. The first diagram is rotated 45 deg, so all other diagrams are rotated
    +#    accordingly.
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.sankey`
    +#    - `matplotlib.sankey.Sankey`
    +#    - `matplotlib.sankey.Sankey.add`
    +#    - `matplotlib.sankey.Sankey.finish`
    diff --git a/galleries/examples/specialty_plots/sankey_rankine.py b/galleries/examples/specialty_plots/sankey_rankine.py
    new file mode 100644
    index 000000000000..5662902384fa
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/sankey_rankine.py
    @@ -0,0 +1,96 @@
    +"""
    +===================
    +Rankine power cycle
    +===================
    +
    +Demonstrate the Sankey class with a practical example of a Rankine power cycle.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.sankey import Sankey
    +
    +fig = plt.figure(figsize=(8, 9))
    +ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[],
    +                     title="Rankine Power Cycle: Example 8.6 from Moran and "
    +                     "Shapiro\n\x22Fundamentals of Engineering Thermodynamics "
    +                     "\x22, 6th ed., 2008")
    +Hdot = [260.431, 35.078, 180.794, 221.115, 22.700,
    +        142.361, 10.193, 10.210, 43.670, 44.312,
    +        68.631, 10.758, 10.758, 0.017, 0.642,
    +        232.121, 44.559, 100.613, 132.168]  # MW
    +sankey = Sankey(ax=ax, format='%.3G', unit=' MW', gap=0.5, scale=1.0/Hdot[0])
    +sankey.add(patchlabel='\n\nPump 1', rotation=90, facecolor='#37c959',
    +           flows=[Hdot[13], Hdot[6], -Hdot[7]],
    +           labels=['Shaft power', '', None],
    +           pathlengths=[0.4, 0.883, 0.25],
    +           orientations=[1, -1, 0])
    +sankey.add(patchlabel='\n\nOpen\nheater', facecolor='#37c959',
    +           flows=[Hdot[11], Hdot[7], Hdot[4], -Hdot[8]],
    +           labels=[None, '', None, None],
    +           pathlengths=[0.25, 0.25, 1.93, 0.25],
    +           orientations=[1, 0, -1, 0], prior=0, connect=(2, 1))
    +sankey.add(patchlabel='\n\nPump 2', facecolor='#37c959',
    +           flows=[Hdot[14], Hdot[8], -Hdot[9]],
    +           labels=['Shaft power', '', None],
    +           pathlengths=[0.4, 0.25, 0.25],
    +           orientations=[1, 0, 0], prior=1, connect=(3, 1))
    +sankey.add(patchlabel='Closed\nheater', trunklength=2.914, fc='#37c959',
    +           flows=[Hdot[9], Hdot[1], -Hdot[11], -Hdot[10]],
    +           pathlengths=[0.25, 1.543, 0.25, 0.25],
    +           labels=['', '', None, None],
    +           orientations=[0, -1, 1, -1], prior=2, connect=(2, 0))
    +sankey.add(patchlabel='Trap', facecolor='#37c959', trunklength=5.102,
    +           flows=[Hdot[11], -Hdot[12]],
    +           labels=['\n', None],
    +           pathlengths=[1.0, 1.01],
    +           orientations=[1, 1], prior=3, connect=(2, 0))
    +sankey.add(patchlabel='Steam\ngenerator', facecolor='#ff5555',
    +           flows=[Hdot[15], Hdot[10], Hdot[2], -Hdot[3], -Hdot[0]],
    +           labels=['Heat rate', '', '', None, None],
    +           pathlengths=0.25,
    +           orientations=[1, 0, -1, -1, -1], prior=3, connect=(3, 1))
    +sankey.add(patchlabel='\n\n\nTurbine 1', facecolor='#37c959',
    +           flows=[Hdot[0], -Hdot[16], -Hdot[1], -Hdot[2]],
    +           labels=['', None, None, None],
    +           pathlengths=[0.25, 0.153, 1.543, 0.25],
    +           orientations=[0, 1, -1, -1], prior=5, connect=(4, 0))
    +sankey.add(patchlabel='\n\n\nReheat', facecolor='#37c959',
    +           flows=[Hdot[2], -Hdot[2]],
    +           labels=[None, None],
    +           pathlengths=[0.725, 0.25],
    +           orientations=[-1, 0], prior=6, connect=(3, 0))
    +sankey.add(patchlabel='Turbine 2', trunklength=3.212, facecolor='#37c959',
    +           flows=[Hdot[3], Hdot[16], -Hdot[5], -Hdot[4], -Hdot[17]],
    +           labels=[None, 'Shaft power', None, '', 'Shaft power'],
    +           pathlengths=[0.751, 0.15, 0.25, 1.93, 0.25],
    +           orientations=[0, -1, 0, -1, 1], prior=6, connect=(1, 1))
    +sankey.add(patchlabel='Condenser', facecolor='#58b1fa', trunklength=1.764,
    +           flows=[Hdot[5], -Hdot[18], -Hdot[6]],
    +           labels=['', 'Heat rate', None],
    +           pathlengths=[0.45, 0.25, 0.883],
    +           orientations=[-1, 1, 0], prior=8, connect=(2, 0))
    +diagrams = sankey.finish()
    +for diagram in diagrams:
    +    diagram.text.set_fontweight('bold')
    +    diagram.text.set_fontsize('10')
    +    for text in diagram.texts:
    +        text.set_fontsize('10')
    +# Notice that the explicit connections are handled automatically, but the
    +# implicit ones currently are not.  The lengths of the paths and the trunks
    +# must be adjusted manually, and that is a bit tricky.
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.sankey`
    +#    - `matplotlib.sankey.Sankey`
    +#    - `matplotlib.sankey.Sankey.add`
    +#    - `matplotlib.sankey.Sankey.finish`
    diff --git a/galleries/examples/specialty_plots/skewt.py b/galleries/examples/specialty_plots/skewt.py
    new file mode 100644
    index 000000000000..3a9c14ca6111
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/skewt.py
    @@ -0,0 +1,275 @@
    +"""
    +===========================================================
    +SkewT-logP diagram: using transforms and custom projections
    +===========================================================
    +
    +This serves as an intensive exercise of Matplotlib's transforms and custom
    +projection API. This example produces a so-called SkewT-logP diagram, which is
    +a common plot in meteorology for displaying vertical profiles of temperature.
    +As far as Matplotlib is concerned, the complexity comes from having X and Y
    +axes that are not orthogonal. This is handled by including a skew component to
    +the basic Axes transforms. Additional complexity comes in handling the fact
    +that the upper and lower X-axes have different data ranges, which necessitates
    +a bunch of custom classes for ticks, spines, and axis to handle this.
    +"""
    +
    +from contextlib import ExitStack
    +
    +from matplotlib.axes import Axes
    +import matplotlib.axis as maxis
    +from matplotlib.projections import register_projection
    +import matplotlib.spines as mspines
    +import matplotlib.transforms as transforms
    +
    +
    +# The sole purpose of this class is to look at the upper, lower, or total
    +# interval as appropriate and see what parts of the tick to draw, if any.
    +class SkewXTick(maxis.XTick):
    +    def draw(self, renderer):
    +        # When adding the callbacks with `stack.callback`, we fetch the current
    +        # visibility state of the artist with `get_visible`; the ExitStack will
    +        # restore these states (`set_visible`) at the end of the block (after
    +        # the draw).
    +        with ExitStack() as stack:
    +            for artist in [self.gridline, self.tick1line, self.tick2line,
    +                           self.label1, self.label2]:
    +                stack.callback(artist.set_visible, artist.get_visible())
    +            needs_lower = transforms._interval_contains(
    +                self.axes.lower_xlim, self.get_loc())
    +            needs_upper = transforms._interval_contains(
    +                self.axes.upper_xlim, self.get_loc())
    +            self.tick1line.set_visible(
    +                self.tick1line.get_visible() and needs_lower)
    +            self.label1.set_visible(
    +                self.label1.get_visible() and needs_lower)
    +            self.tick2line.set_visible(
    +                self.tick2line.get_visible() and needs_upper)
    +            self.label2.set_visible(
    +                self.label2.get_visible() and needs_upper)
    +            super().draw(renderer)
    +
    +    def get_view_interval(self):
    +        return self.axes.xaxis.get_view_interval()
    +
    +
    +# This class exists to provide two separate sets of intervals to the tick,
    +# as well as create instances of the custom tick
    +class SkewXAxis(maxis.XAxis):
    +    def _get_tick(self, major):
    +        return SkewXTick(self.axes, None, major=major)
    +
    +    def get_view_interval(self):
    +        return self.axes.upper_xlim[0], self.axes.lower_xlim[1]
    +
    +
    +# This class exists to calculate the separate data range of the
    +# upper X-axis and draw the spine there. It also provides this range
    +# to the X-axis artist for ticking and gridlines
    +class SkewSpine(mspines.Spine):
    +    def _adjust_location(self):
    +        pts = self._path.vertices
    +        if self.spine_type == 'top':
    +            pts[:, 0] = self.axes.upper_xlim
    +        else:
    +            pts[:, 0] = self.axes.lower_xlim
    +
    +
    +# This class handles registration of the skew-xaxes as a projection as well
    +# as setting up the appropriate transformations. It also overrides standard
    +# spines and axes instances as appropriate.
    +class SkewXAxes(Axes):
    +    # The projection must specify a name.  This will be used be the
    +    # user to select the projection, i.e. ``subplot(projection='skewx')``.
    +    name = 'skewx'
    +
    +    def _init_axis(self):
    +        # Taken from Axes and modified to use our modified X-axis
    +        self.xaxis = SkewXAxis(self)
    +        self.spines.top.register_axis(self.xaxis)
    +        self.spines.bottom.register_axis(self.xaxis)
    +        self.yaxis = maxis.YAxis(self)
    +        self.spines.left.register_axis(self.yaxis)
    +        self.spines.right.register_axis(self.yaxis)
    +
    +    def _gen_axes_spines(self):
    +        spines = {'top': SkewSpine.linear_spine(self, 'top'),
    +                  'bottom': mspines.Spine.linear_spine(self, 'bottom'),
    +                  'left': mspines.Spine.linear_spine(self, 'left'),
    +                  'right': mspines.Spine.linear_spine(self, 'right')}
    +        return spines
    +
    +    def _set_lim_and_transforms(self):
    +        """
    +        This is called once when the plot is created to set up all the
    +        transforms for the data, text and grids.
    +        """
    +        rot = 30
    +
    +        # Get the standard transform setup from the Axes base class
    +        super()._set_lim_and_transforms()
    +
    +        # Need to put the skew in the middle, after the scale and limits,
    +        # but before the transAxes. This way, the skew is done in Axes
    +        # coordinates thus performing the transform around the proper origin
    +        # We keep the pre-transAxes transform around for other users, like the
    +        # spines for finding bounds
    +        self.transDataToAxes = (
    +            self.transScale
    +            + self.transLimits
    +            + transforms.Affine2D().skew_deg(rot, 0)
    +        )
    +        # Create the full transform from Data to Pixels
    +        self.transData = self.transDataToAxes + self.transAxes
    +
    +        # Blended transforms like this need to have the skewing applied using
    +        # both axes, in axes coords like before.
    +        self._xaxis_transform = (
    +            transforms.blended_transform_factory(
    +                self.transScale + self.transLimits,
    +                transforms.IdentityTransform())
    +            + transforms.Affine2D().skew_deg(rot, 0)
    +            + self.transAxes
    +        )
    +
    +    @property
    +    def lower_xlim(self):
    +        return self.axes.viewLim.intervalx
    +
    +    @property
    +    def upper_xlim(self):
    +        pts = [[0., 1.], [1., 1.]]
    +        return self.transDataToAxes.inverted().transform(pts)[:, 0]
    +
    +
    +# Now register the projection with matplotlib so the user can select it.
    +register_projection(SkewXAxes)
    +
    +if __name__ == '__main__':
    +    # Now make a simple example using the custom projection.
    +    from io import StringIO
    +
    +    import matplotlib.pyplot as plt
    +    import numpy as np
    +
    +    from matplotlib.ticker import MultipleLocator, NullFormatter, ScalarFormatter
    +
    +    # Some example data.
    +    data_txt = '''
    +        978.0    345    7.8    0.8
    +        971.0    404    7.2    0.2
    +        946.7    610    5.2   -1.8
    +        944.0    634    5.0   -2.0
    +        925.0    798    3.4   -2.6
    +        911.8    914    2.4   -2.7
    +        906.0    966    2.0   -2.7
    +        877.9   1219    0.4   -3.2
    +        850.0   1478   -1.3   -3.7
    +        841.0   1563   -1.9   -3.8
    +        823.0   1736    1.4   -0.7
    +        813.6   1829    4.5    1.2
    +        809.0   1875    6.0    2.2
    +        798.0   1988    7.4   -0.6
    +        791.0   2061    7.6   -1.4
    +        783.9   2134    7.0   -1.7
    +        755.1   2438    4.8   -3.1
    +        727.3   2743    2.5   -4.4
    +        700.5   3048    0.2   -5.8
    +        700.0   3054    0.2   -5.8
    +        698.0   3077    0.0   -6.0
    +        687.0   3204   -0.1   -7.1
    +        648.9   3658   -3.2  -10.9
    +        631.0   3881   -4.7  -12.7
    +        600.7   4267   -6.4  -16.7
    +        592.0   4381   -6.9  -17.9
    +        577.6   4572   -8.1  -19.6
    +        555.3   4877  -10.0  -22.3
    +        536.0   5151  -11.7  -24.7
    +        533.8   5182  -11.9  -25.0
    +        500.0   5680  -15.9  -29.9
    +        472.3   6096  -19.7  -33.4
    +        453.0   6401  -22.4  -36.0
    +        400.0   7310  -30.7  -43.7
    +        399.7   7315  -30.8  -43.8
    +        387.0   7543  -33.1  -46.1
    +        382.7   7620  -33.8  -46.8
    +        342.0   8398  -40.5  -53.5
    +        320.4   8839  -43.7  -56.7
    +        318.0   8890  -44.1  -57.1
    +        310.0   9060  -44.7  -58.7
    +        306.1   9144  -43.9  -57.9
    +        305.0   9169  -43.7  -57.7
    +        300.0   9280  -43.5  -57.5
    +        292.0   9462  -43.7  -58.7
    +        276.0   9838  -47.1  -62.1
    +        264.0  10132  -47.5  -62.5
    +        251.0  10464  -49.7  -64.7
    +        250.0  10490  -49.7  -64.7
    +        247.0  10569  -48.7  -63.7
    +        244.0  10649  -48.9  -63.9
    +        243.3  10668  -48.9  -63.9
    +        220.0  11327  -50.3  -65.3
    +        212.0  11569  -50.5  -65.5
    +        210.0  11631  -49.7  -64.7
    +        200.0  11950  -49.9  -64.9
    +        194.0  12149  -49.9  -64.9
    +        183.0  12529  -51.3  -66.3
    +        164.0  13233  -55.3  -68.3
    +        152.0  13716  -56.5  -69.5
    +        150.0  13800  -57.1  -70.1
    +        136.0  14414  -60.5  -72.5
    +        132.0  14600  -60.1  -72.1
    +        131.4  14630  -60.2  -72.2
    +        128.0  14792  -60.9  -72.9
    +        125.0  14939  -60.1  -72.1
    +        119.0  15240  -62.2  -73.8
    +        112.0  15616  -64.9  -75.9
    +        108.0  15838  -64.1  -75.1
    +        107.8  15850  -64.1  -75.1
    +        105.0  16010  -64.7  -75.7
    +        103.0  16128  -62.9  -73.9
    +        100.0  16310  -62.5  -73.5
    +    '''
    +
    +    # Parse the data
    +    sound_data = StringIO(data_txt)
    +    p, h, T, Td = np.loadtxt(sound_data, unpack=True)
    +
    +    # Create a new figure. The dimensions here give a good aspect ratio
    +    fig = plt.figure(figsize=(6.5875, 6.2125))
    +    ax = fig.add_subplot(projection='skewx')
    +
    +    plt.grid(True)
    +
    +    # Plot the data using normal plotting functions, in this case using
    +    # log scaling in Y, as dictated by the typical meteorological plot
    +    ax.semilogy(T, p, color='C3')
    +    ax.semilogy(Td, p, color='C2')
    +
    +    # An example of a slanted line at constant X
    +    l = ax.axvline(0, color='C0')
    +
    +    # Disables the log-formatting that comes with semilogy
    +    ax.yaxis.set_major_formatter(ScalarFormatter())
    +    ax.yaxis.set_minor_formatter(NullFormatter())
    +    ax.set_yticks(np.linspace(100, 1000, 10))
    +    ax.set_ylim(1050, 100)
    +
    +    ax.xaxis.set_major_locator(MultipleLocator(10))
    +    ax.set_xlim(-50, 50)
    +
    +    plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.transforms`
    +#    - `matplotlib.spines`
    +#    - `matplotlib.spines.Spine`
    +#    - `matplotlib.spines.Spine.register_axis`
    +#    - `matplotlib.projections`
    +#    - `matplotlib.projections.register_projection`
    diff --git a/galleries/examples/specialty_plots/topographic_hillshading.py b/galleries/examples/specialty_plots/topographic_hillshading.py
    new file mode 100644
    index 000000000000..c9aeaa23254a
    --- /dev/null
    +++ b/galleries/examples/specialty_plots/topographic_hillshading.py
    @@ -0,0 +1,74 @@
    +"""
    +=======================
    +Topographic hillshading
    +=======================
    +
    +Demonstrates the visual effect of varying blend mode and vertical exaggeration
    +on "hillshaded" plots.
    +
    +Note that the "overlay" and "soft" blend modes work well for complex surfaces
    +such as this example, while the default "hsv" blend mode works best for smooth
    +surfaces such as many mathematical functions.
    +
    +In most cases, hillshading is used purely for visual purposes, and *dx*/*dy*
    +can be safely ignored. In that case, you can tweak *vert_exag* (vertical
    +exaggeration) by trial and error to give the desired visual effect. However,
    +this example demonstrates how to use the *dx* and *dy* keyword arguments to
    +ensure that the *vert_exag* parameter is the true vertical exaggeration.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.cbook import get_sample_data
    +from matplotlib.colors import LightSource
    +
    +dem = get_sample_data('jacksboro_fault_dem.npz')
    +z = dem['elevation']
    +# -- Optional dx and dy for accurate vertical exaggeration --------------------
    +# If you need topographically accurate vertical exaggeration, or you don't want
    +# to guess at what *vert_exag* should be, you'll need to specify the cellsize
    +# of the grid (i.e. the *dx* and *dy* parameters).  Otherwise, any *vert_exag*
    +# value you specify will be relative to the grid spacing of your input data
    +# (in other words, *dx* and *dy* default to 1.0, and *vert_exag* is calculated
    +# relative to those parameters).  Similarly, *dx* and *dy* are assumed to be in
    +# the same units as your input z-values.  Therefore, we'll need to convert the
    +# given dx and dy from decimal degrees to meters.
    +dx, dy = dem['dx'], dem['dy']
    +dy = 111200 * dy
    +dx = 111200 * dx * np.cos(np.radians(dem['ymin']))
    +# -----------------------------------------------------------------------------
    +
    +# Shade from the northwest, with the sun 45 degrees from horizontal
    +ls = LightSource(azdeg=315, altdeg=45)
    +cmap = plt.colormaps["gist_earth"]
    +
    +fig, axs = plt.subplots(nrows=4, ncols=3, figsize=(8, 9))
    +plt.setp(axs.flat, xticks=[], yticks=[])
    +
    +# Vary vertical exaggeration and blend mode and plot all combinations
    +for col, ve in zip(axs.T, [0.1, 1, 10]):
    +    # Show the hillshade intensity image in the first row
    +    col[0].imshow(ls.hillshade(z, vert_exag=ve, dx=dx, dy=dy), cmap='gray')
    +
    +    # Place hillshaded plots with different blend modes in the rest of the rows
    +    for ax, mode in zip(col[1:], ['hsv', 'overlay', 'soft']):
    +        rgb = ls.shade(z, cmap=cmap, blend_mode=mode,
    +                       vert_exag=ve, dx=dx, dy=dy)
    +        ax.imshow(rgb)
    +
    +# Label rows and columns
    +for ax, ve in zip(axs[0], [0.1, 1, 10]):
    +    ax.set_title(f'{ve}', size=18)
    +for ax, mode in zip(axs[:, 0], ['Hillshade', 'hsv', 'overlay', 'soft']):
    +    ax.set_ylabel(mode, size=18)
    +
    +# Group labels...
    +axs[0, 1].annotate('Vertical Exaggeration', (0.5, 1), xytext=(0, 30),
    +                   textcoords='offset points', xycoords='axes fraction',
    +                   ha='center', va='bottom', size=20)
    +axs[2, 0].annotate('Blend Mode', (0, 0.5), xytext=(-30, 0),
    +                   textcoords='offset points', xycoords='axes fraction',
    +                   ha='right', va='center', size=20, rotation=90)
    +fig.subplots_adjust(bottom=0.05, right=0.95)
    +
    +plt.show()
    diff --git a/galleries/examples/spines/README.txt b/galleries/examples/spines/README.txt
    new file mode 100644
    index 000000000000..40bc3952eacd
    --- /dev/null
    +++ b/galleries/examples/spines/README.txt
    @@ -0,0 +1,4 @@
    +.. _spines_examples:
    +
    +Spines
    +======
    diff --git a/galleries/examples/spines/centered_spines_with_arrows.py b/galleries/examples/spines/centered_spines_with_arrows.py
    new file mode 100644
    index 000000000000..f71d071a5f82
    --- /dev/null
    +++ b/galleries/examples/spines/centered_spines_with_arrows.py
    @@ -0,0 +1,32 @@
    +"""
    +===========================
    +Centered spines with arrows
    +===========================
    +
    +This example shows a way to draw a "math textbook" style plot, where the
    +spines ("axes lines") are drawn at ``x = 0`` and ``y = 0``, and have arrows at
    +their ends.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, ax = plt.subplots()
    +# Move the left and bottom spines to x = 0 and y = 0, respectively.
    +ax.spines[["left", "bottom"]].set_position(("data", 0))
    +# Hide the top and right spines.
    +ax.spines[["top", "right"]].set_visible(False)
    +
    +# Draw arrows (as black triangles: ">k"/"^k") at the end of the axes.  In each
    +# case, one of the coordinates (0) is a data coordinate (i.e., y = 0 or x = 0,
    +# respectively) and the other one (1) is an axes coordinate (i.e., at the very
    +# right/top of the axes).  Also, disable clipping (clip_on=False) as the marker
    +# actually spills out of the Axes.
    +ax.plot(1, 0, ">k", transform=ax.get_yaxis_transform(), clip_on=False)
    +ax.plot(0, 1, "^k", transform=ax.get_xaxis_transform(), clip_on=False)
    +
    +# Some sample data.
    +x = np.linspace(-0.5, 1., 100)
    +ax.plot(x, np.sin(x*np.pi))
    +
    +plt.show()
    diff --git a/galleries/examples/spines/multiple_yaxis_with_spines.py b/galleries/examples/spines/multiple_yaxis_with_spines.py
    new file mode 100644
    index 000000000000..a0281bdeda0f
    --- /dev/null
    +++ b/galleries/examples/spines/multiple_yaxis_with_spines.py
    @@ -0,0 +1,46 @@
    +r"""
    +===========================
    +Multiple y-axis with Spines
    +===========================
    +
    +Create multiple y axes with a shared x-axis. This is done by creating
    +a `~.axes.Axes.twinx` Axes, turning all spines but the right one invisible
    +and offset its position using `~.spines.Spine.set_position`.
    +
    +Note that this approach uses `matplotlib.axes.Axes` and their
    +`~matplotlib.spines.Spine`\s.  Alternative approaches using non-standard Axes
    +are shown in the :doc:`/gallery/axisartist/demo_parasite_axes` and
    +:doc:`/gallery/axisartist/demo_parasite_axes2` examples.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +fig.subplots_adjust(right=0.75)
    +
    +twin1 = ax.twinx()
    +twin2 = ax.twinx()
    +
    +# Offset the right spine of twin2.  The ticks and label have already been
    +# placed on the right by twinx above.
    +twin2.spines.right.set_position(("axes", 1.2))
    +
    +p1, = ax.plot([0, 1, 2], [0, 1, 2], "C0", label="Density")
    +p2, = twin1.plot([0, 1, 2], [0, 3, 2], "C1", label="Temperature")
    +p3, = twin2.plot([0, 1, 2], [50, 30, 15], "C2", label="Velocity")
    +
    +ax.set(xlim=(0, 2), ylim=(0, 2), xlabel="Distance", ylabel="Density")
    +twin1.set(ylim=(0, 4), ylabel="Temperature")
    +twin2.set(ylim=(1, 65), ylabel="Velocity")
    +
    +ax.yaxis.label.set_color(p1.get_color())
    +twin1.yaxis.label.set_color(p2.get_color())
    +twin2.yaxis.label.set_color(p3.get_color())
    +
    +ax.tick_params(axis='y', colors=p1.get_color())
    +twin1.tick_params(axis='y', colors=p2.get_color())
    +twin2.tick_params(axis='y', colors=p3.get_color())
    +
    +ax.legend(handles=[p1, p2, p3])
    +
    +plt.show()
    diff --git a/galleries/examples/spines/spine_placement_demo.py b/galleries/examples/spines/spine_placement_demo.py
    new file mode 100644
    index 000000000000..d7d52605c6d7
    --- /dev/null
    +++ b/galleries/examples/spines/spine_placement_demo.py
    @@ -0,0 +1,52 @@
    +"""
    +===============
    +Spine placement
    +===============
    +
    +The position of the axis spines can be influenced using `~.Spine.set_position`.
    +
    +Note: If you want to obtain arrow heads at the ends of the axes, also check
    +out the :doc:`/gallery/spines/centered_spines_with_arrows` example.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# %%
    +
    +x = np.linspace(0, 2*np.pi, 100)
    +y = 2 * np.sin(x)
    +
    +fig, ax_dict = plt.subplot_mosaic(
    +    [['center', 'zero'],
    +     ['axes', 'data']]
    +)
    +fig.suptitle('Spine positions')
    +
    +
    +ax = ax_dict['center']
    +ax.set_title("'center'")
    +ax.plot(x, y)
    +ax.spines[['left', 'bottom']].set_position('center')
    +ax.spines[['top', 'right']].set_visible(False)
    +
    +ax = ax_dict['zero']
    +ax.set_title("'zero'")
    +ax.plot(x, y)
    +ax.spines[['left', 'bottom']].set_position('zero')
    +ax.spines[['top', 'right']].set_visible(False)
    +
    +ax = ax_dict['axes']
    +ax.set_title("'axes' (0.2, 0.2)")
    +ax.plot(x, y)
    +ax.spines.left.set_position(('axes', 0.2))
    +ax.spines.bottom.set_position(('axes', 0.2))
    +ax.spines[['top', 'right']].set_visible(False)
    +
    +ax = ax_dict['data']
    +ax.set_title("'data' (1, 2)")
    +ax.plot(x, y)
    +ax.spines.left.set_position(('data', 1))
    +ax.spines.bottom.set_position(('data', 2))
    +ax.spines[['top', 'right']].set_visible(False)
    +
    +plt.show()
    diff --git a/galleries/examples/spines/spines.py b/galleries/examples/spines/spines.py
    new file mode 100644
    index 000000000000..2e28fb546d86
    --- /dev/null
    +++ b/galleries/examples/spines/spines.py
    @@ -0,0 +1,57 @@
    +"""
    +======
    +Spines
    +======
    +
    +This demo compares:
    +
    +- normal Axes, with spines on all four sides;
    +- an Axes with spines only on the left and bottom;
    +- an Axes using custom bounds to limit the extent of the spine.
    +
    +Each `.axes.Axes` has a list of `.Spine` objects, accessible
    +via the container ``ax.spines``.
    +
    +.. redirect-from:: /gallery/spines/spines_bounds
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.linspace(0, 2 * np.pi, 100)
    +y = 2 * np.sin(x)
    +
    +# Constrained layout makes sure the labels don't overlap the Axes.
    +fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, layout='constrained')
    +
    +ax0.plot(x, y)
    +ax0.set_title('normal spines')
    +
    +ax1.plot(x, y)
    +ax1.set_title('bottom-left spines')
    +
    +# Hide the right and top spines
    +ax1.spines.right.set_visible(False)
    +ax1.spines.top.set_visible(False)
    +
    +ax2.plot(x, y)
    +ax2.set_title('spines with bounds limited to data range')
    +
    +# Only draw spines for the data range, not in the margins
    +ax2.spines.bottom.set_bounds(x.min(), x.max())
    +ax2.spines.left.set_bounds(y.min(), y.max())
    +# Hide the right and top spines
    +ax2.spines.right.set_visible(False)
    +ax2.spines.top.set_visible(False)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.artist.Artist.set_visible`
    +#    - `matplotlib.spines.Spine.set_bounds`
    diff --git a/galleries/examples/spines/spines_dropped.py b/galleries/examples/spines/spines_dropped.py
    new file mode 100644
    index 000000000000..3adeee92ba37
    --- /dev/null
    +++ b/galleries/examples/spines/spines_dropped.py
    @@ -0,0 +1,37 @@
    +"""
    +==============
    +Dropped spines
    +==============
    +
    +Demo of spines offset from the axes (a.k.a. "dropped spines").
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def adjust_spines(ax, visible_spines):
    +    ax.label_outer(remove_inner_ticks=True)
    +    ax.grid(color='0.9')
    +
    +    for loc, spine in ax.spines.items():
    +        if loc in visible_spines:
    +            spine.set_position(('outward', 10))  # outward by 10 points
    +        else:
    +            spine.set_visible(False)
    +
    +
    +x = np.linspace(0, 2 * np.pi, 100)
    +
    +fig, axs = plt.subplots(2, 2)
    +
    +axs[0, 0].plot(x, np.sin(x))
    +axs[0, 1].plot(x, np.cos(x))
    +axs[1, 0].plot(x, -np.cos(x))
    +axs[1, 1].plot(x, -np.sin(x))
    +
    +adjust_spines(axs[0, 0], ['left'])
    +adjust_spines(axs[0, 1], [])
    +adjust_spines(axs[1, 0], ['left', 'bottom'])
    +adjust_spines(axs[1, 1], ['bottom'])
    +
    +plt.show()
    diff --git a/galleries/examples/statistics/README.txt b/galleries/examples/statistics/README.txt
    new file mode 100644
    index 000000000000..5949a1920c02
    --- /dev/null
    +++ b/galleries/examples/statistics/README.txt
    @@ -0,0 +1,4 @@
    +.. _statistics_examples:
    +
    +Statistics
    +==========
    diff --git a/galleries/examples/statistics/boxplot.py b/galleries/examples/statistics/boxplot.py
    new file mode 100644
    index 000000000000..6d30cbd4b5f0
    --- /dev/null
    +++ b/galleries/examples/statistics/boxplot.py
    @@ -0,0 +1,107 @@
    +"""
    +=================================
    +Artist customization in box plots
    +=================================
    +
    +This example demonstrates how to use the various keyword arguments to fully
    +customize box plots. The first figure demonstrates how to remove and add
    +individual components (note that the mean is the only value not shown by
    +default). The second figure demonstrates how the styles of the artists can be
    +customized. It also demonstrates how to set the limit of the whiskers to
    +specific percentiles (lower right Axes)
    +
    +A good general reference on boxplots and their history can be found here:
    +https://vita.had.co.nz/papers/boxplots.pdf
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# fake data
    +np.random.seed(19680801)
    +data = np.random.lognormal(size=(37, 4), mean=1.5, sigma=1.75)
    +labels = list('ABCD')
    +fs = 10  # fontsize
    +
    +# %%
    +# Demonstrate how to toggle the display of different elements:
    +
    +fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True)
    +axs[0, 0].boxplot(data, tick_labels=labels)
    +axs[0, 0].set_title('Default', fontsize=fs)
    +
    +axs[0, 1].boxplot(data, tick_labels=labels, showmeans=True)
    +axs[0, 1].set_title('showmeans=True', fontsize=fs)
    +
    +axs[0, 2].boxplot(data, tick_labels=labels, showmeans=True, meanline=True)
    +axs[0, 2].set_title('showmeans=True,\nmeanline=True', fontsize=fs)
    +
    +axs[1, 0].boxplot(data, tick_labels=labels, showbox=False, showcaps=False)
    +tufte_title = 'Tufte Style \n(showbox=False,\nshowcaps=False)'
    +axs[1, 0].set_title(tufte_title, fontsize=fs)
    +
    +axs[1, 1].boxplot(data, tick_labels=labels, notch=True, bootstrap=10000)
    +axs[1, 1].set_title('notch=True,\nbootstrap=10000', fontsize=fs)
    +
    +axs[1, 2].boxplot(data, tick_labels=labels, showfliers=False)
    +axs[1, 2].set_title('showfliers=False', fontsize=fs)
    +
    +for ax in axs.flat:
    +    ax.set_yscale('log')
    +    ax.set_yticklabels([])
    +
    +fig.subplots_adjust(hspace=0.4)
    +plt.show()
    +
    +
    +# %%
    +# Demonstrate how to customize the display different elements:
    +
    +boxprops = dict(linestyle='--', linewidth=3, color='darkgoldenrod')
    +flierprops = dict(marker='o', markerfacecolor='green', markersize=12,
    +                  markeredgecolor='none')
    +medianprops = dict(linestyle='-.', linewidth=2.5, color='firebrick')
    +meanpointprops = dict(marker='D', markeredgecolor='black',
    +                      markerfacecolor='firebrick')
    +meanlineprops = dict(linestyle='--', linewidth=2.5, color='purple')
    +
    +fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(6, 6), sharey=True)
    +axs[0, 0].boxplot(data, boxprops=boxprops)
    +axs[0, 0].set_title('Custom boxprops', fontsize=fs)
    +
    +axs[0, 1].boxplot(data, flierprops=flierprops, medianprops=medianprops)
    +axs[0, 1].set_title('Custom medianprops\nand flierprops', fontsize=fs)
    +
    +axs[0, 2].boxplot(data, whis=(0, 100))
    +axs[0, 2].set_title('whis=(0, 100)', fontsize=fs)
    +
    +axs[1, 0].boxplot(data, meanprops=meanpointprops, meanline=False,
    +                  showmeans=True)
    +axs[1, 0].set_title('Custom mean\nas point', fontsize=fs)
    +
    +axs[1, 1].boxplot(data, meanprops=meanlineprops, meanline=True,
    +                  showmeans=True)
    +axs[1, 1].set_title('Custom mean\nas line', fontsize=fs)
    +
    +axs[1, 2].boxplot(data, whis=[15, 85])
    +axs[1, 2].set_title('whis=[15, 85]\n#percentiles', fontsize=fs)
    +
    +for ax in axs.flat:
    +    ax.set_yscale('log')
    +    ax.set_yticklabels([])
    +
    +fig.suptitle("I never said they'd be pretty")
    +fig.subplots_adjust(hspace=0.4)
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: boxplot, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot`
    diff --git a/galleries/examples/statistics/boxplot_color.py b/galleries/examples/statistics/boxplot_color.py
    new file mode 100644
    index 000000000000..acdb37d7d520
    --- /dev/null
    +++ b/galleries/examples/statistics/boxplot_color.py
    @@ -0,0 +1,46 @@
    +"""
    +=================================
    +Box plots with custom fill colors
    +=================================
    +
    +To color each box of a box plot individually:
    +
    +1) use the keyword argument ``patch_artist=True`` to create filled boxes.
    +2) loop through the created boxes and adapt their color.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +fruit_weights = [
    +    np.random.normal(130, 10, size=100),
    +    np.random.normal(125, 20, size=100),
    +    np.random.normal(120, 30, size=100),
    +]
    +labels = ['peaches', 'oranges', 'tomatoes']
    +colors = ['peachpuff', 'orange', 'tomato']
    +
    +fig, ax = plt.subplots()
    +ax.set_ylabel('fruit weight (g)')
    +
    +bplot = ax.boxplot(fruit_weights,
    +                   patch_artist=True,  # fill with color
    +                   tick_labels=labels)  # will be used to label x-ticks
    +
    +# fill with colors
    +for patch, color in zip(bplot['boxes'], colors):
    +    patch.set_facecolor(color)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: styling: color, domain: statistics, plot-type: boxplot
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot`
    diff --git a/galleries/examples/statistics/boxplot_demo.py b/galleries/examples/statistics/boxplot_demo.py
    new file mode 100644
    index 000000000000..b642d7e9f658
    --- /dev/null
    +++ b/galleries/examples/statistics/boxplot_demo.py
    @@ -0,0 +1,258 @@
    +"""
    +========
    +Boxplots
    +========
    +
    +Visualizing boxplots with matplotlib.
    +
    +The following examples show off how to visualize boxplots with
    +Matplotlib. There are many options to control their appearance and
    +the statistics that they use to summarize the data.
    +
    +.. redirect-from:: /gallery/pyplots/boxplot_demo_pyplot
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Polygon
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# fake up some data
    +spread = np.random.rand(50) * 100
    +center = np.ones(25) * 50
    +flier_high = np.random.rand(10) * 100 + 100
    +flier_low = np.random.rand(10) * -100
    +data = np.concatenate((spread, center, flier_high, flier_low))
    +
    +fig, axs = plt.subplots(2, 3)
    +
    +# basic plot
    +axs[0, 0].boxplot(data)
    +axs[0, 0].set_title('basic plot')
    +
    +# notched plot
    +axs[0, 1].boxplot(data, notch=True)
    +axs[0, 1].set_title('notched plot')
    +
    +# change outlier point symbols
    +axs[0, 2].boxplot(data, sym='gD')
    +axs[0, 2].set_title('change outlier\npoint symbols')
    +
    +# don't show outlier points
    +axs[1, 0].boxplot(data, sym='')
    +axs[1, 0].set_title("don't show\noutlier points")
    +
    +# horizontal boxes
    +axs[1, 1].boxplot(data, sym='rs', orientation='horizontal')
    +axs[1, 1].set_title('horizontal boxes')
    +
    +# change whisker length
    +axs[1, 2].boxplot(data, sym='rs', orientation='horizontal', whis=0.75)
    +axs[1, 2].set_title('change whisker length')
    +
    +fig.subplots_adjust(left=0.08, right=0.98, bottom=0.05, top=0.9,
    +                    hspace=0.4, wspace=0.3)
    +
    +# fake up some more data
    +spread = np.random.rand(50) * 100
    +center = np.ones(25) * 40
    +flier_high = np.random.rand(10) * 100 + 100
    +flier_low = np.random.rand(10) * -100
    +d2 = np.concatenate((spread, center, flier_high, flier_low))
    +# Making a 2-D array only works if all the columns are the
    +# same length.  If they are not, then use a list instead.
    +# This is actually more efficient because boxplot converts
    +# a 2-D array into a list of vectors internally anyway.
    +data = [data, d2, d2[::2]]
    +
    +# Multiple box plots on one Axes
    +fig, ax = plt.subplots()
    +ax.boxplot(data)
    +
    +plt.show()
    +
    +
    +# %%
    +# Below we'll generate data from five different probability distributions,
    +# each with different characteristics. We want to play with how an IID
    +# bootstrap resample of the data preserves the distributional
    +# properties of the original sample, and a boxplot is one visual tool
    +# to make this assessment
    +
    +random_dists = ['Normal(1, 1)', 'Lognormal(1, 1)', 'Exp(1)', 'Gumbel(6, 4)',
    +                'Triangular(2, 9, 11)']
    +N = 500
    +
    +norm = np.random.normal(1, 1, N)
    +logn = np.random.lognormal(1, 1, N)
    +expo = np.random.exponential(1, N)
    +gumb = np.random.gumbel(6, 4, N)
    +tria = np.random.triangular(2, 9, 11, N)
    +
    +# Generate some random indices that we'll use to resample the original data
    +# arrays. For code brevity, just use the same random indices for each array
    +bootstrap_indices = np.random.randint(0, N, N)
    +data = [
    +    norm, norm[bootstrap_indices],
    +    logn, logn[bootstrap_indices],
    +    expo, expo[bootstrap_indices],
    +    gumb, gumb[bootstrap_indices],
    +    tria, tria[bootstrap_indices],
    +]
    +
    +fig, ax1 = plt.subplots(figsize=(10, 6))
    +fig.canvas.manager.set_window_title('A Boxplot Example')
    +fig.subplots_adjust(left=0.075, right=0.95, top=0.9, bottom=0.25)
    +
    +bp = ax1.boxplot(data, notch=False, sym='+', orientation='vertical', whis=1.5)
    +plt.setp(bp['boxes'], color='black')
    +plt.setp(bp['whiskers'], color='black')
    +plt.setp(bp['fliers'], color='red', marker='+')
    +
    +# Add a horizontal grid to the plot, but make it very light in color
    +# so we can use it for reading data values but not be distracting
    +ax1.yaxis.grid(True, linestyle='-', which='major', color='lightgrey',
    +               alpha=0.5)
    +
    +ax1.set(
    +    axisbelow=True,  # Hide the grid behind plot objects
    +    title='Comparison of IID Bootstrap Resampling Across Five Distributions',
    +    xlabel='Distribution',
    +    ylabel='Value',
    +)
    +
    +# Now fill the boxes with desired colors
    +box_colors = ['darkkhaki', 'royalblue']
    +num_boxes = len(data)
    +medians = np.empty(num_boxes)
    +for i in range(num_boxes):
    +    box = bp['boxes'][i]
    +    box_x = []
    +    box_y = []
    +    for j in range(5):
    +        box_x.append(box.get_xdata()[j])
    +        box_y.append(box.get_ydata()[j])
    +    box_coords = np.column_stack([box_x, box_y])
    +    # Alternate between Dark Khaki and Royal Blue
    +    ax1.add_patch(Polygon(box_coords, facecolor=box_colors[i % 2]))
    +    # Now draw the median lines back over what we just filled in
    +    med = bp['medians'][i]
    +    median_x = []
    +    median_y = []
    +    for j in range(2):
    +        median_x.append(med.get_xdata()[j])
    +        median_y.append(med.get_ydata()[j])
    +        ax1.plot(median_x, median_y, 'k')
    +    medians[i] = median_y[0]
    +    # Finally, overplot the sample averages, with horizontal alignment
    +    # in the center of each box
    +    ax1.plot(np.average(med.get_xdata()), np.average(data[i]),
    +             color='w', marker='*', markeredgecolor='k')
    +
    +# Set the axes ranges and axes labels
    +ax1.set_xlim(0.5, num_boxes + 0.5)
    +top = 40
    +bottom = -5
    +ax1.set_ylim(bottom, top)
    +ax1.set_xticklabels(np.repeat(random_dists, 2),
    +                    rotation=45, fontsize=8)
    +
    +# Due to the Y-axis scale being different across samples, it can be
    +# hard to compare differences in medians across the samples. Add upper
    +# X-axis tick labels with the sample medians to aid in comparison
    +# (just use two decimal places of precision)
    +pos = np.arange(num_boxes) + 1
    +upper_labels = [str(round(s, 2)) for s in medians]
    +weights = ['bold', 'semibold']
    +for tick, label in zip(range(num_boxes), ax1.get_xticklabels()):
    +    k = tick % 2
    +    ax1.text(pos[tick], .95, upper_labels[tick],
    +             transform=ax1.get_xaxis_transform(),
    +             horizontalalignment='center', size='x-small',
    +             weight=weights[k], color=box_colors[k])
    +
    +# Finally, add a basic legend
    +fig.text(0.80, 0.08, f'{N} Random Numbers',
    +         backgroundcolor=box_colors[0], color='black', weight='roman',
    +         size='x-small')
    +fig.text(0.80, 0.045, 'IID Bootstrap Resample',
    +         backgroundcolor=box_colors[1],
    +         color='white', weight='roman', size='x-small')
    +fig.text(0.80, 0.015, '*', color='white', backgroundcolor='silver',
    +         weight='roman', size='medium')
    +fig.text(0.815, 0.013, ' Average Value', color='black', weight='roman',
    +         size='x-small')
    +
    +plt.show()
    +
    +# %%
    +# Here we write a custom function to bootstrap confidence intervals.
    +# We can then use the boxplot along with this function to show these intervals.
    +
    +
    +def fake_bootstrapper(n):
    +    """
    +    This is just a placeholder for the user's method of
    +    bootstrapping the median and its confidence intervals.
    +
    +    Returns an arbitrary median and confidence interval packed into a tuple.
    +    """
    +    if n == 1:
    +        med = 0.1
    +        ci = (-0.25, 0.25)
    +    else:
    +        med = 0.2
    +        ci = (-0.35, 0.50)
    +    return med, ci
    +
    +inc = 0.1
    +e1 = np.random.normal(0, 1, size=500)
    +e2 = np.random.normal(0, 1, size=500)
    +e3 = np.random.normal(0, 1 + inc, size=500)
    +e4 = np.random.normal(0, 1 + 2*inc, size=500)
    +
    +treatments = [e1, e2, e3, e4]
    +med1, ci1 = fake_bootstrapper(1)
    +med2, ci2 = fake_bootstrapper(2)
    +medians = [None, None, med1, med2]
    +conf_intervals = [None, None, ci1, ci2]
    +
    +fig, ax = plt.subplots()
    +pos = np.arange(len(treatments)) + 1
    +bp = ax.boxplot(treatments, sym='k+', positions=pos,
    +                notch=True, bootstrap=5000,
    +                usermedians=medians,
    +                conf_intervals=conf_intervals)
    +
    +ax.set_xlabel('treatment')
    +ax.set_ylabel('response')
    +plt.setp(bp['whiskers'], color='k', linestyle='-')
    +plt.setp(bp['fliers'], markersize=3.0)
    +plt.show()
    +
    +
    +# %%
    +# Here we customize the widths of the caps .
    +
    +x = np.linspace(-7, 7, 140)
    +x = np.hstack([-25, x, 25])
    +fig, ax = plt.subplots()
    +
    +ax.boxplot([x, x], notch=True, capwidths=[0.01, 0.2])
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: domain: statistics, plot-type: boxplot
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot`
    +#    - `matplotlib.artist.Artist.set` / `matplotlib.pyplot.setp`
    diff --git a/galleries/examples/statistics/boxplot_vs_violin.py b/galleries/examples/statistics/boxplot_vs_violin.py
    new file mode 100644
    index 000000000000..06aa2693f446
    --- /dev/null
    +++ b/galleries/examples/statistics/boxplot_vs_violin.py
    @@ -0,0 +1,68 @@
    +"""
    +===================================
    +Box plot vs. violin plot comparison
    +===================================
    +
    +Note that although violin plots are closely related to Tukey's (1977)
    +box plots, they add useful information such as the distribution of the
    +sample data (density trace).
    +
    +By default, box plots show data points outside 1.5 * the inter-quartile
    +range as outliers above or below the whiskers whereas violin plots show
    +the whole range of the data.
    +
    +A good general reference on boxplots and their history can be found
    +here: https://vita.had.co.nz/papers/boxplots.pdf
    +
    +Violin plots require matplotlib >= 1.4.
    +
    +Violin plots show the distribution of the data as a rotated kernel density
    +estimate (KDE) along with summary statistics similar to a box plot.
    +
    +For more information on violin plots, see:
    +https://en.wikipedia.org/wiki/Violin_plot
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(9, 4))
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +# generate some random test data
    +all_data = [np.random.normal(0, std, 100) for std in range(6, 10)]
    +
    +# plot violin plot
    +axs[0].violinplot(all_data,
    +                  showmeans=False,
    +                  showmedians=True)
    +axs[0].set_title('Violin plot')
    +
    +# plot box plot
    +axs[1].boxplot(all_data)
    +axs[1].set_title('Box plot')
    +
    +# adding horizontal grid lines
    +for ax in axs:
    +    ax.yaxis.grid(True)
    +    ax.set_xticks([y + 1 for y in range(len(all_data))],
    +                  labels=['x1', 'x2', 'x3', 'x4'])
    +    ax.set_xlabel('Four separate samples')
    +    ax.set_ylabel('Observed values')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: violin, plot-type: boxplot, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.boxplot` / `matplotlib.pyplot.boxplot`
    +#    - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot`
    diff --git a/galleries/examples/statistics/bxp.py b/galleries/examples/statistics/bxp.py
    new file mode 100644
    index 000000000000..763608bf0273
    --- /dev/null
    +++ b/galleries/examples/statistics/bxp.py
    @@ -0,0 +1,76 @@
    +"""
    +=============================================
    +Separate calculation and plotting of boxplots
    +=============================================
    +
    +Drawing a `~.axes.Axes.boxplot` for a given data set, consists of two main operations,
    +that can also be used separately:
    +
    +1. Calculating the boxplot statistics: `matplotlib.cbook.boxplot_stats`
    +2. Drawing the boxplot: `matplotlib.axes.Axes.bxp`
    +
    +Thus, ``ax.boxplot(data)`` is equivalent to ::
    +
    +    stats = cbook.boxplot_stats(data)
    +    ax.bxp(stats)
    +
    +All styling keyword arguments are identical between `~.axes.Axes.boxplot` and
    +`~.axes.Axes.bxp`, and they are passed through from `~.axes.Axes.boxplot` to
    +`~.axes.Axes.bxp`. However, the *tick_labels* parameter of `~.axes.Axes.boxplot`
    +translates to a generic *labels* parameter in `.boxplot_stats`, because the labels are
    +data-related and attached to the returned per-dataset dictionaries.
    +
    +The following code demonstrates the equivalence between the two methods.
    +
    +"""
    +# sphinx_gallery_thumbnail_number = 2
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import cbook
    +
    +np.random.seed(19680801)
    +data = np.random.randn(20, 3)
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +
    +# single boxplot call
    +ax1.boxplot(data, tick_labels=['A', 'B', 'C'],
    +            patch_artist=True, boxprops={'facecolor': 'bisque'})
    +
    +# separate calculation of statistics and plotting
    +stats = cbook.boxplot_stats(data, labels=['A', 'B', 'C'])
    +ax2.bxp(stats, patch_artist=True, boxprops={'facecolor': 'bisque'})
    +
    +# %%
    +# Using the separate functions allows to pre-calculate statistics, in case you need
    +# them explicitly for other purposes, or to reuse the statistics for multiple plots.
    +#
    +# Conversely, you can also use the `~.axes.Axes.bxp` function directly, if you already
    +# have the statistical parameters:
    +
    +fig, ax = plt.subplots()
    +
    +stats = [
    +    dict(med=0, q1=-1, q3=1, whislo=-2, whishi=2, fliers=[-4, -3, 3, 4], label='A'),
    +    dict(med=0, q1=-2, q3=2, whislo=-3, whishi=3, fliers=[], label='B'),
    +    dict(med=0, q1=-3, q3=3, whislo=-4, whishi=4, fliers=[], label='C'),
    +]
    +
    +ax.bxp(stats, patch_artist=True, boxprops={'facecolor': 'bisque'})
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: specialty, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.bxp`
    +#    - `matplotlib.axes.Axes.boxplot`
    +#    - `matplotlib.cbook.boxplot_stats`
    diff --git a/galleries/examples/statistics/cohere.py b/galleries/examples/statistics/cohere.py
    new file mode 100644
    index 000000000000..4fd76d66a06a
    --- /dev/null
    +++ b/galleries/examples/statistics/cohere.py
    @@ -0,0 +1,42 @@
    +"""
    +=====================================
    +Plotting the coherence of two signals
    +=====================================
    +
    +An example showing how to plot the coherence of two signals using `~.Axes.cohere`.
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/cohere
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +dt = 0.01
    +t = np.arange(0, 30, dt)
    +nse1 = np.random.randn(len(t))                 # white noise 1
    +nse2 = np.random.randn(len(t))                 # white noise 2
    +
    +# Two signals with a coherent part at 10 Hz and a random part
    +s1 = np.sin(2 * np.pi * 10 * t) + nse1
    +s2 = np.sin(2 * np.pi * 10 * t) + nse2
    +
    +fig, axs = plt.subplots(2, 1, layout='constrained')
    +axs[0].plot(t, s1, t, s2)
    +axs[0].set_xlim(0, 2)
    +axs[0].set_xlabel('Time (s)')
    +axs[0].set_ylabel('s1 and s2')
    +axs[0].grid(True)
    +
    +cxy, f = axs[1].cohere(s1, s2, NFFT=256, Fs=1. / dt)
    +axs[1].set_ylabel('Coherence')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    domain: signal-processing
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/statistics/confidence_ellipse.py b/galleries/examples/statistics/confidence_ellipse.py
    new file mode 100644
    index 000000000000..965a6ee75b0f
    --- /dev/null
    +++ b/galleries/examples/statistics/confidence_ellipse.py
    @@ -0,0 +1,234 @@
    +"""
    +======================================================
    +Plot a confidence ellipse of a two-dimensional dataset
    +======================================================
    +
    +This example shows how to plot a confidence ellipse of a
    +two-dimensional dataset, using its pearson correlation coefficient.
    +
    +The approach that is used to obtain the correct geometry is
    +explained and proved here:
    +
    +https://carstenschelp.github.io/2018/09/14/Plot_Confidence_Ellipse_001.html
    +
    +The method avoids the use of an iterative eigen decomposition algorithm
    +and makes use of the fact that a normalized covariance matrix (composed of
    +pearson correlation coefficients and ones) is particularly easy to handle.
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Ellipse
    +import matplotlib.transforms as transforms
    +
    +# %%
    +#
    +# The plotting function itself
    +# """"""""""""""""""""""""""""
    +#
    +# This function plots the confidence ellipse of the covariance of the given
    +# array-like variables x and y. The ellipse is plotted into the given
    +# Axes object *ax*.
    +#
    +# The radiuses of the ellipse can be controlled by n_std which is the number
    +# of standard deviations. The default value is 3 which makes the ellipse
    +# enclose 98.9% of the points if the data is normally distributed
    +# like in these examples (3 standard deviations in 1-D contain 99.7%
    +# of the data, which is 98.9% of the data in 2-D).
    +
    +
    +def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs):
    +    """
    +    Create a plot of the covariance confidence ellipse of *x* and *y*.
    +
    +    Parameters
    +    ----------
    +    x, y : array-like, shape (n, )
    +        Input data.
    +
    +    ax : matplotlib.axes.Axes
    +        The Axes object to draw the ellipse into.
    +
    +    n_std : float
    +        The number of standard deviations to determine the ellipse's radiuses.
    +
    +    **kwargs
    +        Forwarded to `~matplotlib.patches.Ellipse`
    +
    +    Returns
    +    -------
    +    matplotlib.patches.Ellipse
    +    """
    +    if x.size != y.size:
    +        raise ValueError("x and y must be the same size")
    +
    +    cov = np.cov(x, y)
    +    pearson = cov[0, 1]/np.sqrt(cov[0, 0] * cov[1, 1])
    +    # Using a special case to obtain the eigenvalues of this
    +    # two-dimensional dataset.
    +    ell_radius_x = np.sqrt(1 + pearson)
    +    ell_radius_y = np.sqrt(1 - pearson)
    +    ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2,
    +                      facecolor=facecolor, **kwargs)
    +
    +    # Calculating the standard deviation of x from
    +    # the squareroot of the variance and multiplying
    +    # with the given number of standard deviations.
    +    scale_x = np.sqrt(cov[0, 0]) * n_std
    +    mean_x = np.mean(x)
    +
    +    # calculating the standard deviation of y ...
    +    scale_y = np.sqrt(cov[1, 1]) * n_std
    +    mean_y = np.mean(y)
    +
    +    transf = transforms.Affine2D() \
    +        .rotate_deg(45) \
    +        .scale(scale_x, scale_y) \
    +        .translate(mean_x, mean_y)
    +
    +    ellipse.set_transform(transf + ax.transData)
    +    return ax.add_patch(ellipse)
    +
    +
    +# %%
    +#
    +# A helper function to create a correlated dataset
    +# """"""""""""""""""""""""""""""""""""""""""""""""
    +#
    +# Creates a random two-dimensional dataset with the specified
    +# two-dimensional mean (mu) and dimensions (scale).
    +# The correlation can be controlled by the param 'dependency',
    +# a 2x2 matrix.
    +
    +def get_correlated_dataset(n, dependency, mu, scale):
    +    latent = np.random.randn(n, 2)
    +    dependent = latent.dot(dependency)
    +    scaled = dependent * scale
    +    scaled_with_offset = scaled + mu
    +    # return x and y of the new, correlated dataset
    +    return scaled_with_offset[:, 0], scaled_with_offset[:, 1]
    +
    +
    +# %%
    +#
    +# Positive, negative and weak correlation
    +# """""""""""""""""""""""""""""""""""""""
    +#
    +# Note that the shape for the weak correlation (right) is an ellipse,
    +# not a circle because x and y are differently scaled.
    +# However, the fact that x and y are uncorrelated is shown by
    +# the axes of the ellipse being aligned with the x- and y-axis
    +# of the coordinate system.
    +
    +np.random.seed(0)
    +
    +PARAMETERS = {
    +    'Positive correlation': [[0.85, 0.35],
    +                             [0.15, -0.65]],
    +    'Negative correlation': [[0.9, -0.4],
    +                             [0.1, -0.6]],
    +    'Weak correlation': [[1, 0],
    +                         [0, 1]],
    +}
    +
    +mu = 2, 4
    +scale = 3, 5
    +
    +fig, axs = plt.subplots(1, 3, figsize=(9, 3))
    +for ax, (title, dependency) in zip(axs, PARAMETERS.items()):
    +    x, y = get_correlated_dataset(800, dependency, mu, scale)
    +    ax.scatter(x, y, s=0.5)
    +
    +    ax.axvline(c='grey', lw=1)
    +    ax.axhline(c='grey', lw=1)
    +
    +    confidence_ellipse(x, y, ax, edgecolor='red')
    +
    +    ax.scatter(mu[0], mu[1], c='red', s=3)
    +    ax.set_title(title)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# Different number of standard deviations
    +# """""""""""""""""""""""""""""""""""""""
    +#
    +# A plot with n_std = 3 (blue), 2 (purple) and 1 (red)
    +
    +fig, ax_nstd = plt.subplots(figsize=(6, 6))
    +
    +dependency_nstd = [[0.8, 0.75],
    +                   [-0.2, 0.35]]
    +mu = 0, 0
    +scale = 8, 5
    +
    +ax_nstd.axvline(c='grey', lw=1)
    +ax_nstd.axhline(c='grey', lw=1)
    +
    +x, y = get_correlated_dataset(500, dependency_nstd, mu, scale)
    +ax_nstd.scatter(x, y, s=0.5)
    +
    +confidence_ellipse(x, y, ax_nstd, n_std=1,
    +                   label=r'$1\sigma$', edgecolor='firebrick')
    +confidence_ellipse(x, y, ax_nstd, n_std=2,
    +                   label=r'$2\sigma$', edgecolor='fuchsia', linestyle='--')
    +confidence_ellipse(x, y, ax_nstd, n_std=3,
    +                   label=r'$3\sigma$', edgecolor='blue', linestyle=':')
    +
    +ax_nstd.scatter(mu[0], mu[1], c='red', s=3)
    +ax_nstd.set_title('Different standard deviations')
    +ax_nstd.legend()
    +plt.show()
    +
    +
    +# %%
    +#
    +# Using the keyword arguments
    +# """""""""""""""""""""""""""
    +#
    +# Use the keyword arguments specified for `matplotlib.patches.Patch` in order
    +# to have the ellipse rendered in different ways.
    +
    +fig, ax_kwargs = plt.subplots(figsize=(6, 6))
    +dependency_kwargs = [[-0.8, 0.5],
    +                     [-0.2, 0.5]]
    +mu = 2, -3
    +scale = 6, 5
    +
    +ax_kwargs.axvline(c='grey', lw=1)
    +ax_kwargs.axhline(c='grey', lw=1)
    +
    +x, y = get_correlated_dataset(500, dependency_kwargs, mu, scale)
    +# Plot the ellipse with zorder=0 in order to demonstrate
    +# its transparency (caused by the use of alpha).
    +confidence_ellipse(x, y, ax_kwargs,
    +                   alpha=0.5, facecolor='pink', edgecolor='purple', zorder=0)
    +
    +ax_kwargs.scatter(x, y, s=0.5)
    +ax_kwargs.scatter(mu[0], mu[1], c='red', s=3)
    +ax_kwargs.set_title('Using keyword arguments')
    +
    +fig.subplots_adjust(hspace=0.25)
    +plt.show()
    +
    +# %%
    +#
    +# .. tags::
    +#
    +#    plot-type: specialty
    +#    plot-type: scatter
    +#    component: ellipse
    +#    component: patch
    +#    domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.transforms.Affine2D`
    +#    - `matplotlib.patches.Ellipse`
    diff --git a/galleries/examples/statistics/csd_demo.py b/galleries/examples/statistics/csd_demo.py
    new file mode 100644
    index 000000000000..31c657f6b47f
    --- /dev/null
    +++ b/galleries/examples/statistics/csd_demo.py
    @@ -0,0 +1,49 @@
    +"""
    +============================
    +Cross spectral density (CSD)
    +============================
    +
    +Plot the cross spectral density (CSD) of two signals using `~.Axes.csd`.
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/csd_demo
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1, layout='constrained')
    +
    +dt = 0.01
    +t = np.arange(0, 30, dt)
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +nse1 = np.random.randn(len(t))                 # white noise 1
    +nse2 = np.random.randn(len(t))                 # white noise 2
    +r = np.exp(-t / 0.05)
    +
    +cnse1 = np.convolve(nse1, r, mode='same') * dt   # colored noise 1
    +cnse2 = np.convolve(nse2, r, mode='same') * dt   # colored noise 2
    +
    +# two signals with a coherent part and a random part
    +s1 = 0.01 * np.sin(2 * np.pi * 10 * t) + cnse1
    +s2 = 0.01 * np.sin(2 * np.pi * 10 * t) + cnse2
    +
    +ax1.plot(t, s1, t, s2)
    +ax1.set_xlim(0, 5)
    +ax1.set_xlabel('Time (s)')
    +ax1.set_ylabel('s1 and s2')
    +ax1.grid(True)
    +
    +cxy, f = ax2.csd(s1, s2, NFFT=256, Fs=1. / dt)
    +ax2.set_ylabel('CSD (dB)')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    domain: signal-processing
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/statistics/customized_violin.py b/galleries/examples/statistics/customized_violin.py
    new file mode 100644
    index 000000000000..cc18e47ebd67
    --- /dev/null
    +++ b/galleries/examples/statistics/customized_violin.py
    @@ -0,0 +1,94 @@
    +"""
    +=========================
    +Violin plot customization
    +=========================
    +
    +This example demonstrates how to fully customize violin plots. The first plot
    +shows the default style by providing only the data. The second plot first
    +limits what Matplotlib draws with additional keyword arguments. Then a
    +simplified representation of a box plot is drawn on top. Lastly, the styles of
    +the artists of the violins are modified.
    +
    +For more information on violin plots, the scikit-learn docs have a great
    +section: https://scikit-learn.org/stable/modules/density.html
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def adjacent_values(vals, q1, q3):
    +    upper_adjacent_value = q3 + (q3 - q1) * 1.5
    +    upper_adjacent_value = np.clip(upper_adjacent_value, q3, vals[-1])
    +
    +    lower_adjacent_value = q1 - (q3 - q1) * 1.5
    +    lower_adjacent_value = np.clip(lower_adjacent_value, vals[0], q1)
    +    return lower_adjacent_value, upper_adjacent_value
    +
    +
    +def set_axis_style(ax, labels):
    +    ax.set_xticks(np.arange(1, len(labels) + 1), labels=labels)
    +    ax.set_xlim(0.25, len(labels) + 0.75)
    +    ax.set_xlabel('Sample name')
    +
    +
    +# create test data
    +np.random.seed(19680801)
    +data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 5)]
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, figsize=(9, 3), sharey=True)
    +
    +ax1.set_title('Default violin plot')
    +ax1.set_ylabel('Observed values')
    +ax1.violinplot(data)
    +
    +ax2.set_title('Set colors of violins')
    +ax2.set_ylabel('Observed values')
    +ax2.violinplot(
    +    data,
    +    facecolor=[('yellow', 0.3), ('blue', 0.3), ('red', 0.3), ('green', 0.3)],
    +    linecolor='black',
    +)
    +# Note that when passing a sequence of colors, the method will repeat the sequence if
    +# less colors are provided than data distributions.
    +
    +ax3.set_title('Customized violin plot')
    +parts = ax3.violinplot(
    +        data, showmeans=False, showmedians=False, showextrema=False,
    +        facecolor='#D43F3A', linecolor='black')
    +
    +for pc in parts['bodies']:
    +    pc.set_edgecolor('black')
    +    pc.set_linewidth(1)
    +    pc.set_alpha(1)
    +
    +quartile1, medians, quartile3 = np.percentile(data, [25, 50, 75], axis=1)
    +whiskers = np.array([
    +    adjacent_values(sorted_array, q1, q3)
    +    for sorted_array, q1, q3 in zip(data, quartile1, quartile3)])
    +whiskers_min, whiskers_max = whiskers[:, 0], whiskers[:, 1]
    +
    +inds = np.arange(1, len(medians) + 1)
    +ax3.scatter(inds, medians, marker='o', color='white', s=30, zorder=3)
    +ax3.vlines(inds, quartile1, quartile3, color='k', linestyle='-', lw=5)
    +ax3.vlines(inds, whiskers_min, whiskers_max, color='k', linestyle='-', lw=1)
    +
    +# set style for the axes
    +labels = ['A', 'B', 'C', 'D']
    +for ax in [ax1, ax2, ax3]:
    +    set_axis_style(ax, labels)
    +
    +plt.subplots_adjust(bottom=0.15, wspace=0.05)
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: violin, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot`
    +#    - `matplotlib.axes.Axes.vlines` / `matplotlib.pyplot.vlines`
    diff --git a/galleries/examples/statistics/errorbar.py b/galleries/examples/statistics/errorbar.py
    new file mode 100644
    index 000000000000..019590dc3f32
    --- /dev/null
    +++ b/galleries/examples/statistics/errorbar.py
    @@ -0,0 +1,32 @@
    +"""
    +=================
    +Errorbar function
    +=================
    +
    +This exhibits the most basic use of the error bar method.
    +In this case, constant values are provided for the error
    +in both the x- and y-directions.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# example data
    +x = np.arange(0.1, 4, 0.5)
    +y = np.exp(-x)
    +
    +fig, ax = plt.subplots()
    +ax.errorbar(x, y, xerr=0.2, yerr=0.4)
    +plt.show()
    +
    +# %%
    +#
    +#
    +# .. tags:: plot-type: errorbar, domain: statistics,
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    diff --git a/galleries/examples/statistics/errorbar_features.py b/galleries/examples/statistics/errorbar_features.py
    new file mode 100644
    index 000000000000..8abaffcda537
    --- /dev/null
    +++ b/galleries/examples/statistics/errorbar_features.py
    @@ -0,0 +1,58 @@
    +"""
    +=======================================
    +Different ways of specifying error bars
    +=======================================
    +
    +Errors can be specified as a constant value (as shown in
    +:doc:`/gallery/statistics/errorbar`). However, this example demonstrates
    +how they vary by specifying arrays of error values.
    +
    +If the raw ``x`` and ``y`` data have length N, there are two options:
    +
    +Array of shape (N,):
    +    Error varies for each point, but the error values are
    +    symmetric (i.e. the lower and upper values are equal).
    +
    +Array of shape (2, N):
    +    Error varies for each point, and the lower and upper limits
    +    (in that order) are different (asymmetric case)
    +
    +In addition, this example demonstrates how to use log
    +scale with error bars.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# example data
    +x = np.arange(0.1, 4, 0.5)
    +y = np.exp(-x)
    +
    +# example error bar values that vary with x-position
    +error = 0.1 + 0.2 * x
    +
    +fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)
    +ax0.errorbar(x, y, yerr=error, fmt='-o')
    +ax0.set_title('variable, symmetric error')
    +
    +# error bar values w/ different -/+ errors that
    +# also vary with the x-position
    +lower_error = 0.4 * error
    +upper_error = error
    +asymmetric_error = [lower_error, upper_error]
    +
    +ax1.errorbar(x, y, xerr=asymmetric_error, fmt='o')
    +ax1.set_title('variable, asymmetric error')
    +ax1.set_yscale('log')
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: errorbar, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    diff --git a/galleries/examples/statistics/errorbar_limits.py b/galleries/examples/statistics/errorbar_limits.py
    new file mode 100644
    index 000000000000..fde18327af83
    --- /dev/null
    +++ b/galleries/examples/statistics/errorbar_limits.py
    @@ -0,0 +1,87 @@
    +"""
    +==============================================
    +Including upper and lower limits in error bars
    +==============================================
    +
    +In matplotlib, errors bars can have "limits". Applying limits to the
    +error bars essentially makes the error unidirectional. Because of that,
    +upper and lower limits can be applied in both the y- and x-directions
    +via the ``uplims``, ``lolims``, ``xuplims``, and ``xlolims`` parameters,
    +respectively. These parameters can be scalar or boolean arrays.
    +
    +For example, if ``xlolims`` is ``True``, the x-error bars will only
    +extend from the data towards increasing values. If ``uplims`` is an
    +array filled with ``False`` except for the 4th and 7th values, all of the
    +y-error bars will be bidirectional, except the 4th and 7th bars, which
    +will extend from the data towards decreasing y-values.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# example data
    +x = np.array([0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0])
    +y = np.exp(-x)
    +xerr = 0.1
    +yerr = 0.2
    +
    +# lower & upper limits of the error
    +lolims = np.array([0, 0, 1, 0, 1, 0, 0, 0, 1, 0], dtype=bool)
    +uplims = np.array([0, 1, 0, 0, 0, 1, 0, 0, 0, 1], dtype=bool)
    +ls = 'dotted'
    +
    +fig, ax = plt.subplots(figsize=(7, 4))
    +
    +# standard error bars
    +ax.errorbar(x, y, xerr=xerr, yerr=yerr, linestyle=ls)
    +
    +# including upper limits
    +ax.errorbar(x, y + 0.5, xerr=xerr, yerr=yerr, uplims=uplims,
    +            linestyle=ls)
    +
    +# including lower limits
    +ax.errorbar(x, y + 1.0, xerr=xerr, yerr=yerr, lolims=lolims,
    +            linestyle=ls)
    +
    +# including upper and lower limits
    +ax.errorbar(x, y + 1.5, xerr=xerr, yerr=yerr,
    +            lolims=lolims, uplims=uplims,
    +            marker='o', markersize=8,
    +            linestyle=ls)
    +
    +# Plot a series with lower and upper limits in both x & y
    +# constant x-error with varying y-error
    +xerr = 0.2
    +yerr = np.full_like(x, 0.2)
    +yerr[[3, 6]] = 0.3
    +
    +# mock up some limits by modifying previous data
    +xlolims = lolims
    +xuplims = uplims
    +lolims = np.zeros_like(x)
    +uplims = np.zeros_like(x)
    +lolims[[6]] = True  # only limited at this index
    +uplims[[3]] = True  # only limited at this index
    +
    +# do the plotting
    +ax.errorbar(x, y + 2.1, xerr=xerr, yerr=yerr,
    +            xlolims=xlolims, xuplims=xuplims,
    +            uplims=uplims, lolims=lolims,
    +            marker='o', markersize=8,
    +            linestyle='none')
    +
    +# tidy up the figure
    +ax.set_xlim(0, 5.5)
    +ax.set_title('Errorbar upper and lower limits')
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: errorbar, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    diff --git a/galleries/examples/statistics/errorbars_and_boxes.py b/galleries/examples/statistics/errorbars_and_boxes.py
    new file mode 100644
    index 000000000000..10face31f2d6
    --- /dev/null
    +++ b/galleries/examples/statistics/errorbars_and_boxes.py
    @@ -0,0 +1,89 @@
    +"""
    +==================================================
    +Create boxes from error bars using PatchCollection
    +==================================================
    +
    +In this example, we snazz up a pretty standard error bar plot by adding
    +a rectangle patch defined by the limits of the bars in both the x- and
    +y- directions. To do this, we have to write our own custom function
    +called ``make_error_boxes``. Close inspection of this function will
    +reveal the preferred pattern in writing functions for matplotlib:
    +
    +1. an `~.axes.Axes` object is passed directly to the function
    +2. the function operates on the ``Axes`` methods directly, not through
    +   the ``pyplot`` interface
    +3. plotting keyword arguments that could be abbreviated are spelled out for
    +   better code readability in the future (for example we use *facecolor*
    +   instead of *fc*)
    +4. the artists returned by the ``Axes`` plotting methods are then
    +   returned by the function so that, if desired, their styles
    +   can be modified later outside of the function (they are not
    +   modified in this example).
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.collections import PatchCollection
    +from matplotlib.patches import Rectangle
    +
    +# Number of data points
    +n = 5
    +
    +# Dummy data
    +np.random.seed(19680801)
    +x = np.arange(0, n, 1)
    +y = np.random.rand(n) * 5.
    +
    +# Dummy errors (above and below)
    +xerr = np.random.rand(2, n) + 0.1
    +yerr = np.random.rand(2, n) + 0.2
    +
    +
    +def make_error_boxes(ax, xdata, ydata, xerror, yerror, facecolor='r',
    +                     edgecolor='none', alpha=0.5):
    +
    +    # Loop over data points; create box from errors at each point
    +    errorboxes = [Rectangle((x - xe[0], y - ye[0]), xe.sum(), ye.sum())
    +                  for x, y, xe, ye in zip(xdata, ydata, xerror.T, yerror.T)]
    +
    +    # Create patch collection with specified colour/alpha
    +    pc = PatchCollection(errorboxes, facecolor=facecolor, alpha=alpha,
    +                         edgecolor=edgecolor)
    +
    +    # Add collection to Axes
    +    ax.add_collection(pc)
    +
    +    # Plot errorbars
    +    artists = ax.errorbar(xdata, ydata, xerr=xerror, yerr=yerror,
    +                          fmt='none', ecolor='k')
    +
    +    return artists
    +
    +
    +# Create figure and Axes
    +fig, ax = plt.subplots(1)
    +
    +# Call function to create error boxes
    +_ = make_error_boxes(ax, x, y, xerr, yerr)
    +
    +plt.show()
    +
    +# %%
    +#
    +#
    +# .. tags::
    +#
    +#    plot-type: errorbar
    +#    component: rectangle
    +#    component: patchcollection
    +#    domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.errorbar` / `matplotlib.pyplot.errorbar`
    +#    - `matplotlib.axes.Axes.add_collection`
    +#    - `matplotlib.collections.PatchCollection`
    diff --git a/galleries/examples/statistics/hexbin_demo.py b/galleries/examples/statistics/hexbin_demo.py
    new file mode 100644
    index 000000000000..bd1522772aae
    --- /dev/null
    +++ b/galleries/examples/statistics/hexbin_demo.py
    @@ -0,0 +1,45 @@
    +"""
    +=====================
    +Hexagonal binned plot
    +=====================
    +
    +`~.Axes.hexbin` is a 2D histogram plot, in which the bins are hexagons and
    +the color represents the number of data points within each bin.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +n = 100_000
    +x = np.random.standard_normal(n)
    +y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
    +xlim = x.min(), x.max()
    +ylim = y.min(), y.max()
    +
    +fig, (ax0, ax1) = plt.subplots(ncols=2, sharey=True, figsize=(9, 4))
    +
    +hb = ax0.hexbin(x, y, gridsize=50, cmap='inferno')
    +ax0.set(xlim=xlim, ylim=ylim)
    +ax0.set_title("Hexagon binning")
    +cb = fig.colorbar(hb, ax=ax0, label='counts')
    +
    +hb = ax1.hexbin(x, y, gridsize=50, bins='log', cmap='inferno')
    +ax1.set(xlim=xlim, ylim=ylim)
    +ax1.set_title("With a log color scale")
    +cb = fig.colorbar(hb, ax=ax1, label='counts')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: histogram, plot-type: hexbin, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hexbin` / `matplotlib.pyplot.hexbin`
    diff --git a/galleries/examples/statistics/hist.py b/galleries/examples/statistics/hist.py
    new file mode 100644
    index 000000000000..4c3294b369a8
    --- /dev/null
    +++ b/galleries/examples/statistics/hist.py
    @@ -0,0 +1,122 @@
    +"""
    +==========
    +Histograms
    +==========
    +
    +How to plot histograms with Matplotlib.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib import colors
    +from matplotlib.ticker import PercentFormatter
    +
    +# Create a random number generator with a fixed seed for reproducibility
    +rng = np.random.default_rng(19680801)
    +
    +# %%
    +# Generate data and plot a simple histogram
    +# -----------------------------------------
    +#
    +# To generate a 1D histogram we only need a single vector of numbers. For a 2D
    +# histogram we'll need a second vector. We'll generate both below, and show
    +# the histogram for each vector.
    +
    +N_points = 100000
    +n_bins = 20
    +
    +# Generate two normal distributions
    +dist1 = rng.standard_normal(N_points)
    +dist2 = 0.4 * rng.standard_normal(N_points) + 5
    +
    +fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True)
    +
    +# We can set the number of bins with the *bins* keyword argument.
    +axs[0].hist(dist1, bins=n_bins)
    +axs[1].hist(dist2, bins=n_bins)
    +
    +plt.show()
    +
    +
    +# %%
    +# Updating histogram colors
    +# -------------------------
    +#
    +# The histogram method returns (among other things) a ``patches`` object. This
    +# gives us access to the properties of the objects drawn. Using this, we can
    +# edit the histogram to our liking. Let's change the color of each bar
    +# based on its y value.
    +
    +fig, axs = plt.subplots(1, 2, tight_layout=True)
    +
    +# N is the count in each bin, bins is the lower-limit of the bin
    +N, bins, patches = axs[0].hist(dist1, bins=n_bins)
    +
    +# We'll color code by height, but you could use any scalar
    +fracs = N / N.max()
    +
    +# we need to normalize the data to 0..1 for the full range of the colormap
    +norm = colors.Normalize(fracs.min(), fracs.max())
    +
    +# Now, we'll loop through our objects and set the color of each accordingly
    +for thisfrac, thispatch in zip(fracs, patches):
    +    color = plt.colormaps["viridis"](norm(thisfrac))
    +    thispatch.set_facecolor(color)
    +
    +# We can also normalize our inputs by the total number of counts
    +axs[1].hist(dist1, bins=n_bins, density=True)
    +
    +# Now we format the y-axis to display percentage
    +axs[1].yaxis.set_major_formatter(PercentFormatter(xmax=1))
    +
    +
    +# %%
    +# Plot a 2D histogram
    +# -------------------
    +#
    +# To plot a 2D histogram, one only needs two vectors of the same length,
    +# corresponding to each axis of the histogram.
    +
    +fig, ax = plt.subplots(tight_layout=True)
    +hist = ax.hist2d(dist1, dist2)
    +
    +
    +# %%
    +# Customizing your histogram
    +# --------------------------
    +#
    +# Customizing a 2D histogram is similar to the 1D case, you can control
    +# visual components such as the bin size or color normalization.
    +
    +fig, axs = plt.subplots(3, 1, figsize=(5, 15), sharex=True, sharey=True,
    +                        tight_layout=True)
    +
    +# We can increase the number of bins on each axis
    +axs[0].hist2d(dist1, dist2, bins=40)
    +
    +# As well as define normalization of the colors
    +axs[1].hist2d(dist1, dist2, bins=40, norm=colors.LogNorm())
    +
    +# We can also define custom numbers of bins for each axis
    +axs[2].hist2d(dist1, dist2, bins=(80, 10), norm=colors.LogNorm())
    +
    +# %%
    +#
    +# .. tags::
    +#
    +#    plot-type: histogram,
    +#    plot-type: histogram2d
    +#    domain: statistics
    +#    styling: color,
    +#    component: normalization
    +#    component: patch
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`
    +#    - `matplotlib.pyplot.hist2d`
    +#    - `matplotlib.ticker.PercentFormatter`
    diff --git a/galleries/examples/statistics/histogram_bihistogram.py b/galleries/examples/statistics/histogram_bihistogram.py
    new file mode 100644
    index 000000000000..73f549493438
    --- /dev/null
    +++ b/galleries/examples/statistics/histogram_bihistogram.py
    @@ -0,0 +1,49 @@
    +"""
    +===========
    +Bihistogram
    +===========
    +
    +How to plot a bihistogram with Matplotlib.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Create a random number generator with a fixed seed for reproducibility
    +rng = np.random.default_rng(19680801)
    +
    +# %%
    +# Generate data and plot a bihistogram
    +# ------------------------------------
    +#
    +# To generate a bihistogram we need two datasets (each being a vector of numbers).
    +# We will plot both histograms using plt.hist() and set the weights of the second
    +# one to be negative. We'll generate data below and plot the bihistogram.
    +
    +N_points = 10_000
    +
    +# Generate two normal distributions
    +dataset1 = np.random.normal(0, 1, size=N_points)
    +dataset2 = np.random.normal(1, 2, size=N_points)
    +
    +# Use a constant bin width to make the two histograms easier to compare visually
    +bin_width = 0.25
    +bins = np.arange(np.min([dataset1, dataset2]),
    +                    np.max([dataset1, dataset2]) + bin_width, bin_width)
    +
    +fig, ax = plt.subplots()
    +
    +# Plot the first histogram
    +ax.hist(dataset1, bins=bins, label="Dataset 1")
    +
    +# Plot the second histogram
    +# (notice the negative weights, which flip the histogram upside down)
    +ax.hist(dataset2, weights=-np.ones_like(dataset2), bins=bins, label="Dataset 2")
    +ax.axhline(0, color="k")
    +ax.legend()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: histogram, domain: statistics, purpose: showcase
    diff --git a/galleries/examples/statistics/histogram_cumulative.py b/galleries/examples/statistics/histogram_cumulative.py
    new file mode 100644
    index 000000000000..7d6735d7b9a6
    --- /dev/null
    +++ b/galleries/examples/statistics/histogram_cumulative.py
    @@ -0,0 +1,78 @@
    +"""
    +========================
    +Cumulative distributions
    +========================
    +
    +This example shows how to plot the empirical cumulative distribution function
    +(ECDF) of a sample. We also show the theoretical CDF.
    +
    +In engineering, ECDFs are sometimes called "non-exceedance" curves: the y-value
    +for a given x-value gives probability that an observation from the sample is
    +below that x-value. For example, the value of 220 on the x-axis corresponds to
    +about 0.80 on the y-axis, so there is an 80% chance that an observation in the
    +sample does not exceed 220. Conversely, the empirical *complementary*
    +cumulative distribution function (the ECCDF, or "exceedance" curve) shows the
    +probability y that an observation from the sample is above a value x.
    +
    +A direct method to plot ECDFs is `.Axes.ecdf`.  Passing ``complementary=True``
    +results in an ECCDF instead.
    +
    +Alternatively, one can use ``ax.hist(data, density=True, cumulative=True)`` to
    +first bin the data, as if plotting a histogram, and then compute and plot the
    +cumulative sums of the frequencies of entries in each bin.  Here, to plot the
    +ECCDF, pass ``cumulative=-1``.  Note that this approach results in an
    +approximation of the E(C)CDF, whereas `.Axes.ecdf` is exact.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +mu = 200
    +sigma = 25
    +n_bins = 25
    +data = np.random.normal(mu, sigma, size=100)
    +
    +fig = plt.figure(figsize=(9, 4), layout="constrained")
    +axs = fig.subplots(1, 2, sharex=True, sharey=True)
    +
    +# Cumulative distributions.
    +axs[0].ecdf(data, label="CDF")
    +n, bins, patches = axs[0].hist(data, n_bins, density=True, histtype="step",
    +                               cumulative=True, label="Cumulative histogram")
    +x = np.linspace(data.min(), data.max())
    +y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
    +     np.exp(-0.5 * (1 / sigma * (x - mu))**2))
    +y = y.cumsum()
    +y /= y[-1]
    +axs[0].plot(x, y, "k--", linewidth=1.5, label="Theory")
    +
    +# Complementary cumulative distributions.
    +axs[1].ecdf(data, complementary=True, label="CCDF")
    +axs[1].hist(data, bins=bins, density=True, histtype="step", cumulative=-1,
    +            label="Reversed cumulative histogram")
    +axs[1].plot(x, 1 - y, "k--", linewidth=1.5, label="Theory")
    +
    +# Label the figure.
    +fig.suptitle("Cumulative distributions")
    +for ax in axs:
    +    ax.grid(True)
    +    ax.legend()
    +    ax.set_xlabel("Annual rainfall (mm)")
    +    ax.set_ylabel("Probability of occurrence")
    +    ax.label_outer()
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: ecdf, plot-type: histogram, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`
    +#    - `matplotlib.axes.Axes.ecdf` / `matplotlib.pyplot.ecdf`
    diff --git a/galleries/examples/statistics/histogram_histtypes.py b/galleries/examples/statistics/histogram_histtypes.py
    new file mode 100644
    index 000000000000..53d6425cf4dc
    --- /dev/null
    +++ b/galleries/examples/statistics/histogram_histtypes.py
    @@ -0,0 +1,61 @@
    +"""
    +================================================================
    +Demo of the histogram function's different ``histtype`` settings
    +================================================================
    +
    +* Histogram with step curve that has a color fill.
    +* Histogram with step curve with no fill.
    +* Histogram with custom and unequal bin widths.
    +* Two histograms with stacked bars.
    +
    +Selecting different bin counts and sizes can significantly affect the
    +shape of a histogram. The Astropy docs have a great section on how to
    +select these parameters:
    +http://docs.astropy.org/en/stable/visualization/histogram.html
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +mu_x = 200
    +sigma_x = 25
    +x = np.random.normal(mu_x, sigma_x, size=100)
    +
    +mu_w = 200
    +sigma_w = 10
    +w = np.random.normal(mu_w, sigma_w, size=100)
    +
    +fig, axs = plt.subplots(nrows=2, ncols=2)
    +
    +axs[0, 0].hist(x, 20, density=True, histtype='stepfilled', facecolor='g',
    +               alpha=0.75)
    +axs[0, 0].set_title('stepfilled')
    +
    +axs[0, 1].hist(x, 20, density=True, histtype='step', facecolor='g',
    +               alpha=0.75)
    +axs[0, 1].set_title('step')
    +
    +axs[1, 0].hist(x, density=True, histtype='barstacked', rwidth=0.8)
    +axs[1, 0].hist(w, density=True, histtype='barstacked', rwidth=0.8)
    +axs[1, 0].set_title('barstacked')
    +
    +# Create a histogram by providing the bin edges (unequally spaced).
    +bins = [100, 150, 180, 195, 205, 220, 250, 300]
    +axs[1, 1].hist(x, bins, density=True, histtype='bar', rwidth=0.8)
    +axs[1, 1].set_title('bar, unequal bins')
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: histogram, domain: statistics, purpose: reference
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`
    diff --git a/galleries/examples/statistics/histogram_multihist.py b/galleries/examples/statistics/histogram_multihist.py
    new file mode 100644
    index 000000000000..a85ec2acfa8d
    --- /dev/null
    +++ b/galleries/examples/statistics/histogram_multihist.py
    @@ -0,0 +1,148 @@
    +"""
    +=====================================================
    +The histogram (hist) function with multiple data sets
    +=====================================================
    +
    +Plot histogram with multiple sample sets and demonstrate:
    +
    +* Use of legend with multiple sample sets
    +* Stacked bars
    +* Step curve with no fill
    +* Data sets of different sample sizes
    +
    +Selecting different bin counts and sizes can significantly affect the
    +shape of a histogram. The Astropy docs have a great section on how to
    +select these parameters:
    +http://docs.astropy.org/en/stable/visualization/histogram.html
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/filled_step
    +
    +"""
    +# %%
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +n_bins = 10
    +x = np.random.randn(1000, 3)
    +
    +fig, ((ax0, ax1), (ax2, ax3)) = plt.subplots(nrows=2, ncols=2)
    +
    +colors = ['red', 'tan', 'lime']
    +ax0.hist(x, n_bins, density=True, histtype='bar', color=colors, label=colors)
    +ax0.legend(prop={'size': 10})
    +ax0.set_title('bars with legend')
    +
    +ax1.hist(x, n_bins, density=True, histtype='bar', stacked=True)
    +ax1.set_title('stacked bar')
    +
    +ax2.hist(x, n_bins, histtype='step', stacked=True, fill=False)
    +ax2.set_title('stack step (unfilled)')
    +
    +# Make a multiple-histogram of data-sets with different length.
    +x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]]
    +ax3.hist(x_multi, n_bins, histtype='bar')
    +ax3.set_title('different sample sizes')
    +
    +fig.tight_layout()
    +plt.show()
    +
    +# %%
    +# -----------------------------------
    +# Setting properties for each dataset
    +# -----------------------------------
    +#
    +# You can style the histograms individually by passing a list of values to the
    +# following parameters:
    +#
    +# * edgecolor
    +# * facecolor
    +# * hatch
    +# * linewidth
    +# * linestyle
    +#
    +#
    +# edgecolor
    +# .........
    +
    +fig, ax = plt.subplots()
    +
    +edgecolors = ['green', 'red', 'blue']
    +
    +ax.hist(x, n_bins, fill=False, histtype="step", stacked=True,
    +        edgecolor=edgecolors, label=edgecolors)
    +ax.legend()
    +ax.set_title('Stacked Steps with Edgecolors')
    +
    +plt.show()
    +
    +# %%
    +# facecolor
    +# .........
    +
    +fig, ax = plt.subplots()
    +
    +facecolors = ['green', 'red', 'blue']
    +
    +ax.hist(x, n_bins, histtype="barstacked", facecolor=facecolors, label=facecolors)
    +ax.legend()
    +ax.set_title("Bars with different Facecolors")
    +
    +plt.show()
    +
    +# %%
    +# hatch
    +# .....
    +
    +fig, ax = plt.subplots()
    +
    +hatches = [".", "o", "x"]
    +
    +ax.hist(x, n_bins, histtype="barstacked", hatch=hatches, label=hatches)
    +ax.legend()
    +ax.set_title("Hatches on Stacked Bars")
    +
    +plt.show()
    +
    +# %%
    +# linewidth
    +# .........
    +
    +fig, ax = plt.subplots()
    +
    +linewidths = [1, 2, 3]
    +edgecolors = ["green", "red", "blue"]
    +
    +ax.hist(x, n_bins, fill=False, histtype="bar", linewidth=linewidths,
    +        edgecolor=edgecolors, label=linewidths)
    +ax.legend()
    +ax.set_title("Bars with Linewidths")
    +
    +plt.show()
    +
    +# %%
    +# linestyle
    +# .........
    +
    +fig, ax = plt.subplots()
    +
    +linestyles = ['-', ':', '--']
    +
    +ax.hist(x, n_bins, fill=False, histtype='bar', linestyle=linestyles,
    +        edgecolor=edgecolors, label=linestyles)
    +ax.legend()
    +ax.set_title('Bars with Linestyles')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: histogram, domain: statistics, purpose: reference
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`
    diff --git a/galleries/examples/statistics/histogram_normalization.py b/galleries/examples/statistics/histogram_normalization.py
    new file mode 100644
    index 000000000000..2c423edad208
    --- /dev/null
    +++ b/galleries/examples/statistics/histogram_normalization.py
    @@ -0,0 +1,257 @@
    +"""
    +.. redirect-from:: /gallery/statistics/histogram_features
    +
    +===================================
    +Histogram bins, density, and weight
    +===================================
    +
    +The `.Axes.hist` method can flexibly create histograms in a few different ways,
    +which is flexible and helpful, but can also lead to confusion.  In particular,
    +you can:
    +
    +- bin the data as you want, either with an automatically chosen number of
    +  bins, or with fixed bin edges,
    +- normalize the histogram so that its integral is one,
    +- and assign weights to the data points, so that each data point affects the
    +  count in its bin differently.
    +
    +The Matplotlib ``hist`` method calls `numpy.histogram` and plots the results,
    +therefore users should consult the numpy documentation for a definitive guide.
    +
    +Histograms are created by defining bin edges, and taking a dataset of values
    +and sorting them into the bins, and counting or summing how much data is in
    +each bin.  In this simple example, 9 numbers between 1 and 4 are sorted into 3
    +bins:
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +rng = np.random.default_rng(19680801)
    +
    +xdata = np.array([1.2, 2.3, 3.3, 3.1, 1.7, 3.4, 2.1, 1.25, 1.3])
    +xbins = np.array([1, 2, 3, 4])
    +
    +# changing the style of the histogram bars just to make it
    +# very clear where the boundaries of the bins are:
    +style = {'facecolor': 'none', 'edgecolor': 'C0', 'linewidth': 3}
    +
    +fig, ax = plt.subplots()
    +ax.hist(xdata, bins=xbins, **style)
    +
    +# plot the xdata locations on the x axis:
    +ax.plot(xdata, 0*xdata, 'd')
    +ax.set_ylabel('Number per bin')
    +ax.set_xlabel('x bins (dx=1.0)')
    +
    +# %%
    +# Modifying bins
    +# ==============
    +#
    +# Changing the bin size changes the shape of this sparse histogram, so its a
    +# good idea to choose bins with some care with respect to your data.  Here we
    +# make the bins half as wide.
    +
    +xbins = np.arange(1, 4.5, 0.5)
    +
    +fig, ax = plt.subplots()
    +ax.hist(xdata, bins=xbins, **style)
    +ax.plot(xdata, 0*xdata, 'd')
    +ax.set_ylabel('Number per bin')
    +ax.set_xlabel('x bins (dx=0.5)')
    +
    +# %%
    +# We can also let numpy (via Matplotlib) choose the bins automatically, or
    +# specify a number of bins to choose automatically:
    +
    +fig, ax = plt.subplot_mosaic([['auto', 'n4']],
    +                             sharex=True, sharey=True, layout='constrained')
    +
    +ax['auto'].hist(xdata, **style)
    +ax['auto'].plot(xdata, 0*xdata, 'd')
    +ax['auto'].set_ylabel('Number per bin')
    +ax['auto'].set_xlabel('x bins (auto)')
    +
    +ax['n4'].hist(xdata, bins=4, **style)
    +ax['n4'].plot(xdata, 0*xdata, 'd')
    +ax['n4'].set_xlabel('x bins ("bins=4")')
    +
    +# %%
    +# Normalizing histograms: density and weight
    +# ==========================================
    +#
    +# Counts-per-bin is the default length of each bar in the histogram.  However,
    +# we can also normalize the bar lengths as a probability density function using
    +# the ``density`` parameter:
    +
    +fig, ax = plt.subplots()
    +ax.hist(xdata, bins=xbins, density=True, **style)
    +ax.set_ylabel('Probability density [$V^{-1}$])')
    +ax.set_xlabel('x bins (dx=0.5 $V$)')
    +
    +# %%
    +# This normalization can be a little hard to interpret when just exploring the
    +# data. The value attached to each bar is divided by the total number of data
    +# points *and* the width of the bin, and thus the values _integrate_ to one
    +# when integrating across the full range of data.
    +# e.g. ::
    +#
    +#     density = counts / (sum(counts) * np.diff(bins))
    +#     np.sum(density * np.diff(bins)) == 1
    +#
    +# This normalization is how `probability density functions
    +# `_ are defined in
    +# statistics.  If :math:`X` is a random variable on :math:`x`, then :math:`f_X`
    +# is is the probability density function if :math:`P[a`_, and also calculate the
    +# known probability density function:
    +
    +xdata = rng.normal(size=1000)
    +xpdf = np.arange(-4, 4, 0.1)
    +pdf = 1 / (np.sqrt(2 * np.pi)) * np.exp(-xpdf**2 / 2)
    +
    +# %%
    +# If we don't use ``density=True``, we need to scale the expected probability
    +# distribution function by both the length of the data and the width of the
    +# bins:
    +
    +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained')
    +dx = 0.1
    +xbins = np.arange(-4, 4, dx)
    +ax['False'].hist(xdata, bins=xbins, density=False, histtype='step', label='Counts')
    +
    +# scale and plot the expected pdf:
    +ax['False'].plot(xpdf, pdf * len(xdata) * dx, label=r'$N\,f_X(x)\,\delta x$')
    +ax['False'].set_ylabel('Count per bin')
    +ax['False'].set_xlabel('x bins [V]')
    +ax['False'].legend()
    +
    +ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label='density')
    +ax['True'].plot(xpdf, pdf, label='$f_X(x)$')
    +ax['True'].set_ylabel('Probability density [$V^{-1}$]')
    +ax['True'].set_xlabel('x bins [$V$]')
    +ax['True'].legend()
    +
    +# %%
    +# One advantage of using the density is therefore that the shape and amplitude
    +# of the histogram does not depend on the size of the bins.  Consider an
    +# extreme case where the bins do not have the same width.  In this example, the
    +# bins below ``x=-1.25`` are six times wider than the rest of the bins.   By
    +# normalizing by density, we preserve the shape of the distribution, whereas if
    +# we do not, then the wider bins have much higher counts than the thinner bins:
    +
    +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained')
    +dx = 0.1
    +xbins = np.hstack([np.arange(-4, -1.25, 6*dx), np.arange(-1.25, 4, dx)])
    +ax['False'].hist(xdata, bins=xbins, density=False, histtype='step', label='Counts')
    +ax['False'].plot(xpdf, pdf * len(xdata) * dx, label=r'$N\,f_X(x)\,\delta x_0$')
    +ax['False'].set_ylabel('Count per bin')
    +ax['False'].set_xlabel('x bins [V]')
    +ax['False'].legend()
    +
    +ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label='density')
    +ax['True'].plot(xpdf, pdf, label='$f_X(x)$')
    +ax['True'].set_ylabel('Probability density [$V^{-1}$]')
    +ax['True'].set_xlabel('x bins [$V$]')
    +ax['True'].legend()
    +
    +# %%
    +# Similarly, if we want to compare histograms with different bin widths, we may
    +# want to use ``density=True``:
    +
    +fig, ax = plt.subplot_mosaic([['False', 'True']], layout='constrained')
    +
    +# expected PDF
    +ax['True'].plot(xpdf, pdf, '--', label='$f_X(x)$', color='k')
    +
    +for nn, dx in enumerate([0.1, 0.4, 1.2]):
    +    xbins = np.arange(-4, 4, dx)
    +    # expected histogram:
    +    ax['False'].plot(xpdf, pdf*1000*dx, '--', color=f'C{nn}')
    +    ax['False'].hist(xdata, bins=xbins, density=False, histtype='step')
    +
    +    ax['True'].hist(xdata, bins=xbins, density=True, histtype='step', label=dx)
    +
    +# Labels:
    +ax['False'].set_xlabel('x bins [$V$]')
    +ax['False'].set_ylabel('Count per bin')
    +ax['True'].set_ylabel('Probability density [$V^{-1}$]')
    +ax['True'].set_xlabel('x bins [$V$]')
    +ax['True'].legend(fontsize='small', title='bin width:')
    +
    +# %%
    +# Sometimes people want to normalize so that the sum of counts is one.  This is
    +# analogous to a `probability mass function
    +# `_ for a discrete
    +# variable where the sum of probabilities for all the values equals one.  Using
    +# ``hist``, we can get this normalization if we set the *weights* to 1/N.
    +# Note that the amplitude of this normalized histogram still depends on
    +# width and/or number of the bins:
    +
    +fig, ax = plt.subplots(layout='constrained', figsize=(3.5, 3))
    +
    +for nn, dx in enumerate([0.1, 0.4, 1.2]):
    +    xbins = np.arange(-4, 4, dx)
    +    ax.hist(xdata, bins=xbins, weights=1/len(xdata) * np.ones(len(xdata)),
    +                   histtype='step', label=f'{dx}')
    +ax.set_xlabel('x bins [$V$]')
    +ax.set_ylabel('Bin count / N')
    +ax.legend(fontsize='small', title='bin width:')
    +
    +# %%
    +# The value of normalizing histograms is comparing two distributions that have
    +# different sized populations.  Here we compare the distribution of ``xdata``
    +# with a population of 1000, and ``xdata2`` with 100 members.
    +
    +xdata2 = rng.normal(size=100)
    +
    +fig, ax = plt.subplot_mosaic([['no_norm', 'density', 'weight']],
    +                             layout='constrained', figsize=(8, 4))
    +
    +xbins = np.arange(-4, 4, 0.25)
    +
    +ax['no_norm'].hist(xdata, bins=xbins, histtype='step')
    +ax['no_norm'].hist(xdata2, bins=xbins, histtype='step')
    +ax['no_norm'].set_ylabel('Counts')
    +ax['no_norm'].set_xlabel('x bins [$V$]')
    +ax['no_norm'].set_title('No normalization')
    +
    +ax['density'].hist(xdata, bins=xbins, histtype='step', density=True)
    +ax['density'].hist(xdata2, bins=xbins, histtype='step', density=True)
    +ax['density'].set_ylabel('Probability density [$V^{-1}$]')
    +ax['density'].set_title('Density=True')
    +ax['density'].set_xlabel('x bins [$V$]')
    +
    +ax['weight'].hist(xdata, bins=xbins, histtype='step',
    +                  weights=1 / len(xdata) * np.ones(len(xdata)),
    +                  label='N=1000')
    +ax['weight'].hist(xdata2, bins=xbins, histtype='step',
    +                  weights=1 / len(xdata2) * np.ones(len(xdata2)),
    +                  label='N=100')
    +ax['weight'].set_xlabel('x bins [$V$]')
    +ax['weight'].set_ylabel('Counts / N')
    +ax['weight'].legend(fontsize='small')
    +ax['weight'].set_title('Weight = 1/N')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: histogram, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`
    +#    - `matplotlib.axes.Axes.set_title`
    +#    - `matplotlib.axes.Axes.set_xlabel`
    +#    - `matplotlib.axes.Axes.set_ylabel`
    +#    - `matplotlib.axes.Axes.legend`
    diff --git a/examples/statistics/multiple_histograms_side_by_side.py b/galleries/examples/statistics/multiple_histograms_side_by_side.py
    similarity index 76%
    rename from examples/statistics/multiple_histograms_side_by_side.py
    rename to galleries/examples/statistics/multiple_histograms_side_by_side.py
    index b150f8b53197..684faa62a904 100644
    --- a/examples/statistics/multiple_histograms_side_by_side.py
    +++ b/galleries/examples/statistics/multiple_histograms_side_by_side.py
    @@ -1,7 +1,7 @@
     """
    -=======================================================
    -Demo of how to produce multiple histograms side by side
    -=======================================================
    +================================
    +Multiple histograms side by side
    +================================
     
     This example plots horizontal histograms of different samples along
     a categorical x-axis. Additionally, the histograms are plotted to
    @@ -9,7 +9,7 @@
     to violin plots.
     
     To make this highly specialized plot, we can't use the standard ``hist``
    -method. Instead we use ``barh`` to draw the horizontal bars directly. The
    +method. Instead, we use ``barh`` to draw the horizontal bars directly. The
     vertical positions and lengths of the bars are computed via the
     ``np.histogram`` function. The histograms for all the samples are
     computed using the same range (min and max values) and number of bins,
    @@ -21,10 +21,10 @@
     http://docs.astropy.org/en/stable/visualization/histogram.html
     """
     
    -import numpy as np
     import matplotlib.pyplot as plt
    +import numpy as np
     
    -np.random.seed(0)
    +np.random.seed(19680801)
     number_of_bins = 20
     
     # An example of three data sets to compare
    @@ -45,8 +45,8 @@
     
     # The bin_edges are the same for all of the histograms
     bin_edges = np.linspace(hist_range[0], hist_range[1], number_of_bins + 1)
    -centers = 0.5 * (bin_edges + np.roll(bin_edges, 1))[:-1]
     heights = np.diff(bin_edges)
    +centers = bin_edges[:-1] + heights / 2
     
     # Cycle through and plot each histogram
     fig, ax = plt.subplots()
    @@ -54,10 +54,25 @@
         lefts = x_loc - 0.5 * binned_data
         ax.barh(centers, binned_data, height=heights, left=lefts)
     
    -ax.set_xticks(x_locations)
    -ax.set_xticklabels(labels)
    +ax.set_xticks(x_locations, labels)
     
     ax.set_ylabel("Data values")
     ax.set_xlabel("Data sets")
     
     plt.show()
    +
    +# %%
    +#
    +# .. tags::
    +#
    +#    domain: statistics
    +#    plot-type: barh
    +#    plot-type: histogram
    +#    styling: position
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.barh` / `matplotlib.pyplot.barh`
    diff --git a/galleries/examples/statistics/psd_demo.py b/galleries/examples/statistics/psd_demo.py
    new file mode 100644
    index 000000000000..bf564df7542c
    --- /dev/null
    +++ b/galleries/examples/statistics/psd_demo.py
    @@ -0,0 +1,189 @@
    +"""
    +============================
    +Power spectral density (PSD)
    +============================
    +
    +Plotting power spectral density (PSD) using `~.Axes.psd`.
    +
    +The PSD is a common plot in the field of signal processing. NumPy has
    +many useful libraries for computing a PSD. Below we demo a few examples
    +of how this can be accomplished and visualized with Matplotlib.
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/psd_demo
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.mlab as mlab
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +dt = 0.01
    +t = np.arange(0, 10, dt)
    +nse = np.random.randn(len(t))
    +r = np.exp(-t / 0.05)
    +
    +cnse = np.convolve(nse, r) * dt
    +cnse = cnse[:len(t)]
    +s = 0.1 * np.sin(2 * np.pi * t) + cnse
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1, layout='constrained')
    +ax0.plot(t, s)
    +ax0.set_xlabel('Time (s)')
    +ax0.set_ylabel('Signal')
    +ax1.psd(s, NFFT=512, Fs=1 / dt)
    +
    +plt.show()
    +
    +# %%
    +# Compare this with the equivalent Matlab code to accomplish the same thing::
    +#
    +#     dt = 0.01;
    +#     t = [0:dt:10];
    +#     nse = randn(size(t));
    +#     r = exp(-t/0.05);
    +#     cnse = conv(nse, r)*dt;
    +#     cnse = cnse(1:length(t));
    +#     s = 0.1*sin(2*pi*t) + cnse;
    +#
    +#     subplot(211)
    +#     plot(t, s)
    +#     subplot(212)
    +#     psd(s, 512, 1/dt)
    +#
    +# Below we'll show a slightly more complex example that demonstrates
    +# how padding affects the resulting PSD.
    +
    +dt = np.pi / 100.
    +fs = 1. / dt
    +t = np.arange(0, 8, dt)
    +y = 10. * np.sin(2 * np.pi * 4 * t) + 5. * np.sin(2 * np.pi * 4.25 * t)
    +y = y + np.random.randn(*t.shape)
    +
    +# Plot the raw time series
    +fig, axs = plt.subplot_mosaic([
    +    ['signal', 'signal', 'signal'],
    +    ['zero padding', 'block size', 'overlap'],
    +], layout='constrained')
    +
    +axs['signal'].plot(t, y)
    +axs['signal'].set_xlabel('Time (s)')
    +axs['signal'].set_ylabel('Signal')
    +
    +# Plot the PSD with different amounts of zero padding. This uses the entire
    +# time series at once
    +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t), Fs=fs)
    +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t) * 2, Fs=fs)
    +axs['zero padding'].psd(y, NFFT=len(t), pad_to=len(t) * 4, Fs=fs)
    +
    +# Plot the PSD with different block sizes, Zero pad to the length of the
    +# original data sequence.
    +axs['block size'].psd(y, NFFT=len(t), pad_to=len(t), Fs=fs)
    +axs['block size'].psd(y, NFFT=len(t) // 2, pad_to=len(t), Fs=fs)
    +axs['block size'].psd(y, NFFT=len(t) // 4, pad_to=len(t), Fs=fs)
    +axs['block size'].set_ylabel('')
    +
    +# Plot the PSD with different amounts of overlap between blocks
    +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t), noverlap=0, Fs=fs)
    +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t),
    +                   noverlap=int(0.025 * len(t)), Fs=fs)
    +axs['overlap'].psd(y, NFFT=len(t) // 2, pad_to=len(t),
    +                   noverlap=int(0.1 * len(t)), Fs=fs)
    +axs['overlap'].set_ylabel('')
    +axs['overlap'].set_title('overlap')
    +
    +for title, ax in axs.items():
    +    if title == 'signal':
    +        continue
    +
    +    ax.set_title(title)
    +    ax.sharex(axs['zero padding'])
    +    ax.sharey(axs['zero padding'])
    +
    +plt.show()
    +
    +
    +# %%
    +# This is a ported version of a MATLAB example from the signal
    +# processing toolbox that showed some difference at one time between
    +# Matplotlib's and MATLAB's scaling of the PSD.
    +
    +fs = 1000
    +t = np.linspace(0, 0.3, 301)
    +A = np.array([2, 8]).reshape(-1, 1)
    +f = np.array([150, 140]).reshape(-1, 1)
    +xn = (A * np.sin(2 * np.pi * f * t)).sum(axis=0)
    +xn += 5 * np.random.randn(*t.shape)
    +
    +fig, (ax0, ax1) = plt.subplots(ncols=2, layout='constrained')
    +
    +yticks = np.arange(-50, 30, 10)
    +yrange = (yticks[0], yticks[-1])
    +xticks = np.arange(0, 550, 100)
    +
    +ax0.psd(xn, NFFT=301, Fs=fs, window=mlab.window_none, pad_to=1024,
    +        scale_by_freq=True)
    +ax0.set_title('Periodogram')
    +ax0.set_yticks(yticks)
    +ax0.set_xticks(xticks)
    +ax0.grid(True)
    +ax0.set_ylim(yrange)
    +
    +ax1.psd(xn, NFFT=150, Fs=fs, window=mlab.window_none, pad_to=512, noverlap=75,
    +        scale_by_freq=True)
    +ax1.set_title('Welch')
    +ax1.set_xticks(xticks)
    +ax1.set_yticks(yticks)
    +ax1.set_ylabel('')  # overwrite the y-label added by `psd`
    +ax1.grid(True)
    +ax1.set_ylim(yrange)
    +
    +plt.show()
    +
    +# %%
    +# This is a ported version of a MATLAB example from the signal
    +# processing toolbox that showed some difference at one time between
    +# Matplotlib's and MATLAB's scaling of the PSD.
    +#
    +# It uses a complex signal so we can see that complex PSD's work properly.
    +
    +prng = np.random.RandomState(19680801)  # to ensure reproducibility
    +
    +fs = 1000
    +t = np.linspace(0, 0.3, 301)
    +A = np.array([2, 8]).reshape(-1, 1)
    +f = np.array([150, 140]).reshape(-1, 1)
    +xn = (A * np.exp(2j * np.pi * f * t)).sum(axis=0) + 5 * prng.randn(*t.shape)
    +
    +fig, (ax0, ax1) = plt.subplots(ncols=2, layout='constrained')
    +
    +yticks = np.arange(-50, 30, 10)
    +yrange = (yticks[0], yticks[-1])
    +xticks = np.arange(-500, 550, 200)
    +
    +ax0.psd(xn, NFFT=301, Fs=fs, window=mlab.window_none, pad_to=1024,
    +        scale_by_freq=True)
    +ax0.set_title('Periodogram')
    +ax0.set_yticks(yticks)
    +ax0.set_xticks(xticks)
    +ax0.grid(True)
    +ax0.set_ylim(yrange)
    +
    +ax1.psd(xn, NFFT=150, Fs=fs, window=mlab.window_none, pad_to=512, noverlap=75,
    +        scale_by_freq=True)
    +ax1.set_title('Welch')
    +ax1.set_xticks(xticks)
    +ax1.set_yticks(yticks)
    +ax1.set_ylabel('')  # overwrite the y-label added by `psd`
    +ax1.grid(True)
    +ax1.set_ylim(yrange)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    domain: signal-processing
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/statistics/time_series_histogram.py b/galleries/examples/statistics/time_series_histogram.py
    new file mode 100644
    index 000000000000..9caacd7f52c9
    --- /dev/null
    +++ b/galleries/examples/statistics/time_series_histogram.py
    @@ -0,0 +1,111 @@
    +"""
    +=====================
    +Time Series Histogram
    +=====================
    +
    +This example demonstrates how to efficiently visualize large numbers of time
    +series in a way that could potentially reveal hidden substructure and patterns
    +that are not immediately obvious, and display them in a visually appealing way.
    +
    +In this example, we generate multiple sinusoidal "signal" series that are
    +buried under a larger number of random walk "noise/background" series. For an
    +unbiased Gaussian random walk with standard deviation of σ, the RMS deviation
    +from the origin after n steps is σ*sqrt(n). So in order to keep the sinusoids
    +visible on the same scale as the random walks, we scale the amplitude by the
    +random walk RMS. In addition, we also introduce a small random offset ``phi``
    +to shift the sines left/right, and some additive random noise to shift
    +individual data points up/down to make the signal a bit more "realistic" (you
    +wouldn't expect a perfect sine wave to appear in your data).
    +
    +The first plot shows the typical way of visualizing multiple time series by
    +overlaying them on top of each other with ``plt.plot`` and a small value of
    +``alpha``. The second and third plots show how to reinterpret the data as a 2d
    +histogram, with optional interpolation between data points, by using
    +``np.histogram2d`` and ``plt.pcolormesh``.
    +"""
    +
    +import time
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, axes = plt.subplots(nrows=3, figsize=(6, 8), layout='constrained')
    +
    +# Fix random state for reproducibility
    +np.random.seed(19680801)
    +# Make some data; a 1D random walk + small fraction of sine waves
    +num_series = 1000
    +num_points = 100
    +SNR = 0.10  # Signal to Noise Ratio
    +x = np.linspace(0, 4 * np.pi, num_points)
    +# Generate unbiased Gaussian random walks
    +Y = np.cumsum(np.random.randn(num_series, num_points), axis=-1)
    +# Generate sinusoidal signals
    +num_signal = round(SNR * num_series)
    +phi = (np.pi / 8) * np.random.randn(num_signal, 1)  # small random offset
    +Y[-num_signal:] = (
    +    np.sqrt(np.arange(num_points))  # random walk RMS scaling factor
    +    * (np.sin(x - phi)
    +       + 0.05 * np.random.randn(num_signal, num_points))  # small random noise
    +)
    +
    +
    +# Plot series using `plot` and a small value of `alpha`. With this view it is
    +# very difficult to observe the sinusoidal behavior because of how many
    +# overlapping series there are. It also takes a bit of time to run because so
    +# many individual artists need to be generated.
    +tic = time.time()
    +axes[0].plot(x, Y.T, color="C0", alpha=0.1)
    +toc = time.time()
    +axes[0].set_title("Line plot with alpha")
    +print(f"{toc-tic:.3f} sec. elapsed")
    +
    +
    +# Now we will convert the multiple time series into a histogram. Not only will
    +# the hidden signal be more visible, but it is also a much quicker procedure.
    +tic = time.time()
    +# Linearly interpolate between the points in each time series
    +num_fine = 800
    +x_fine = np.linspace(x.min(), x.max(), num_fine)
    +y_fine = np.concatenate([np.interp(x_fine, x, y_row) for y_row in Y])
    +x_fine = np.broadcast_to(x_fine, (num_series, num_fine)).ravel()
    +
    +
    +# Plot (x, y) points in 2d histogram with log colorscale
    +# It is pretty evident that there is some kind of structure under the noise
    +# You can tune vmax to make signal more visible
    +cmap = plt.colormaps["plasma"]
    +cmap = cmap.with_extremes(bad=cmap(0))
    +h, xedges, yedges = np.histogram2d(x_fine, y_fine, bins=[400, 100])
    +pcm = axes[1].pcolormesh(xedges, yedges, h.T, cmap=cmap,
    +                         norm="log", vmax=1.5e2, rasterized=True)
    +fig.colorbar(pcm, ax=axes[1], label="# points", pad=0)
    +axes[1].set_title("2d histogram and log color scale")
    +
    +# Same data but on linear color scale
    +pcm = axes[2].pcolormesh(xedges, yedges, h.T, cmap=cmap,
    +                         vmax=1.5e2, rasterized=True)
    +fig.colorbar(pcm, ax=axes[2], label="# points", pad=0)
    +axes[2].set_title("2d histogram and linear color scale")
    +
    +toc = time.time()
    +print(f"{toc-tic:.3f} sec. elapsed")
    +plt.show()
    +
    +# %%
    +#
    +# .. tags::
    +#
    +#    plot-type: histogram2d
    +#    plot-type: pcolormesh
    +#    purpose: storytelling
    +#    styling: color
    +#    styling: colormap
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`
    +#    - `matplotlib.figure.Figure.colorbar`
    diff --git a/galleries/examples/statistics/violinplot.py b/galleries/examples/statistics/violinplot.py
    new file mode 100644
    index 000000000000..7f4725ff7a8c
    --- /dev/null
    +++ b/galleries/examples/statistics/violinplot.py
    @@ -0,0 +1,116 @@
    +"""
    +==================
    +Violin plot basics
    +==================
    +
    +Violin plots are similar to histograms and box plots in that they show
    +an abstract representation of the probability distribution of the
    +sample. Rather than showing counts of data points that fall into bins
    +or order statistics, violin plots use kernel density estimation (KDE) to
    +compute an empirical distribution of the sample. That computation
    +is controlled by several parameters. This example demonstrates how to
    +modify the number of points at which the KDE is evaluated (``points``)
    +and how to modify the bandwidth of the KDE (``bw_method``).
    +
    +For more information on violin plots and KDE, the scikit-learn docs
    +have a great section: https://scikit-learn.org/stable/modules/density.html
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +# fake data
    +fs = 10  # fontsize
    +pos = [1, 2, 4, 5, 7, 8]
    +data = [np.random.normal(0, std, size=100) for std in pos]
    +
    +fig, axs = plt.subplots(nrows=2, ncols=6, figsize=(10, 4))
    +
    +axs[0, 0].violinplot(data, pos, points=20, widths=0.3,
    +                     showmeans=True, showextrema=True, showmedians=True)
    +axs[0, 0].set_title('Custom violin 1', fontsize=fs)
    +
    +axs[0, 1].violinplot(data, pos, points=40, widths=0.5,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     bw_method='silverman')
    +axs[0, 1].set_title('Custom violin 2', fontsize=fs)
    +
    +axs[0, 2].violinplot(data, pos, points=60, widths=0.7, showmeans=True,
    +                     showextrema=True, showmedians=True, bw_method=0.5)
    +axs[0, 2].set_title('Custom violin 3', fontsize=fs)
    +
    +axs[0, 3].violinplot(data, pos, points=60, widths=0.7, showmeans=True,
    +                     showextrema=True, showmedians=True, bw_method=0.5,
    +                     quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]])
    +axs[0, 3].set_title('Custom violin 4', fontsize=fs)
    +
    +axs[0, 4].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5)
    +axs[0, 4].set_title('Custom violin 5', fontsize=fs)
    +
    +axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low')
    +
    +axs[0, 5].violinplot(data[-1:], pos[-1:], points=60, widths=0.7,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
    +axs[0, 5].set_title('Custom violin 6', fontsize=fs)
    +
    +axs[1, 0].violinplot(data, pos, points=80, orientation='horizontal', widths=0.7,
    +                     showmeans=True, showextrema=True, showmedians=True)
    +axs[1, 0].set_title('Custom violin 7', fontsize=fs)
    +
    +axs[1, 1].violinplot(data, pos, points=100, orientation='horizontal', widths=0.9,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     bw_method='silverman')
    +axs[1, 1].set_title('Custom violin 8', fontsize=fs)
    +
    +axs[1, 2].violinplot(data, pos, points=200, orientation='horizontal', widths=1.1,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     bw_method=0.5)
    +axs[1, 2].set_title('Custom violin 9', fontsize=fs)
    +
    +axs[1, 3].violinplot(data, pos, points=200, orientation='horizontal', widths=1.1,
    +                     showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[[0.1], [], [], [0.175, 0.954], [0.75], [0.25]],
    +                     bw_method=0.5)
    +axs[1, 3].set_title('Custom violin 10', fontsize=fs)
    +
    +axs[1, 4].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
    +                     widths=1.1, showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5)
    +axs[1, 4].set_title('Custom violin 11', fontsize=fs)
    +
    +axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
    +                     widths=1.1, showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='low')
    +
    +axs[1, 5].violinplot(data[-1:], pos[-1:], points=200, orientation='horizontal',
    +                     widths=1.1, showmeans=True, showextrema=True, showmedians=True,
    +                     quantiles=[0.05, 0.1, 0.8, 0.9], bw_method=0.5, side='high')
    +axs[1, 5].set_title('Custom violin 12', fontsize=fs)
    +
    +
    +for ax in axs.flat:
    +    ax.set_yticklabels([])
    +
    +fig.suptitle("Violin Plotting Examples")
    +fig.subplots_adjust(hspace=0.4)
    +plt.show()
    +
    +# %%
    +#
    +# .. tags:: plot-type: violin, domain: statistics
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.violinplot` / `matplotlib.pyplot.violinplot`
    diff --git a/galleries/examples/statistics/xcorr_acorr_demo.py b/galleries/examples/statistics/xcorr_acorr_demo.py
    new file mode 100644
    index 000000000000..f0cd0ecaf8ed
    --- /dev/null
    +++ b/galleries/examples/statistics/xcorr_acorr_demo.py
    @@ -0,0 +1,43 @@
    +"""
    +===========================
    +Cross- and auto-correlation
    +===========================
    +
    +Example use of cross-correlation (`~.Axes.xcorr`) and auto-correlation
    +(`~.Axes.acorr`) plots.
    +
    +.. redirect-from:: /gallery/lines_bars_and_markers/xcorr_acorr_demo
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +x, y = np.random.randn(2, 100)
    +fig, [ax1, ax2] = plt.subplots(2, 1, sharex=True)
    +ax1.xcorr(x, y, usevlines=True, maxlags=50, normed=True, lw=2)
    +ax1.grid(True)
    +ax1.set_title('Cross-correlation (xcorr)')
    +
    +ax2.acorr(x, usevlines=True, normed=True, maxlags=50, lw=2)
    +ax2.grid(True)
    +ax2.set_title('Auto-correlation (acorr)')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.acorr` / `matplotlib.pyplot.acorr`
    +#    - `matplotlib.axes.Axes.xcorr` / `matplotlib.pyplot.xcorr`
    +#
    +# .. tags::
    +#
    +#    domain: statistics
    +#    level: beginner
    diff --git a/galleries/examples/style_sheets/README.txt b/galleries/examples/style_sheets/README.txt
    new file mode 100644
    index 000000000000..671ff3bf3067
    --- /dev/null
    +++ b/galleries/examples/style_sheets/README.txt
    @@ -0,0 +1,4 @@
    +.. _style_sheet_examples:
    +
    +Style sheets
    +============
    diff --git a/galleries/examples/style_sheets/bmh.py b/galleries/examples/style_sheets/bmh.py
    new file mode 100644
    index 000000000000..941a21a3030f
    --- /dev/null
    +++ b/galleries/examples/style_sheets/bmh.py
    @@ -0,0 +1,33 @@
    +"""
    +========================================
    +Bayesian Methods for Hackers style sheet
    +========================================
    +
    +This example demonstrates the style used in the Bayesian Methods for Hackers
    +[1]_ online book.
    +
    +.. [1] http://camdavidsonpilon.github.io/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +plt.style.use('bmh')
    +
    +
    +def plot_beta_hist(ax, a, b):
    +    ax.hist(np.random.beta(a, b, size=10000),
    +            histtype="stepfilled", bins=25, alpha=0.8, density=True)
    +
    +
    +fig, ax = plt.subplots()
    +plot_beta_hist(ax, 10, 10)
    +plot_beta_hist(ax, 4, 12)
    +plot_beta_hist(ax, 50, 12)
    +plot_beta_hist(ax, 6, 55)
    +ax.set_title("'bmh' style sheet")
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/dark_background.py b/galleries/examples/style_sheets/dark_background.py
    new file mode 100644
    index 000000000000..e142521c6460
    --- /dev/null
    +++ b/galleries/examples/style_sheets/dark_background.py
    @@ -0,0 +1,28 @@
    +"""
    +===========================
    +Dark background style sheet
    +===========================
    +
    +This example demonstrates the "dark_background" style, which uses white for
    +elements that are typically black (text, borders, etc). Note that not all plot
    +elements default to colors defined by an rc parameter.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +plt.style.use('dark_background')
    +
    +fig, ax = plt.subplots()
    +
    +L = 6
    +x = np.linspace(0, L)
    +ncolors = len(plt.rcParams['axes.prop_cycle'])
    +shift = np.linspace(0, L, ncolors, endpoint=False)
    +for s in shift:
    +    ax.plot(x, np.sin(x + s), 'o-')
    +ax.set_xlabel('x-axis')
    +ax.set_ylabel('y-axis')
    +ax.set_title("'dark_background' style sheet")
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/fivethirtyeight.py b/galleries/examples/style_sheets/fivethirtyeight.py
    new file mode 100644
    index 000000000000..45dd851e81d6
    --- /dev/null
    +++ b/galleries/examples/style_sheets/fivethirtyeight.py
    @@ -0,0 +1,30 @@
    +"""
    +===========================
    +FiveThirtyEight style sheet
    +===========================
    +
    +This shows an example of the "fivethirtyeight" styling, which
    +tries to replicate the styles from FiveThirtyEight.com.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +plt.style.use('fivethirtyeight')
    +
    +x = np.linspace(0, 10)
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, ax = plt.subplots()
    +
    +ax.plot(x, np.sin(x) + x + np.random.randn(50))
    +ax.plot(x, np.sin(x) + 0.5 * x + np.random.randn(50))
    +ax.plot(x, np.sin(x) + 2 * x + np.random.randn(50))
    +ax.plot(x, np.sin(x) - 0.5 * x + np.random.randn(50))
    +ax.plot(x, np.sin(x) - 2 * x + np.random.randn(50))
    +ax.plot(x, np.sin(x) + np.random.randn(50))
    +ax.set_title("'fivethirtyeight' style sheet")
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/ggplot.py b/galleries/examples/style_sheets/ggplot.py
    new file mode 100644
    index 000000000000..51a103f5f970
    --- /dev/null
    +++ b/galleries/examples/style_sheets/ggplot.py
    @@ -0,0 +1,57 @@
    +"""
    +==================
    +ggplot style sheet
    +==================
    +
    +This example demonstrates the "ggplot" style, which adjusts the style to
    +emulate ggplot_ (a popular plotting package for R_).
    +
    +These settings were shamelessly stolen from [1]_ (with permission).
    +
    +.. [1] https://everyhue.me/posts/sane-color-scheme-for-matplotlib/
    +
    +.. _ggplot: https://ggplot2.tidyverse.org/
    +.. _R: https://www.r-project.org/
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +plt.style.use('ggplot')
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, axs = plt.subplots(ncols=2, nrows=2)
    +ax1, ax2, ax3, ax4 = axs.flat
    +
    +# scatter plot (Note: `plt.scatter` doesn't use default colors)
    +x, y = np.random.normal(size=(2, 200))
    +ax1.plot(x, y, 'o')
    +
    +# sinusoidal lines with colors from default color cycle
    +L = 2*np.pi
    +x = np.linspace(0, L)
    +ncolors = len(plt.rcParams['axes.prop_cycle'])
    +shift = np.linspace(0, L, ncolors, endpoint=False)
    +for s in shift:
    +    ax2.plot(x, np.sin(x + s), '-')
    +ax2.margins(0)
    +
    +# bar graphs
    +x = np.arange(5)
    +y1, y2 = np.random.randint(1, 25, size=(2, 5))
    +width = 0.25
    +ax3.bar(x, y1, width)
    +ax3.bar(x + width, y2, width,
    +        color=list(plt.rcParams['axes.prop_cycle'])[2]['color'])
    +ax3.set_xticks(x + width, labels=['a', 'b', 'c', 'd', 'e'])
    +
    +# circles with colors from default color cycle
    +for i, color in enumerate(plt.rcParams['axes.prop_cycle']):
    +    xy = np.random.normal(size=2)
    +    ax4.add_patch(plt.Circle(xy, radius=0.3, color=color['color']))
    +ax4.axis('equal')
    +ax4.margins(0)
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/grayscale.py b/galleries/examples/style_sheets/grayscale.py
    new file mode 100644
    index 000000000000..e2c3788e94b3
    --- /dev/null
    +++ b/galleries/examples/style_sheets/grayscale.py
    @@ -0,0 +1,41 @@
    +"""
    +=====================
    +Grayscale style sheet
    +=====================
    +
    +This example demonstrates the "grayscale" style sheet, which changes all colors
    +that are defined as `.rcParams` to grayscale. Note, however, that not all
    +plot elements respect `.rcParams`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def color_cycle_example(ax):
    +    L = 6
    +    x = np.linspace(0, L)
    +    ncolors = len(plt.rcParams['axes.prop_cycle'])
    +    shift = np.linspace(0, L, ncolors, endpoint=False)
    +    for s in shift:
    +        ax.plot(x, np.sin(x + s), 'o-')
    +
    +
    +def image_and_patch_example(ax):
    +    ax.imshow(np.random.random(size=(20, 20)), interpolation='none')
    +    c = plt.Circle((5, 5), radius=5, label='patch')
    +    ax.add_patch(c)
    +
    +
    +plt.style.use('grayscale')
    +
    +fig, (ax1, ax2) = plt.subplots(ncols=2)
    +fig.suptitle("'grayscale' style sheet")
    +
    +color_cycle_example(ax1)
    +image_and_patch_example(ax2)
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/petroff10.py b/galleries/examples/style_sheets/petroff10.py
    new file mode 100644
    index 000000000000..5683a4df296c
    --- /dev/null
    +++ b/galleries/examples/style_sheets/petroff10.py
    @@ -0,0 +1,49 @@
    +"""
    +====================
    +Petroff style sheets
    +====================
    +
    +This example demonstrates the "petroffN" styles, which implement the 6-, 8- and
    +10-color sequences developed by Matthew A. Petroff [1]_ for accessible data
    +visualization.  The styles balance aesthetics with accessibility considerations,
    +making them suitable for various types of plots while ensuring readability and
    +distinction between data series.
    +
    +.. [1] https://arxiv.org/abs/2107.02270
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def colored_lines_example(ax):
    +    t = np.linspace(-10, 10, 100)
    +    nb_colors = len(plt.rcParams['axes.prop_cycle'])
    +    shifts = np.linspace(-5, 5, nb_colors)
    +    amplitudes = np.linspace(1, 1.5, nb_colors)
    +    for t0, a in zip(shifts, amplitudes):
    +        y = a / (1 + np.exp(-(t - t0)))
    +        line, = ax.plot(t, y, '-')
    +        point_indices = np.linspace(0, len(t) - 1, 20, dtype=int)
    +        ax.plot(t[point_indices], y[point_indices], 'o', color=line.get_color())
    +    ax.set_xlim(-10, 10)
    +
    +
    +def image_and_patch_example(ax):
    +    ax.imshow(np.random.random(size=(20, 20)), interpolation='none')
    +    c = plt.Circle((5, 5), radius=5, label='patch')
    +    ax.add_patch(c)
    +
    +
    +fig = plt.figure(figsize=(6.4, 9.6), layout='compressed')
    +sfigs = fig.subfigures(nrows=3)
    +
    +for style, sfig in zip(['petroff6', 'petroff8', 'petroff10'], sfigs):
    +    sfig.suptitle(f"'{style}' style sheet")
    +    with plt.style.context(style):
    +        ax1, ax2 = sfig.subplots(ncols=2)
    +        colored_lines_example(ax1)
    +        image_and_patch_example(ax2)
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/plot_solarizedlight2.py b/galleries/examples/style_sheets/plot_solarizedlight2.py
    new file mode 100644
    index 000000000000..a6434f5ef04a
    --- /dev/null
    +++ b/galleries/examples/style_sheets/plot_solarizedlight2.py
    @@ -0,0 +1,45 @@
    +"""
    +==========================
    +Solarized Light stylesheet
    +==========================
    +
    +This shows an example of "Solarized_Light" styling, which
    +tries to replicate the styles of:
    +
    +- https://ethanschoonover.com/solarized/
    +- https://github.com/jrnold/ggthemes
    +- http://www.pygal.org/en/stable/documentation/builtin_styles.html#light-solarized
    +
    +and work of:
    +
    +- https://github.com/tonysyu/mpltools
    +
    +using all 8 accents of the color palette - starting with blue
    +
    +Still TODO:
    +
    +- Create alpha values for bar and stacked charts. .33 or .5
    +- Apply Layout Rules
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +x = np.linspace(0, 10)
    +with plt.style.context('Solarize_Light2'):
    +    plt.plot(x, np.sin(x) + x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 2 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 3 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 4 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 5 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 6 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 7 * x + np.random.randn(50))
    +    plt.plot(x, np.sin(x) + 8 * x + np.random.randn(50))
    +    # Number of accent colors in the color scheme
    +    plt.title('8 Random Lines - Line')
    +    plt.xlabel('x label', fontsize=14)
    +    plt.ylabel('y label', fontsize=14)
    +
    +plt.show()
    diff --git a/galleries/examples/style_sheets/style_sheets_reference.py b/galleries/examples/style_sheets/style_sheets_reference.py
    new file mode 100644
    index 000000000000..43b9c4f941ee
    --- /dev/null
    +++ b/galleries/examples/style_sheets/style_sheets_reference.py
    @@ -0,0 +1,174 @@
    +"""
    +======================
    +Style sheets reference
    +======================
    +
    +This script demonstrates the different available style sheets on a
    +common set of example plots: scatter plot, image, bar graph, patches,
    +line plot and histogram.
    +
    +Any of these style sheets can be imported (i.e. activated) by its name.
    +For example for the ggplot style:
    +
    +>>> plt.style.use('ggplot')
    +
    +The names of the available style sheets can be found
    +in the list `matplotlib.style.available`
    +(they are also printed in the corner of each plot below).
    +
    +See more details in :ref:`Customizing Matplotlib
    +using style sheets`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.colors as mcolors
    +from matplotlib.patches import Rectangle
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +def plot_scatter(ax, prng, nb_samples=100):
    +    """Scatter plot."""
    +    for mu, sigma, marker in [(-.5, 0.75, 'o'), (0.75, 1., 's')]:
    +        x, y = prng.normal(loc=mu, scale=sigma, size=(2, nb_samples))
    +        ax.plot(x, y, ls='none', marker=marker)
    +    ax.set_xlabel('X-label')
    +    ax.set_title('Axes title')
    +    return ax
    +
    +
    +def plot_colored_lines(ax):
    +    """Plot lines with colors following the style color cycle."""
    +    t = np.linspace(-10, 10, 100)
    +
    +    def sigmoid(t, t0):
    +        return 1 / (1 + np.exp(-(t - t0)))
    +
    +    nb_colors = len(plt.rcParams['axes.prop_cycle'])
    +    shifts = np.linspace(-5, 5, nb_colors)
    +    amplitudes = np.linspace(1, 1.5, nb_colors)
    +    for t0, a in zip(shifts, amplitudes):
    +        ax.plot(t, a * sigmoid(t, t0), '-')
    +    ax.set_xlim(-10, 10)
    +    return ax
    +
    +
    +def plot_bar_graphs(ax, prng, min_value=5, max_value=25, nb_samples=5):
    +    """Plot two bar graphs side by side, with letters as x-tick labels."""
    +    x = np.arange(nb_samples)
    +    ya, yb = prng.randint(min_value, max_value, size=(2, nb_samples))
    +    width = 0.25
    +    ax.bar(x, ya, width)
    +    ax.bar(x + width, yb, width, color='C2')
    +    ax.set_xticks(x + width, labels=['a', 'b', 'c', 'd', 'e'])
    +    return ax
    +
    +
    +def plot_colored_circles(ax, prng, nb_samples=15):
    +    """
    +    Plot circle patches.
    +
    +    NB: draws a fixed amount of samples, rather than using the length of
    +    the color cycle, because different styles may have different numbers
    +    of colors.
    +    """
    +    for sty_dict, j in zip(plt.rcParams['axes.prop_cycle'](),
    +                           range(nb_samples)):
    +        ax.add_patch(plt.Circle(prng.normal(scale=3, size=2),
    +                                radius=1.0, color=sty_dict['color']))
    +    ax.grid(visible=True)
    +
    +    # Add title for enabling grid
    +    plt.title('ax.grid(True)', family='monospace', fontsize='small')
    +
    +    ax.set_xlim([-4, 8])
    +    ax.set_ylim([-5, 6])
    +    ax.set_aspect('equal', adjustable='box')  # to plot circles as circles
    +    return ax
    +
    +
    +def plot_image_and_patch(ax, prng, size=(20, 20)):
    +    """Plot an image with random values and superimpose a circular patch."""
    +    values = prng.random_sample(size=size)
    +    ax.imshow(values, interpolation='none')
    +    c = plt.Circle((5, 5), radius=5, label='patch')
    +    ax.add_patch(c)
    +    # Remove ticks
    +    ax.set_xticks([])
    +    ax.set_yticks([])
    +
    +
    +def plot_histograms(ax, prng, nb_samples=10000):
    +    """Plot 4 histograms and a text annotation."""
    +    params = ((10, 10), (4, 12), (50, 12), (6, 55))
    +    for a, b in params:
    +        values = prng.beta(a, b, size=nb_samples)
    +        ax.hist(values, histtype="stepfilled", bins=30,
    +                alpha=0.8, density=True)
    +
    +    # Add a small annotation.
    +    ax.annotate('Annotation', xy=(0.25, 4.25),
    +                xytext=(0.9, 0.9), textcoords=ax.transAxes,
    +                va="top", ha="right",
    +                bbox=dict(boxstyle="round", alpha=0.2),
    +                arrowprops=dict(
    +                          arrowstyle="->",
    +                          connectionstyle="angle,angleA=-95,angleB=35,rad=10"),
    +                )
    +    return ax
    +
    +
    +def plot_figure(style_label=""):
    +    """Setup and plot the demonstration figure with a given style."""
    +    # Use a dedicated RandomState instance to draw the same "random" values
    +    # across the different figures.
    +    prng = np.random.RandomState(96917002)
    +
    +    fig, axs = plt.subplots(ncols=6, nrows=1, num=style_label,
    +                            figsize=(14.8, 2.8), layout='constrained')
    +
    +    # make a suptitle, in the same style for all subfigures,
    +    # except those with dark backgrounds, which get a lighter color:
    +    background_color = mcolors.rgb_to_hsv(
    +        mcolors.to_rgb(plt.rcParams['figure.facecolor']))[2]
    +    if background_color < 0.5:
    +        title_color = [0.8, 0.8, 1]
    +    else:
    +        title_color = np.array([19, 6, 84]) / 256
    +    fig.suptitle(style_label, x=0.01, ha='left', color=title_color,
    +                 fontsize=14, fontfamily='DejaVu Sans', fontweight='normal')
    +
    +    plot_scatter(axs[0], prng)
    +    plot_image_and_patch(axs[1], prng)
    +    plot_bar_graphs(axs[2], prng)
    +    plot_colored_lines(axs[3])
    +    plot_histograms(axs[4], prng)
    +    plot_colored_circles(axs[5], prng)
    +
    +    # add divider
    +    rec = Rectangle((1 + 0.025, -2), 0.05, 16,
    +                    clip_on=False, color='gray')
    +
    +    axs[4].add_artist(rec)
    +
    +if __name__ == "__main__":
    +
    +    # Set up a list of all available styles, in alphabetical order but
    +    # the `default` and `classic` ones, which will be forced resp. in
    +    # first and second position.
    +    # styles with leading underscores are for internal use such as testing
    +    # and plot types gallery. These are excluded here.
    +    style_list = ['default', 'classic'] + sorted(
    +        style for style in plt.style.available
    +        if style != 'classic' and not style.startswith('_'))
    +
    +    # Plot a demonstration figure for every available style sheet.
    +    for style_label in style_list:
    +        with plt.rc_context({"figure.max_open_warning": len(style_list)}):
    +            with plt.style.context(style_label):
    +                plot_figure(style_label=style_label)
    +
    +    plt.show()
    diff --git a/galleries/examples/subplots_axes_and_figures/README.txt b/galleries/examples/subplots_axes_and_figures/README.txt
    new file mode 100644
    index 000000000000..4ed26f5ef72f
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/README.txt
    @@ -0,0 +1,4 @@
    +.. _subplots_axes_and_figures_examples:
    +
    +Subplots, axes and figures
    +==========================
    diff --git a/galleries/examples/subplots_axes_and_figures/align_labels_demo.py b/galleries/examples/subplots_axes_and_figures/align_labels_demo.py
    new file mode 100644
    index 000000000000..f4a0cec18933
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/align_labels_demo.py
    @@ -0,0 +1,76 @@
    +"""
    +=======================
    +Align labels and titles
    +=======================
    +
    +Aligning xlabel, ylabel, and title using `.Figure.align_xlabels`,
    +`.Figure.align_ylabels`, and `.Figure.align_titles`.
    +
    +`.Figure.align_labels` wraps the x and y label functions.
    +
    +We align the xlabels and ylabels using short calls to `.Figure.align_xlabels`
    +and `.Figure.align_ylabels`. We also show a manual way to align the ylabels
    +using the `~.Axis.set_label_coords` method of the yaxis object. Note this requires
    +knowing a good offset value which is hardcoded.
    +
    +.. redirect-from:: /gallery/pyplots/align_ylabels
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, axs = plt.subplots(2, 3, figsize=(8.9, 5.5),
    +                        layout='constrained', gridspec_kw={'wspace': 0.1})
    +
    +# add sample data and labels
    +for ax in axs.flat:
    +    scale = 2000 if ax.get_subplotspec().is_first_row() else 1
    +    ax.plot(scale * (1 - np.exp(-np.linspace(0, 5, 100))))
    +    if ax.get_subplotspec().is_last_row():
    +        ax.set_xlabel('xlabel', bbox=dict(facecolor='yellow', pad=5, alpha=0.2))
    +    ax.set_ylabel('ylabel', bbox=dict(facecolor='yellow', pad=5, alpha=0.2))
    +    ax.set_ylim(0, scale)
    +
    +# Modify ticks to get different margins in some plots
    +axs[0, 0].xaxis.tick_top()
    +axs[1, 2].tick_params(axis='x', rotation=55)
    +axs[0, 0].set_title('ylabels not aligned')
    +
    +# Align labels
    +fig.align_titles()            # Align titles
    +fig.align_xlabels()           # Align all x-axis labels
    +fig.align_ylabels(axs[:, 1])  # Align only the second column's y-labels
    +axs[0, 1].set_title('fig.align_ylabels()')
    +
    +# Manually adjust y-labels for the third column
    +for ax in axs[:, 2]:
    +    ax.yaxis.set_label_coords(-0.3, 0.5)
    +axs[0, 2].set_title('ylabels manually aligned')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.align_xlabels`
    +#    - `matplotlib.figure.Figure.align_ylabels`
    +#    - `matplotlib.figure.Figure.align_labels`
    +#    - `matplotlib.figure.Figure.align_titles`
    +#    - `matplotlib.axis.Axis.set_label_coords`
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.axes.Axes.set_title`
    +#    - `matplotlib.axes.Axes.set_ylabel`
    +#    - `matplotlib.axes.Axes.set_ylim`
    +
    +# %%
    +# .. tags::
    +#
    +#    component: label
    +#    component: title
    +#    styling: position
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py b/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py
    new file mode 100644
    index 000000000000..ec865798d648
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/auto_subplots_adjust.py
    @@ -0,0 +1,94 @@
    +"""
    +===========================================
    +Programmatically control subplot adjustment
    +===========================================
    +
    +.. note::
    +
    +    This example is primarily intended to show some advanced concepts in
    +    Matplotlib.
    +
    +    If you are only looking for having enough space for your labels, it is
    +    almost always simpler and good enough to either set the subplot parameters
    +    manually using `.Figure.subplots_adjust`, or use one of the automatic
    +    layout mechanisms
    +    (:ref:`constrainedlayout_guide` or
    +    :ref:`tight_layout_guide`).
    +
    +This example describes a user-defined way to read out Artist sizes and
    +set the subplot parameters accordingly. Its main purpose is to illustrate
    +some advanced concepts like reading out text positions, working with
    +bounding boxes and transforms and using
    +:ref:`events `. But it can also serve as a starting
    +point if you want to automate the layouting and need more flexibility than
    +tight layout and constrained layout.
    +
    +Below, we collect the bounding boxes of all y-labels and move the left border
    +of the subplot to the right so that it leaves enough room for the union of all
    +the bounding boxes.
    +
    +There's one catch with calculating text bounding boxes:
    +Querying the text bounding boxes (`.Text.get_window_extent`) needs a
    +renderer (`.RendererBase` instance), to calculate the text size. This renderer
    +is only available after the figure has been drawn (`.Figure.draw`).
    +
    +A solution to this is putting the adjustment logic in a draw callback.
    +This function is executed after the figure has been drawn. It can now check
    +if the subplot leaves enough room for the text. If not, the subplot parameters
    +are updated and second draw is triggered.
    +
    +.. redirect-from:: /gallery/pyplots/auto_subplots_adjust
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.transforms as mtransforms
    +
    +fig, ax = plt.subplots()
    +ax.plot(range(10))
    +ax.set_yticks([2, 5, 7], labels=['really, really, really', 'long', 'labels'])
    +
    +
    +def on_draw(event):
    +    bboxes = []
    +    for label in ax.get_yticklabels():
    +        # Bounding box in pixels
    +        bbox_px = label.get_window_extent()
    +        # Transform to relative figure coordinates. This is the inverse of
    +        # transFigure.
    +        bbox_fig = bbox_px.transformed(fig.transFigure.inverted())
    +        bboxes.append(bbox_fig)
    +    # the bbox that bounds all the bboxes, again in relative figure coords
    +    bbox = mtransforms.Bbox.union(bboxes)
    +    if fig.subplotpars.left < bbox.width:
    +        # Move the subplot left edge more to the right
    +        fig.subplots_adjust(left=1.1*bbox.width)  # pad a little
    +        fig.canvas.draw()
    +
    +
    +fig.canvas.mpl_connect('draw_event', on_draw)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.artist.Artist.get_window_extent`
    +#    - `matplotlib.transforms.Bbox`
    +#    - `matplotlib.transforms.BboxBase.transformed`
    +#    - `matplotlib.transforms.BboxBase.union`
    +#    - `matplotlib.transforms.Transform.inverted`
    +#    - `matplotlib.figure.Figure.subplots_adjust`
    +#    - `matplotlib.gridspec.SubplotParams`
    +#    - `matplotlib.backend_bases.FigureCanvasBase.mpl_connect`
    +#
    +# .. tags::
    +#
    +#    component: subplot
    +#    plot-type: line
    +#    styling: position
    +#    level: advanced
    diff --git a/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py b/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py
    new file mode 100644
    index 000000000000..6bd6723b27d6
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axes_box_aspect.py
    @@ -0,0 +1,162 @@
    +"""
    +===============
    +Axes box aspect
    +===============
    +
    +This demo shows how to set the aspect of an Axes box directly via
    +`~.Axes.set_box_aspect`. The box aspect is the ratio between Axes height
    +and Axes width in physical units, independent of the data limits.
    +This is useful to e.g. produce a square plot, independent of the data it
    +contains, or to have a usual plot with the same axes dimensions next to
    +an image plot with fixed (data-)aspect.
    +
    +The following lists a few use cases for `~.Axes.set_box_aspect`.
    +"""
    +
    +# %%
    +# A square Axes, independent of data
    +# ----------------------------------
    +#
    +# Produce a square Axes, no matter what the data limits are.
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig1, ax = plt.subplots()
    +
    +ax.set_xlim(300, 400)
    +ax.set_box_aspect(1)
    +
    +plt.show()
    +
    +# %%
    +# Shared square Axes
    +# ------------------
    +#
    +# Produce shared subplots that are squared in size.
    +#
    +fig2, (ax, ax2) = plt.subplots(ncols=2, sharey=True)
    +
    +ax.plot([1, 5], [0, 10])
    +ax2.plot([100, 500], [10, 15])
    +
    +ax.set_box_aspect(1)
    +ax2.set_box_aspect(1)
    +
    +plt.show()
    +
    +# %%
    +# Square twin Axes
    +# ----------------
    +#
    +# Produce a square Axes, with a twin Axes. The twinned Axes takes over the
    +# box aspect of the parent.
    +#
    +
    +fig3, ax = plt.subplots()
    +
    +ax2 = ax.twinx()
    +
    +ax.plot([0, 10])
    +ax2.plot([12, 10])
    +
    +ax.set_box_aspect(1)
    +
    +plt.show()
    +
    +
    +# %%
    +# Normal plot next to image
    +# -------------------------
    +#
    +# When creating an image plot with fixed data aspect and the default
    +# ``adjustable="box"`` next to a normal plot, the Axes would be unequal in
    +# height. `~.Axes.set_box_aspect` provides an easy solution to that by allowing
    +# to have the normal plot's Axes use the images dimensions as box aspect.
    +#
    +# This example also shows that *constrained layout* interplays nicely with
    +# a fixed box aspect.
    +
    +fig4, (ax, ax2) = plt.subplots(ncols=2, layout="constrained")
    +
    +np.random.seed(19680801)  # Fixing random state for reproducibility
    +im = np.random.rand(16, 27)
    +ax.imshow(im)
    +
    +ax2.plot([23, 45])
    +ax2.set_box_aspect(im.shape[0]/im.shape[1])
    +
    +plt.show()
    +
    +# %%
    +# Square joint/marginal plot
    +# --------------------------
    +#
    +# It may be desirable to show marginal distributions next to a plot of joint
    +# data. The following creates a square plot with the box aspect of the
    +# marginal Axes being equal to the width- and height-ratios of the gridspec.
    +# This ensures that all Axes align perfectly, independent on the size of the
    +# figure.
    +
    +fig5, axs = plt.subplots(2, 2, sharex="col", sharey="row",
    +                         gridspec_kw=dict(height_ratios=[1, 3],
    +                                          width_ratios=[3, 1]))
    +axs[0, 1].set_visible(False)
    +axs[0, 0].set_box_aspect(1/3)
    +axs[1, 0].set_box_aspect(1)
    +axs[1, 1].set_box_aspect(3/1)
    +
    +np.random.seed(19680801)  # Fixing random state for reproducibility
    +x, y = np.random.randn(2, 400) * [[.5], [180]]
    +axs[1, 0].scatter(x, y)
    +axs[0, 0].hist(x)
    +axs[1, 1].hist(y, orientation="horizontal")
    +
    +plt.show()
    +
    +# %%
    +# Set data aspect with box aspect
    +# -------------------------------
    +#
    +# When setting the box aspect, one may still set the data aspect as well.
    +# Here we create an Axes with a box twice as long as it is tall and use
    +# an "equal" data aspect for its contents, i.e. the circle actually
    +# stays circular.
    +
    +fig6, ax = plt.subplots()
    +
    +ax.add_patch(plt.Circle((5, 3), 1))
    +ax.set_aspect("equal", adjustable="datalim")
    +ax.set_box_aspect(0.5)
    +ax.autoscale()
    +
    +plt.show()
    +
    +# %%
    +# Box aspect for many subplots
    +# ----------------------------
    +#
    +# It is possible to pass the box aspect to an Axes at initialization. The
    +# following creates a 2 by 3 subplot grid with all square Axes.
    +
    +fig7, axs = plt.subplots(2, 3, subplot_kw=dict(box_aspect=1),
    +                         sharex=True, sharey=True, layout="constrained")
    +
    +for i, ax in enumerate(axs.flat):
    +    ax.scatter(i % 3, -((i // 3) - 0.5)*200, c=[plt.colormaps["hsv"](i / 6)], s=300)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.set_box_aspect`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    styling: size
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axes_demo.py b/galleries/examples/subplots_axes_and_figures/axes_demo.py
    new file mode 100644
    index 000000000000..16db465449a4
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axes_demo.py
    @@ -0,0 +1,53 @@
    +"""
    +=========
    +Axes Demo
    +=========
    +
    +Example use of ``fig.add_axes`` to create inset Axes within the main plot Axes.
    +
    +Please see also the :ref:`axes_grid_examples` section, and the following three
    +examples:
    +
    +- :doc:`/gallery/subplots_axes_and_figures/zoom_inset_axes`
    +- :doc:`/gallery/axes_grid1/inset_locator_demo`
    +- :doc:`/gallery/axes_grid1/inset_locator_demo2`
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)  # Fixing random state for reproducibility.
    +
    +# create some data to use for the plot
    +dt = 0.001
    +t = np.arange(0.0, 10.0, dt)
    +r = np.exp(-t[:1000] / 0.05)  # impulse response
    +x = np.random.randn(len(t))
    +s = np.convolve(x, r)[:len(x)] * dt  # colored noise
    +
    +fig, main_ax = plt.subplots()
    +main_ax.plot(t, s)
    +main_ax.set_xlim(0, 1)
    +main_ax.set_ylim(1.1 * np.min(s), 2 * np.max(s))
    +main_ax.set_xlabel('time (s)')
    +main_ax.set_ylabel('current (nA)')
    +main_ax.set_title('Gaussian colored noise')
    +
    +# this is an inset Axes over the main Axes
    +right_inset_ax = fig.add_axes((.65, .6, .2, .2), facecolor='k')
    +right_inset_ax.hist(s, 400, density=True)
    +right_inset_ax.set(title='Probability', xticks=[], yticks=[])
    +
    +# this is another inset Axes over the main Axes
    +left_inset_ax = fig.add_axes((.2, .6, .2, .2), facecolor='k')
    +left_inset_ax.plot(t[:len(r)], r)
    +left_inset_ax.set(title='Impulse response', xlim=(0, .2), xticks=[], yticks=[])
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: line
    +#    plot-type: histogram
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axes_margins.py b/galleries/examples/subplots_axes_and_figures/axes_margins.py
    new file mode 100644
    index 000000000000..30298168c8e8
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axes_margins.py
    @@ -0,0 +1,96 @@
    +"""
    +======================================================
    +Controlling view limits using margins and sticky_edges
    +======================================================
    +
    +The first figure in this example shows how to zoom in and out of a
    +plot using `~.Axes.margins` instead of `~.Axes.set_xlim` and
    +`~.Axes.set_ylim`. The second figure demonstrates the concept of
    +edge "stickiness" introduced by certain methods and artists and how
    +to effectively work around that.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Polygon
    +
    +
    +def f(t):
    +    return np.exp(-t) * np.cos(2*np.pi*t)
    +
    +
    +t1 = np.arange(0.0, 3.0, 0.01)
    +
    +ax1 = plt.subplot(212)
    +ax1.margins(0.05)           # Default margin is 0.05, value 0 means fit
    +ax1.plot(t1, f(t1))
    +
    +ax2 = plt.subplot(221)
    +ax2.margins(2, 2)           # Values >0.0 zoom out
    +ax2.plot(t1, f(t1))
    +ax2.set_title('Zoomed out')
    +
    +ax3 = plt.subplot(222)
    +ax3.margins(x=0, y=-0.25)   # Values in (-0.5, 0.0) zooms in to center
    +ax3.plot(t1, f(t1))
    +ax3.set_title('Zoomed in')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# On the "stickiness" of certain plotting methods
    +# """""""""""""""""""""""""""""""""""""""""""""""
    +#
    +# Some plotting functions make the axis limits "sticky" or immune to the will
    +# of the `~.Axes.margins` methods. For instance, `~.Axes.imshow` and
    +# `~.Axes.pcolor` expect the user to want the limits to be tight around the
    +# pixels shown in the plot. If this behavior is not desired, you need to set
    +# `~.Axes.use_sticky_edges` to `False`. Consider the following example:
    +
    +y, x = np.mgrid[:5, 1:6]
    +poly_coords = [
    +    (0.25, 2.75), (3.25, 2.75),
    +    (2.25, 0.75), (0.25, 0.75)
    +]
    +fig, (ax1, ax2) = plt.subplots(ncols=2)
    +
    +# Here we set the stickiness of the Axes object...
    +# ax1 we'll leave as the default, which uses sticky edges
    +# and we'll turn off stickiness for ax2
    +ax2.use_sticky_edges = False
    +
    +for ax, status in zip((ax1, ax2), ('Is', 'Is Not')):
    +    cells = ax.pcolor(x, y, x+y, cmap='inferno', shading='auto')  # sticky
    +    ax.add_patch(
    +        Polygon(poly_coords, color='forestgreen', alpha=0.5)
    +    )  # not sticky
    +    ax.margins(x=0.1, y=0.05)
    +    ax.set_aspect('equal')
    +    ax.set_title(f'{status} Sticky')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.margins` / `matplotlib.pyplot.margins`
    +#    - `matplotlib.axes.Axes.use_sticky_edges`
    +#    - `matplotlib.axes.Axes.pcolor` / `matplotlib.pyplot.pcolor`
    +#    - `matplotlib.patches.Polygon`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: line
    +#    plot-type: imshow
    +#    plot-type: pcolor
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axes_props.py b/galleries/examples/subplots_axes_and_figures/axes_props.py
    new file mode 100644
    index 000000000000..6bbcc88ad5b8
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axes_props.py
    @@ -0,0 +1,28 @@
    +"""
    +===============
    +Axes properties
    +===============
    +
    +You can control the axis tick and grid properties
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2.0, 0.01)
    +s = np.sin(2 * np.pi * t)
    +
    +fig, ax = plt.subplots()
    +ax.plot(t, s)
    +
    +ax.grid(True, linestyle='-.')
    +ax.tick_params(labelcolor='r', labelsize='medium', width=3)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: ticks
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py b/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py
    new file mode 100644
    index 000000000000..93c7662576e1
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axes_zoom_effect.py
    @@ -0,0 +1,127 @@
    +"""
    +================
    +Axes zoom effect
    +================
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.transforms import Bbox, TransformedBbox, blended_transform_factory
    +from mpl_toolkits.axes_grid1.inset_locator import (BboxConnector, BboxConnectorPatch,
    +                                                   BboxPatch)
    +
    +
    +def connect_bbox(bbox1, bbox2,
    +                 loc1a, loc2a, loc1b, loc2b,
    +                 prop_lines, prop_patches=None):
    +    if prop_patches is None:
    +        prop_patches = {
    +            **prop_lines,
    +            "alpha": prop_lines.get("alpha", 1) * 0.2,
    +            "clip_on": False,
    +        }
    +
    +    c1 = BboxConnector(
    +        bbox1, bbox2, loc1=loc1a, loc2=loc2a, clip_on=False, **prop_lines)
    +    c2 = BboxConnector(
    +        bbox1, bbox2, loc1=loc1b, loc2=loc2b, clip_on=False, **prop_lines)
    +
    +    bbox_patch1 = BboxPatch(bbox1, **prop_patches)
    +    bbox_patch2 = BboxPatch(bbox2, **prop_patches)
    +
    +    p = BboxConnectorPatch(bbox1, bbox2,
    +                           loc1a=loc1a, loc2a=loc2a, loc1b=loc1b, loc2b=loc2b,
    +                           clip_on=False,
    +                           **prop_patches)
    +
    +    return c1, c2, bbox_patch1, bbox_patch2, p
    +
    +
    +def zoom_effect01(ax1, ax2, xmin, xmax, **kwargs):
    +    """
    +    Connect *ax1* and *ax2*. The *xmin*-to-*xmax* range in both Axes will
    +    be marked.
    +
    +    Parameters
    +    ----------
    +    ax1
    +        The main Axes.
    +    ax2
    +        The zoomed Axes.
    +    xmin, xmax
    +        The limits of the colored area in both plot Axes.
    +    **kwargs
    +        Arguments passed to the patch constructor.
    +    """
    +
    +    bbox = Bbox.from_extents(xmin, 0, xmax, 1)
    +
    +    mybbox1 = TransformedBbox(bbox, ax1.get_xaxis_transform())
    +    mybbox2 = TransformedBbox(bbox, ax2.get_xaxis_transform())
    +
    +    prop_patches = {**kwargs, "ec": "none", "alpha": 0.2}
    +
    +    c1, c2, bbox_patch1, bbox_patch2, p = connect_bbox(
    +        mybbox1, mybbox2,
    +        loc1a=3, loc2a=2, loc1b=4, loc2b=1,
    +        prop_lines=kwargs, prop_patches=prop_patches)
    +
    +    ax1.add_patch(bbox_patch1)
    +    ax2.add_patch(bbox_patch2)
    +    ax2.add_patch(c1)
    +    ax2.add_patch(c2)
    +    ax2.add_patch(p)
    +
    +    return c1, c2, bbox_patch1, bbox_patch2, p
    +
    +
    +def zoom_effect02(ax1, ax2, **kwargs):
    +    """
    +    ax1 : the main Axes
    +    ax1 : the zoomed Axes
    +
    +    Similar to zoom_effect01.  The xmin & xmax will be taken from the
    +    ax1.viewLim.
    +    """
    +
    +    tt = ax1.transScale + (ax1.transLimits + ax2.transAxes)
    +    trans = blended_transform_factory(ax2.transData, tt)
    +
    +    mybbox1 = ax1.bbox
    +    mybbox2 = TransformedBbox(ax1.viewLim, trans)
    +
    +    prop_patches = {**kwargs, "ec": "none", "alpha": 0.2}
    +
    +    c1, c2, bbox_patch1, bbox_patch2, p = connect_bbox(
    +        mybbox1, mybbox2,
    +        loc1a=3, loc2a=2, loc1b=4, loc2b=1,
    +        prop_lines=kwargs, prop_patches=prop_patches)
    +
    +    ax1.add_patch(bbox_patch1)
    +    ax2.add_patch(bbox_patch2)
    +    ax2.add_patch(c1)
    +    ax2.add_patch(c2)
    +    ax2.add_patch(p)
    +
    +    return c1, c2, bbox_patch1, bbox_patch2, p
    +
    +
    +axs = plt.figure().subplot_mosaic([
    +    ["zoom1", "zoom2"],
    +    ["main", "main"],
    +])
    +
    +axs["main"].set(xlim=(0, 5))
    +zoom_effect01(axs["zoom1"], axs["main"], 0.2, 0.8)
    +axs["zoom2"].set(xlim=(2, 3))
    +zoom_effect02(axs["zoom2"], axs["main"])
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    component: transform
    +#    level: advanced
    diff --git a/galleries/examples/subplots_axes_and_figures/axhspan_demo.py b/galleries/examples/subplots_axes_and_figures/axhspan_demo.py
    new file mode 100644
    index 000000000000..971c6002ee71
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axhspan_demo.py
    @@ -0,0 +1,55 @@
    +"""
    +==============================
    +Draw regions that span an Axes
    +==============================
    +
    +`~.Axes.axhspan` and `~.Axes.axvspan` draw rectangles that span the Axes in either
    +the horizontal or vertical direction and are bounded in the other direction. They are
    +often used to highlight data regions.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 3))
    +
    +np.random.seed(19680801)
    +s = 2.9 * np.convolve(np.random.randn(500), np.ones(30) / 30, mode='valid')
    +ax1.plot(s)
    +ax1.axhspan(-1, 1, alpha=0.1)
    +ax1.set(ylim=(-1.5, 1.5), title="axhspan")
    +
    +
    +mu = 8
    +sigma = 2
    +x = np.linspace(0, 16, 401)
    +y = np.exp(-((x-mu)**2)/(2*sigma**2))
    +ax2.axvspan(mu-2*sigma, mu-sigma, color='0.95')
    +ax2.axvspan(mu-sigma, mu+sigma, color='0.9')
    +ax2.axvspan(mu+sigma, mu+2*sigma, color='0.95')
    +ax2.axvline(mu, color='darkgrey', linestyle='--')
    +ax2.plot(x, y)
    +ax2.set(title="axvspan")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.axhspan` / `matplotlib.pyplot.axhspan`
    +#    - `matplotlib.axes.Axes.axvspan` / `matplotlib.pyplot.axvspan`
    +#
    +#
    +# .. seealso::
    +#
    +#    `~.Axes.axhline`, `~.Axes.axvline`, `~.Axes.axline` draw infinite lines.
    +#
    +# .. tags::
    +#
    +#    styling: shape
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axis_equal_demo.py b/galleries/examples/subplots_axes_and_figures/axis_equal_demo.py
    new file mode 100644
    index 000000000000..046af386ae59
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axis_equal_demo.py
    @@ -0,0 +1,43 @@
    +"""
    +=======================
    +Equal axis aspect ratio
    +=======================
    +
    +How to set and adjust plots with equal axis aspect ratios.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Plot circle of radius 3.
    +
    +an = np.linspace(0, 2 * np.pi, 100)
    +fig, axs = plt.subplots(2, 2)
    +
    +axs[0, 0].plot(3 * np.cos(an), 3 * np.sin(an))
    +axs[0, 0].set_title('not equal, looks like ellipse', fontsize=10)
    +
    +axs[0, 1].plot(3 * np.cos(an), 3 * np.sin(an))
    +axs[0, 1].axis('equal')
    +axs[0, 1].set_title('equal, looks like circle', fontsize=10)
    +
    +axs[1, 0].plot(3 * np.cos(an), 3 * np.sin(an))
    +axs[1, 0].axis('equal')
    +axs[1, 0].set(xlim=(-3, 3), ylim=(-3, 3))
    +axs[1, 0].set_title('still a circle, even after changing limits', fontsize=10)
    +
    +axs[1, 1].plot(3 * np.cos(an), 3 * np.sin(an))
    +axs[1, 1].set_aspect('equal', 'box')
    +axs[1, 1].set_title('still a circle, auto-adjusted data limits', fontsize=10)
    +
    +fig.tight_layout()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axes
    +#    styling: size
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/axis_labels_demo.py b/galleries/examples/subplots_axes_and_figures/axis_labels_demo.py
    new file mode 100644
    index 000000000000..2d0bc427b1f9
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/axis_labels_demo.py
    @@ -0,0 +1,27 @@
    +"""
    +===================
    +Axis label position
    +===================
    +
    +Choose axis label position when calling `~.Axes.set_xlabel` and
    +`~.Axes.set_ylabel` as well as for colorbar.
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +sc = ax.scatter([1, 2], [1, 2], c=[1, 2])
    +ax.set_ylabel('YLabel', loc='top')
    +ax.set_xlabel('XLabel', loc='left')
    +cbar = fig.colorbar(sc)
    +cbar.set_label("ZLabel", loc='top')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axis
    +#    styling: position
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/broken_axis.py b/galleries/examples/subplots_axes_and_figures/broken_axis.py
    new file mode 100644
    index 000000000000..6305e613e327
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/broken_axis.py
    @@ -0,0 +1,61 @@
    +"""
    +===========
    +Broken axis
    +===========
    +
    +Broken axis example, where the y-axis will have a portion cut out.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +pts = np.random.rand(30)*.2
    +# Now let's make two outlier points which are far away from everything.
    +pts[[3, 14]] += .8
    +
    +# If we were to simply plot pts, we'd lose most of the interesting
    +# details due to the outliers. So let's 'break' or 'cut-out' the y-axis
    +# into two portions - use the top (ax1) for the outliers, and the bottom
    +# (ax2) for the details of the majority of our data
    +fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
    +fig.subplots_adjust(hspace=0.05)  # adjust space between Axes
    +
    +# plot the same data on both Axes
    +ax1.plot(pts)
    +ax2.plot(pts)
    +
    +# zoom-in / limit the view to different portions of the data
    +ax1.set_ylim(.78, 1.)  # outliers only
    +ax2.set_ylim(0, .22)  # most of the data
    +
    +# hide the spines between ax and ax2
    +ax1.spines.bottom.set_visible(False)
    +ax2.spines.top.set_visible(False)
    +ax1.xaxis.tick_top()
    +ax1.tick_params(labeltop=False)  # don't put tick labels at the top
    +ax2.xaxis.tick_bottom()
    +
    +# Now, let's turn towards the cut-out slanted lines.
    +# We create line objects in axes coordinates, in which (0,0), (0,1),
    +# (1,0), and (1,1) are the four corners of the Axes.
    +# The slanted lines themselves are markers at those locations, such that the
    +# lines keep their angle and position, independent of the Axes size or scale
    +# Finally, we need to disable clipping.
    +
    +d = .5  # proportion of vertical to horizontal extent of the slanted line
    +kwargs = dict(marker=[(-1, -d), (1, d)], markersize=12,
    +              linestyle="none", color='k', mec='k', mew=1, clip_on=False)
    +ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
    +ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)
    +
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axis
    +#    plot-type: line
    +#    level: intermediate
    diff --git a/galleries/examples/subplots_axes_and_figures/custom_figure_class.py b/galleries/examples/subplots_axes_and_figures/custom_figure_class.py
    new file mode 100644
    index 000000000000..328447062a5b
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/custom_figure_class.py
    @@ -0,0 +1,59 @@
    +"""
    +========================
    +Custom Figure subclasses
    +========================
    +
    +You can pass a `.Figure` subclass to `.pyplot.figure` if you want to change
    +the default behavior of the figure.
    +
    +This example defines a `.Figure` subclass ``WatermarkFigure`` that accepts an
    +additional parameter ``watermark`` to display a custom watermark text. The
    +figure is created using the ``FigureClass`` parameter of `.pyplot.figure`.
    +The additional ``watermark`` parameter is passed on to the subclass
    +constructor.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.figure import Figure
    +
    +
    +class WatermarkFigure(Figure):
    +    """A figure with a text watermark."""
    +
    +    def __init__(self, *args, watermark=None, **kwargs):
    +        super().__init__(*args, **kwargs)
    +
    +        if watermark is not None:
    +            bbox = dict(boxstyle='square', lw=3, ec='gray',
    +                        fc=(0.9, 0.9, .9, .5), alpha=0.5)
    +            self.text(0.5, 0.5, watermark,
    +                      ha='center', va='center', rotation=30,
    +                      fontsize=40, color='gray', alpha=0.5, bbox=bbox)
    +
    +
    +x = np.linspace(-3, 3, 201)
    +y = np.tanh(x) + 0.1 * np.cos(5 * x)
    +
    +plt.figure(FigureClass=WatermarkFigure, watermark='draft')
    +plt.plot(x, y)
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.figure`
    +#    - `matplotlib.figure.Figure`
    +#    - `matplotlib.figure.Figure.text`
    +#
    +# .. tags::
    +#
    +#    component: figure
    +#    plot-type: line
    +#    level: intermediate
    +#    purpose: showcase
    diff --git a/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py b/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py
    new file mode 100644
    index 000000000000..b3a59ce048c0
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/demo_constrained_layout.py
    @@ -0,0 +1,78 @@
    +"""
    +===================================
    +Resize Axes with constrained layout
    +===================================
    +
    +*Constrained layout* attempts to resize subplots in
    +a figure so that there are no overlaps between Axes objects and labels
    +on the Axes.
    +
    +See :ref:`constrainedlayout_guide` for more details and
    +:ref:`tight_layout_guide` for an alternative.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def example_plot(ax):
    +    ax.plot([1, 2])
    +    ax.set_xlabel('x-label', fontsize=12)
    +    ax.set_ylabel('y-label', fontsize=12)
    +    ax.set_title('Title', fontsize=14)
    +
    +
    +# %%
    +# If we don't use *constrained layout*, then labels overlap the Axes
    +
    +fig, axs = plt.subplots(nrows=2, ncols=2, layout=None)
    +
    +for ax in axs.flat:
    +    example_plot(ax)
    +
    +# %%
    +# adding ``layout='constrained'`` automatically adjusts.
    +
    +fig, axs = plt.subplots(nrows=2, ncols=2, layout='constrained')
    +
    +for ax in axs.flat:
    +    example_plot(ax)
    +
    +# %%
    +# Below is a more complicated example using nested gridspecs.
    +
    +fig = plt.figure(layout='constrained')
    +
    +import matplotlib.gridspec as gridspec
    +
    +gs0 = gridspec.GridSpec(1, 2, figure=fig)
    +
    +gs1 = gridspec.GridSpecFromSubplotSpec(3, 1, subplot_spec=gs0[0])
    +for n in range(3):
    +    ax = fig.add_subplot(gs1[n])
    +    example_plot(ax)
    +
    +
    +gs2 = gridspec.GridSpecFromSubplotSpec(2, 1, subplot_spec=gs0[1])
    +for n in range(2):
    +    ax = fig.add_subplot(gs2[n])
    +    example_plot(ax)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.gridspec.GridSpec`
    +#    - `matplotlib.gridspec.GridSpecFromSubplotSpec`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    component: subplot
    +#    styling: size
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py b/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py
    new file mode 100644
    index 000000000000..4ac0f1b99dfc
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/demo_tight_layout.py
    @@ -0,0 +1,141 @@
    +"""
    +=============================
    +Resize Axes with tight layout
    +=============================
    +
    +`~.Figure.tight_layout` attempts to resize subplots in a figure so that there
    +are no overlaps between Axes objects and labels on the Axes.
    +
    +See :ref:`tight_layout_guide` for more details and
    +:ref:`constrainedlayout_guide` for an alternative.
    +
    +"""
    +
    +import itertools
    +import warnings
    +
    +import matplotlib.pyplot as plt
    +
    +fontsizes = itertools.cycle([8, 16, 24, 32])
    +
    +
    +def example_plot(ax):
    +    ax.plot([1, 2])
    +    ax.set_xlabel('x-label', fontsize=next(fontsizes))
    +    ax.set_ylabel('y-label', fontsize=next(fontsizes))
    +    ax.set_title('Title', fontsize=next(fontsizes))
    +
    +
    +# %%
    +
    +fig, ax = plt.subplots()
    +example_plot(ax)
    +fig.tight_layout()
    +
    +# %%
    +
    +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
    +example_plot(ax1)
    +example_plot(ax2)
    +example_plot(ax3)
    +example_plot(ax4)
    +fig.tight_layout()
    +
    +# %%
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1)
    +example_plot(ax1)
    +example_plot(ax2)
    +fig.tight_layout()
    +
    +# %%
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)
    +example_plot(ax1)
    +example_plot(ax2)
    +fig.tight_layout()
    +
    +# %%
    +
    +fig, axs = plt.subplots(nrows=3, ncols=3)
    +for ax in axs.flat:
    +    example_plot(ax)
    +fig.tight_layout()
    +
    +# %%
    +
    +plt.figure()
    +ax1 = plt.subplot(221)
    +ax2 = plt.subplot(223)
    +ax3 = plt.subplot(122)
    +example_plot(ax1)
    +example_plot(ax2)
    +example_plot(ax3)
    +plt.tight_layout()
    +
    +# %%
    +
    +plt.figure()
    +ax1 = plt.subplot2grid((3, 3), (0, 0))
    +ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
    +ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
    +ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
    +example_plot(ax1)
    +example_plot(ax2)
    +example_plot(ax3)
    +example_plot(ax4)
    +plt.tight_layout()
    +
    +# %%
    +
    +fig = plt.figure()
    +
    +gs1 = fig.add_gridspec(3, 1)
    +ax1 = fig.add_subplot(gs1[0])
    +ax2 = fig.add_subplot(gs1[1])
    +ax3 = fig.add_subplot(gs1[2])
    +example_plot(ax1)
    +example_plot(ax2)
    +example_plot(ax3)
    +gs1.tight_layout(fig, rect=[None, None, 0.45, None])
    +
    +gs2 = fig.add_gridspec(2, 1)
    +ax4 = fig.add_subplot(gs2[0])
    +ax5 = fig.add_subplot(gs2[1])
    +example_plot(ax4)
    +example_plot(ax5)
    +with warnings.catch_warnings():
    +    # gs2.tight_layout cannot handle the subplots from the first gridspec
    +    # (gs1), so it will raise a warning. We are going to match the gridspecs
    +    # manually so we can filter the warning away.
    +    warnings.simplefilter("ignore", UserWarning)
    +    gs2.tight_layout(fig, rect=[0.45, None, None, None])
    +
    +# now match the top and bottom of two gridspecs.
    +top = min(gs1.top, gs2.top)
    +bottom = max(gs1.bottom, gs2.bottom)
    +
    +gs1.update(top=top, bottom=bottom)
    +gs2.update(top=top, bottom=bottom)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.tight_layout` /
    +#      `matplotlib.pyplot.tight_layout`
    +#    - `matplotlib.figure.Figure.add_gridspec`
    +#    - `matplotlib.figure.Figure.add_subplot`
    +#    - `matplotlib.pyplot.subplot2grid`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    component: subplot
    +#    styling: size
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py b/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py
    new file mode 100644
    index 000000000000..95b92482d5ac
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/fahrenheit_celsius_scales.py
    @@ -0,0 +1,53 @@
    +"""
    +=================================
    +Different scales on the same Axes
    +=================================
    +
    +Demo of how to display two scales on the left and right y-axis.
    +
    +This example uses the Fahrenheit and Celsius scales.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def fahrenheit2celsius(temp):
    +    """
    +    Returns temperature in Celsius given Fahrenheit temperature.
    +    """
    +    return (5. / 9.) * (temp - 32)
    +
    +
    +def make_plot():
    +
    +    # Define a closure function to register as a callback
    +    def convert_ax_c_to_celsius(ax_f):
    +        """
    +        Update second axis according to first axis.
    +        """
    +        y1, y2 = ax_f.get_ylim()
    +        ax_c.set_ylim(fahrenheit2celsius(y1), fahrenheit2celsius(y2))
    +        ax_c.figure.canvas.draw()
    +
    +    fig, ax_f = plt.subplots()
    +    ax_c = ax_f.twinx()
    +
    +    # automatically update ylim of ax2 when ylim of ax1 changes.
    +    ax_f.callbacks.connect("ylim_changed", convert_ax_c_to_celsius)
    +    ax_f.plot(np.linspace(-40, 120, 100))
    +    ax_f.set_xlim(0, 100)
    +
    +    ax_f.set_title('Two scales: Fahrenheit and Celsius')
    +    ax_f.set_ylabel('Fahrenheit')
    +    ax_c.set_ylabel('Celsius')
    +
    +    plt.show()
    +
    +make_plot()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/figure_size_units.py b/galleries/examples/subplots_axes_and_figures/figure_size_units.py
    new file mode 100644
    index 000000000000..50292ef92b74
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/figure_size_units.py
    @@ -0,0 +1,87 @@
    +"""
    +==============================
    +Figure size in different units
    +==============================
    +
    +The native figure size unit in Matplotlib is inches, deriving from print
    +industry standards. However, users may need to specify their figures in other
    +units like centimeters or pixels. This example illustrates how to do this
    +efficiently.
    +"""
    +
    +# sphinx_gallery_thumbnail_number = 2
    +
    +import matplotlib.pyplot as plt
    +
    +text_kwargs = dict(ha='center', va='center', fontsize=28, color='C1')
    +
    +# %%
    +# Figure size in inches (default)
    +# -------------------------------
    +#
    +plt.subplots(figsize=(6, 2))
    +plt.text(0.5, 0.5, '6 inches x 2 inches', **text_kwargs)
    +plt.show()
    +
    +
    +# %%
    +# Figure size in centimeter
    +# -------------------------
    +# Multiplying centimeter-based numbers with a conversion factor from cm to
    +# inches, gives the right numbers. Naming the conversion factor ``cm`` makes
    +# the conversion almost look like appending a unit to the number, which is
    +# nicely readable.
    +#
    +cm = 1/2.54  # centimeters in inches
    +plt.subplots(figsize=(15*cm, 5*cm))
    +plt.text(0.5, 0.5, '15cm x 5cm', **text_kwargs)
    +plt.show()
    +
    +
    +# %%
    +# Figure size in pixel
    +# --------------------
    +# Similarly, one can use a conversion from pixels.
    +#
    +# Note that you could break this if you use `~.pyplot.savefig` with a
    +# different explicit dpi value.
    +#
    +px = 1/plt.rcParams['figure.dpi']  # pixel in inches
    +plt.subplots(figsize=(600*px, 200*px))
    +plt.text(0.5, 0.5, '600px x 200px', **text_kwargs)
    +plt.show()
    +
    +# %%
    +# Quick interactive work is usually rendered to the screen, making pixels a
    +# good size of unit. But defining the conversion factor may feel a little
    +# tedious for quick iterations.
    +#
    +# Because of the default ``rcParams['figure.dpi'] = 100``, one can mentally
    +# divide the needed pixel value by 100 [#]_:
    +#
    +plt.subplots(figsize=(6, 2))
    +plt.text(0.5, 0.5, '600px x 200px', **text_kwargs)
    +plt.show()
    +
    +# %%
    +# .. [#] Unfortunately, this does not work well for the ``matplotlib inline``
    +#        backend in Jupyter because that backend saves the figure
    +#        with ``bbox_inches='tight'``, which crops the figure and makes the
    +#        actual size unpredictable.
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.figure`
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.pyplot.subplot_mosaic`
    +#
    +# .. tags::
    +#
    +#    component: figure
    +#    styling: size
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/figure_title.py b/galleries/examples/subplots_axes_and_figures/figure_title.py
    new file mode 100644
    index 000000000000..1b0eb1a00b23
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/figure_title.py
    @@ -0,0 +1,61 @@
    +"""
    +=============================================
    +Figure labels: suptitle, supxlabel, supylabel
    +=============================================
    +
    +Each Axes can have a title (or actually three - one each with *loc* "left",
    +"center", and "right"), but is sometimes desirable to give a whole figure
    +(or `.SubFigure`) an overall title, using `.Figure.suptitle`.
    +
    +We can also add figure-level x- and y-labels using `.Figure.supxlabel` and
    +`.Figure.supylabel`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.cbook import get_sample_data
    +
    +x = np.linspace(0.0, 5.0, 501)
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, layout='constrained', sharey=True)
    +ax1.plot(x, np.cos(6*x) * np.exp(-x))
    +ax1.set_title('damped')
    +ax1.set_xlabel('time (s)')
    +ax1.set_ylabel('amplitude')
    +
    +ax2.plot(x, np.cos(6*x))
    +ax2.set_xlabel('time (s)')
    +ax2.set_title('undamped')
    +
    +fig.suptitle('Different types of oscillations', fontsize=16)
    +
    +# %%
    +# A global x- or y-label can be set using the `.Figure.supxlabel` and
    +# `.Figure.supylabel` methods.
    +
    +
    +with get_sample_data('Stocks.csv') as file:
    +    stocks = np.genfromtxt(
    +        file, delimiter=',', names=True, dtype=None,
    +        converters={0: lambda x: np.datetime64(x, 'D')}, skip_header=1)
    +
    +fig, axs = plt.subplots(4, 2, figsize=(9, 5), layout='constrained',
    +                        sharex=True, sharey=True)
    +for nn, ax in enumerate(axs.flat):
    +    column_name = stocks.dtype.names[1+nn]
    +    y = stocks[column_name]
    +    line, = ax.plot(stocks['Date'], y / np.nanmax(y), lw=2.5)
    +    ax.set_title(column_name, fontsize='small', loc='left')
    +fig.supxlabel('Year')
    +fig.supylabel('Stock price relative to max')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: figure
    +#    component: title
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/ganged_plots.py b/galleries/examples/subplots_axes_and_figures/ganged_plots.py
    new file mode 100644
    index 000000000000..3229d64a15b4
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/ganged_plots.py
    @@ -0,0 +1,47 @@
    +"""
    +=================
    +Adjacent subplots
    +=================
    +
    +To create plots that share a common axis (visually) you can set the hspace
    +between the subplots to zero. Passing sharex=True when creating the subplots
    +will automatically turn off all x ticks and labels except those on the bottom
    +axis.
    +
    +In this example the plots share a common x-axis, but you can follow the same
    +logic to supply a common y-axis.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2.0, 0.01)
    +
    +s1 = np.sin(2 * np.pi * t)
    +s2 = np.exp(-t)
    +s3 = s1 * s2
    +
    +fig, axs = plt.subplots(3, 1, sharex=True)
    +# Remove vertical space between Axes
    +fig.subplots_adjust(hspace=0)
    +
    +# Plot each graph, and manually set the y tick values
    +axs[0].plot(t, s1)
    +axs[0].set_yticks(np.arange(-0.9, 1.0, 0.4))
    +axs[0].set_ylim(-1, 1)
    +
    +axs[1].plot(t, s2)
    +axs[1].set_yticks(np.arange(0.1, 1.0, 0.2))
    +axs[1].set_ylim(0, 1)
    +
    +axs[2].plot(t, s3)
    +axs[2].set_yticks(np.arange(-0.9, 1.0, 0.4))
    +axs[2].set_ylim(-1, 1)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/geo_demo.py b/galleries/examples/subplots_axes_and_figures/geo_demo.py
    new file mode 100644
    index 000000000000..4c8d38cc8a52
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/geo_demo.py
    @@ -0,0 +1,49 @@
    +"""
    +======================
    +Geographic Projections
    +======================
    +
    +This shows 4 possible geographic projections.  Cartopy_ supports more
    +projections.
    +
    +.. _Cartopy: https://cartopy.readthedocs.io
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +# %%
    +
    +plt.figure()
    +plt.subplot(projection="aitoff")
    +plt.title("Aitoff")
    +plt.grid(True)
    +
    +# %%
    +
    +plt.figure()
    +plt.subplot(projection="hammer")
    +plt.title("Hammer")
    +plt.grid(True)
    +
    +# %%
    +
    +plt.figure()
    +plt.subplot(projection="lambert")
    +plt.title("Lambert")
    +plt.grid(True)
    +
    +# %%
    +
    +plt.figure()
    +plt.subplot(projection="mollweide")
    +plt.title("Mollweide")
    +plt.grid(True)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    plot-type: specialty
    +#    component: projection
    +#    domain: cartography
    diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py b/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py
    new file mode 100644
    index 000000000000..9996bde9306a
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/gridspec_and_subplots.py
    @@ -0,0 +1,36 @@
    +"""
    +================================================
    +Combine two subplots using subplots and GridSpec
    +================================================
    +
    +Sometimes we want to combine two subplots in an Axes layout created with
    +`~.Figure.subplots`.  We can get the `~.gridspec.GridSpec` from the Axes
    +and then remove the covered Axes and fill the gap with a new bigger Axes.
    +Here we create a layout with the bottom two Axes in the last column combined.
    +
    +To start with this layout (rather than removing the overlapping Axes) use
    +`~.pyplot.subplot_mosaic`.
    +
    +See also :ref:`arranging_axes`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, axs = plt.subplots(ncols=3, nrows=3)
    +gs = axs[1, 2].get_gridspec()
    +# remove the underlying Axes
    +for ax in axs[1:, -1]:
    +    ax.remove()
    +axbig = fig.add_subplot(gs[1:, -1])
    +axbig.annotate('Big Axes \nGridSpec[1:, -1]', (0.1, 0.5),
    +               xycoords='axes fraction', va='center')
    +
    +fig.tight_layout()
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_customization.py b/galleries/examples/subplots_axes_and_figures/gridspec_customization.py
    new file mode 100644
    index 000000000000..08b2dfd45474
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/gridspec_customization.py
    @@ -0,0 +1,54 @@
    +"""
    +========================================
    +GridSpec with variable sizes and spacing
    +========================================
    +
    +This example demonstrates the use of `.GridSpec` to generate subplots,
    +the control of the relative sizes of subplots with *width_ratios* and
    +*height_ratios*, and the control of the spacing around and between subplots
    +using subplot params (*left*, *right*, *bottom*, *top*, *wspace*, and
    +*hspace*).
    +
    +.. redirect-from:: /gallery/userdemo/demo_gridspec03
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.gridspec import GridSpec
    +
    +
    +def annotate_axes(fig):
    +    for i, ax in enumerate(fig.axes):
    +        ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
    +        ax.tick_params(labelbottom=False, labelleft=False)
    +
    +
    +fig = plt.figure()
    +fig.suptitle("Controlling subplot sizes with width_ratios and height_ratios")
    +
    +gs = GridSpec(2, 2, width_ratios=[1, 2], height_ratios=[4, 1])
    +ax1 = fig.add_subplot(gs[0])
    +ax2 = fig.add_subplot(gs[1])
    +ax3 = fig.add_subplot(gs[2])
    +ax4 = fig.add_subplot(gs[3])
    +
    +annotate_axes(fig)
    +
    +# %%
    +
    +fig = plt.figure()
    +fig.suptitle("Controlling spacing around and between subplots")
    +
    +gs1 = GridSpec(3, 3, left=0.05, right=0.48, wspace=0.05)
    +ax1 = fig.add_subplot(gs1[:-1, :])
    +ax2 = fig.add_subplot(gs1[-1, :-1])
    +ax3 = fig.add_subplot(gs1[-1, -1])
    +
    +gs2 = GridSpec(3, 3, left=0.55, right=0.98, hspace=0.05)
    +ax4 = fig.add_subplot(gs2[:, :-1])
    +ax5 = fig.add_subplot(gs2[:-1, -1])
    +ax6 = fig.add_subplot(gs2[-1, -1])
    +
    +annotate_axes(fig)
    +
    +plt.show()
    diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_multicolumn.py b/galleries/examples/subplots_axes_and_figures/gridspec_multicolumn.py
    new file mode 100644
    index 000000000000..3762dec4fdb8
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/gridspec_multicolumn.py
    @@ -0,0 +1,40 @@
    +"""
    +=============================================
    +Gridspec for multi-column/row subplot layouts
    +=============================================
    +
    +`.GridSpec` is a flexible way to layout
    +subplot grids.  Here is an example with a 3x3 grid, and
    +axes spanning all three columns, two columns, and two rows.
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.gridspec import GridSpec
    +
    +
    +def format_axes(fig):
    +    for i, ax in enumerate(fig.axes):
    +        ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
    +        ax.tick_params(labelbottom=False, labelleft=False)
    +
    +fig = plt.figure(layout="constrained")
    +
    +gs = GridSpec(3, 3, figure=fig)
    +ax1 = fig.add_subplot(gs[0, :])
    +# identical to ax1 = plt.subplot(gs.new_subplotspec((0, 0), colspan=3))
    +ax2 = fig.add_subplot(gs[1, :-1])
    +ax3 = fig.add_subplot(gs[1:, -1])
    +ax4 = fig.add_subplot(gs[-1, 0])
    +ax5 = fig.add_subplot(gs[-1, -2])
    +
    +fig.suptitle("GridSpec")
    +format_axes(fig)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    level: intermediate
    diff --git a/galleries/examples/subplots_axes_and_figures/gridspec_nested.py b/galleries/examples/subplots_axes_and_figures/gridspec_nested.py
    new file mode 100644
    index 000000000000..789cc0ae6b5b
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/gridspec_nested.py
    @@ -0,0 +1,54 @@
    +"""
    +.. redirect-from:: /gallery/userdemo/demo_gridspec06
    +
    +================
    +Nested Gridspecs
    +================
    +
    +GridSpecs can be nested, so that a subplot from a parent GridSpec can
    +set the position for a nested grid of subplots.
    +
    +Note that the same functionality can be achieved more directly with
    +`~.Figure.subfigures`; see
    +:doc:`/gallery/subplots_axes_and_figures/subfigures`.
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.gridspec as gridspec
    +
    +
    +def format_axes(fig):
    +    for i, ax in enumerate(fig.axes):
    +        ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
    +        ax.tick_params(labelbottom=False, labelleft=False)
    +
    +
    +# gridspec inside gridspec
    +fig = plt.figure()
    +
    +gs0 = gridspec.GridSpec(1, 2, figure=fig)
    +
    +gs00 = gridspec.GridSpecFromSubplotSpec(3, 3, subplot_spec=gs0[0])
    +
    +ax1 = fig.add_subplot(gs00[:-1, :])
    +ax2 = fig.add_subplot(gs00[-1, :-1])
    +ax3 = fig.add_subplot(gs00[-1, -1])
    +
    +# the following syntax does the same as the GridSpecFromSubplotSpec call above:
    +gs01 = gs0[1].subgridspec(3, 3)
    +
    +ax4 = fig.add_subplot(gs01[:, :-1])
    +ax5 = fig.add_subplot(gs01[:-1, -1])
    +ax6 = fig.add_subplot(gs01[-1, -1])
    +
    +plt.suptitle("GridSpec Inside GridSpec")
    +format_axes(fig)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    level: intermediate
    diff --git a/galleries/examples/subplots_axes_and_figures/invert_axes.py b/galleries/examples/subplots_axes_and_figures/invert_axes.py
    new file mode 100644
    index 000000000000..40a4ca2479b7
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/invert_axes.py
    @@ -0,0 +1,42 @@
    +"""
    +=============
    +Inverted axis
    +=============
    +
    +This example demonstrates two ways to invert the direction of an axis:
    +
    +- If you want to set *explicit axis limits* anyway, e.g. via `~.Axes.set_xlim`, you
    +  can swap the limit values: ``set_xlim(4, 0)`` instead of ``set_xlim(0, 4)``.
    +- Use `.Axis.set_inverted` if you only want to invert the axis *without modifying
    +  the limits*, i.e. keep existing limits or existing autoscaling behavior.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.arange(0.01, 4.0, 0.01)
    +y = np.exp(-x)
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6.4,  4), layout="constrained")
    +fig.suptitle('Inverted axis with ...')
    +
    +ax1.plot(x, y)
    +ax1.set_xlim(4, 0)   # inverted fixed limits
    +ax1.set_title('fixed limits: set_xlim(4, 0)')
    +ax1.set_xlabel('decreasing x ⟶')
    +ax1.grid(True)
    +
    +ax2.plot(x, y)
    +ax2.xaxis.set_inverted(True)  # inverted axis with autoscaling
    +ax2.set_title('autoscaling: set_inverted(True)')
    +ax2.set_xlabel('decreasing x ⟶')
    +ax2.grid(True)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axis
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py b/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py
    new file mode 100644
    index 000000000000..fe3b2ab191a1
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/multiple_figs_demo.py
    @@ -0,0 +1,54 @@
    +"""
    +=================================
    +Manage multiple figures in pyplot
    +=================================
    +
    +`matplotlib.pyplot` uses the concept of a *current figure* and *current Axes*.
    +Figures are identified via a figure number that is passed to `~.pyplot.figure`.
    +The figure with the given number is set as *current figure*. Additionally, if
    +no figure with the number exists, a new one is created.
    +
    +.. note::
    +
    +    We discourage working with multiple figures through the implicit pyplot
    +    interface because managing the *current figure* is cumbersome and
    +    error-prone. Instead, we recommend using the explicit approach and call
    +    methods on Figure and Axes instances. See :ref:`api_interfaces` for an
    +    explanation of the trade-offs between the implicit and explicit interfaces.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.0, 2.0, 0.01)
    +s1 = np.sin(2*np.pi*t)
    +s2 = np.sin(4*np.pi*t)
    +
    +# %%
    +# Create figure 1
    +
    +plt.figure(1)
    +plt.subplot(211)
    +plt.plot(t, s1)
    +plt.subplot(212)
    +plt.plot(t, 2*s1)
    +
    +# %%
    +# Create figure 2
    +
    +plt.figure(2)
    +plt.plot(t, s2)
    +
    +# %%
    +# Now switch back to figure 1 and make some changes
    +
    +plt.figure(1)
    +plt.subplot(211)
    +plt.plot(t, s2, 's')
    +ax = plt.gca()
    +ax.set_xticklabels([])
    +
    +plt.show()
    +
    +# %%
    +# .. tags:: component: figure, plot-type: line
    diff --git a/galleries/examples/subplots_axes_and_figures/secondary_axis.py b/galleries/examples/subplots_axes_and_figures/secondary_axis.py
    new file mode 100644
    index 000000000000..146de1cceeca
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/secondary_axis.py
    @@ -0,0 +1,220 @@
    +"""
    +==============
    +Secondary Axis
    +==============
    +
    +Sometimes we want a secondary axis on a plot, for instance to convert
    +radians to degrees on the same plot.  We can do this by making a child
    +axes with only one axis visible via `.axes.Axes.secondary_xaxis` and
    +`.axes.Axes.secondary_yaxis`.  This secondary axis can have a different scale
    +than the main axis by providing both a forward and an inverse conversion
    +function in a tuple to the *functions* keyword argument:
    +
    +See also :doc:`/gallery/subplots_axes_and_figures/two_scales` for the case
    +where two scales are not related to one another, but independent.
    +"""
    +
    +import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.dates as mdates
    +
    +fig, ax = plt.subplots(layout='constrained')
    +x = np.arange(0, 360, 1)
    +y = np.sin(2 * x * np.pi / 180)
    +ax.plot(x, y)
    +ax.set_xlabel('angle [degrees]')
    +ax.set_ylabel('signal')
    +ax.set_title('Sine wave')
    +
    +
    +def deg2rad(x):
    +    return x * np.pi / 180
    +
    +
    +def rad2deg(x):
    +    return x * 180 / np.pi
    +
    +
    +secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg))
    +secax.set_xlabel('angle [rad]')
    +plt.show()
    +
    +# %%
    +# By default, the secondary axis is drawn in the Axes coordinate space.
    +# We can also provide a custom transform to place it in a different
    +# coordinate space. Here we put the axis at Y = 0 in data coordinates.
    +
    +fig, ax = plt.subplots(layout='constrained')
    +x = np.arange(0, 10)
    +np.random.seed(19680801)
    +y = np.random.randn(len(x))
    +ax.plot(x, y)
    +ax.set_xlabel('X')
    +ax.set_ylabel('Y')
    +ax.set_title('Random data')
    +
    +# Pass ax.transData as a transform to place the axis relative to our data
    +secax = ax.secondary_xaxis(0, transform=ax.transData)
    +secax.set_xlabel('Axis at Y = 0')
    +plt.show()
    +
    +# %%
    +# Here is the case of converting from wavenumber to wavelength in a
    +# log-log scale.
    +#
    +# .. note::
    +#
    +#   In this case, the xscale of the parent is logarithmic, so the child is
    +#   made logarithmic as well.
    +
    +fig, ax = plt.subplots(layout='constrained')
    +x = np.arange(0.02, 1, 0.02)
    +np.random.seed(19680801)
    +y = np.random.randn(len(x)) ** 2
    +ax.loglog(x, y)
    +ax.set_xlabel('f [Hz]')
    +ax.set_ylabel('PSD')
    +ax.set_title('Random spectrum')
    +
    +
    +def one_over(x):
    +    """Vectorized 1/x, treating x==0 manually"""
    +    x = np.array(x, float)
    +    near_zero = np.isclose(x, 0)
    +    x[near_zero] = np.inf
    +    x[~near_zero] = 1 / x[~near_zero]
    +    return x
    +
    +
    +# the function "1/x" is its own inverse
    +inverse = one_over
    +
    +
    +secax = ax.secondary_xaxis('top', functions=(one_over, inverse))
    +secax.set_xlabel('period [s]')
    +plt.show()
    +
    +# %%
    +# Sometime we want to relate the axes in a transform that is ad-hoc from the data, and
    +# is derived empirically. Or, one axis could be a complicated nonlinear function of the
    +# other. In these cases we can set the forward and inverse transform functions to be
    +# linear interpolations from the one set of independent variables to the other.
    +#
    +# .. note::
    +#
    +#   In order to properly handle the data margins, the mapping functions
    +#   (``forward`` and ``inverse`` in this example) need to be defined beyond the
    +#   nominal plot limits. This condition can be enforced by extending the
    +#   interpolation beyond the plotted values, both to the left and the right,
    +#   see ``x1n`` and ``x2n`` below.
    +
    +fig, ax = plt.subplots(layout='constrained')
    +x1_vals = np.arange(2, 11, 0.4)
    +# second independent variable is a nonlinear function of the other.
    +x2_vals = x1_vals ** 2
    +ydata = 50.0 + 20 * np.random.randn(len(x1_vals))
    +ax.plot(x1_vals, ydata, label='Plotted data')
    +ax.plot(x1_vals, x2_vals, label=r'$x_2 = x_1^2$')
    +ax.set_xlabel(r'$x_1$')
    +ax.legend()
    +
    +# the forward and inverse functions must be defined on the complete visible axis range
    +x1n = np.linspace(0, 20, 201)
    +x2n = x1n**2
    +
    +
    +def forward(x):
    +    return np.interp(x, x1n, x2n)
    +
    +
    +def inverse(x):
    +    return np.interp(x, x2n, x1n)
    +
    +# use axvline to prove that the derived secondary axis is correctly plotted
    +ax.axvline(np.sqrt(40), color="grey", ls="--")
    +ax.axvline(10, color="grey", ls="--")
    +secax = ax.secondary_xaxis('top', functions=(forward, inverse))
    +secax.set_xticks([10, 20, 40, 60, 80, 100])
    +secax.set_xlabel(r'$x_2$')
    +
    +plt.show()
    +
    +# %%
    +# A final example translates np.datetime64 to yearday on the x axis and
    +# from Celsius to Fahrenheit on the y axis.  Note the addition of a
    +# third y axis, and that it can be placed using a float for the
    +# location argument
    +
    +dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6)
    +         for k in range(240)]
    +temperature = np.random.randn(len(dates)) * 4 + 6.7
    +fig, ax = plt.subplots(layout='constrained')
    +
    +ax.plot(dates, temperature)
    +ax.set_ylabel(r'$T\ [^oC]$')
    +ax.xaxis.set_tick_params(rotation=70)
    +
    +
    +def date2yday(x):
    +    """Convert matplotlib datenum to days since 2018-01-01."""
    +    y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
    +    return y
    +
    +
    +def yday2date(x):
    +    """Return a matplotlib datenum for *x* days after 2018-01-01."""
    +    y = x + mdates.date2num(datetime.datetime(2018, 1, 1))
    +    return y
    +
    +
    +secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date))
    +secax_x.set_xlabel('yday [2018]')
    +
    +
    +def celsius_to_fahrenheit(x):
    +    return x * 1.8 + 32
    +
    +
    +def fahrenheit_to_celsius(x):
    +    return (x - 32) / 1.8
    +
    +
    +secax_y = ax.secondary_yaxis(
    +    'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius))
    +secax_y.set_ylabel(r'$T\ [^oF]$')
    +
    +
    +def celsius_to_anomaly(x):
    +    return (x - np.mean(temperature))
    +
    +
    +def anomaly_to_celsius(x):
    +    return (x + np.mean(temperature))
    +
    +
    +# use of a float for the position:
    +secax_y2 = ax.secondary_yaxis(
    +    1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
    +secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')
    +
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.secondary_xaxis`
    +#    - `matplotlib.axes.Axes.secondary_yaxis`
    +#
    +# .. tags::
    +#
    +#    component: axis
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py b/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py
    new file mode 100644
    index 000000000000..e0aa04d13def
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/share_axis_lims_views.py
    @@ -0,0 +1,32 @@
    +"""
    +===========================
    +Share axis limits and views
    +===========================
    +
    +It's common to make two or more plots which share an axis, e.g., two subplots
    +with time as a common axis.  When you pan and zoom around on one, you want the
    +other to move around with you.  To facilitate this, matplotlib Axes support a
    +``sharex`` and ``sharey`` attribute.  When you create a `~.pyplot.subplot` or
    +`~.pyplot.axes`, you can pass in a keyword indicating what Axes you want to
    +share with.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0, 10, 0.01)
    +
    +ax1 = plt.subplot(211)
    +ax1.plot(t, np.sin(2*np.pi*t))
    +
    +ax2 = plt.subplot(212, sharex=ax1)
    +ax2.plot(t, np.sin(4*np.pi*t))
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axis
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py b/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py
    new file mode 100644
    index 000000000000..848db115456a
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/shared_axis_demo.py
    @@ -0,0 +1,53 @@
    +"""
    +===========
    +Shared axis
    +===========
    +
    +You can share the x- or y-axis limits for one axis with another by
    +passing an `~.axes.Axes` instance as a *sharex* or *sharey* keyword argument.
    +
    +Changing the axis limits on one Axes will be reflected automatically
    +in the other, and vice-versa, so when you navigate with the toolbar
    +the Axes will follow each other on their shared axis.  Ditto for
    +changes in the axis scaling (e.g., log vs. linear).  However, it is
    +possible to have differences in tick labeling, e.g., you can selectively
    +turn off the tick labels on one Axes.
    +
    +The example below shows how to customize the tick labels on the
    +various axes.  Shared axes share the tick locator, tick formatter,
    +view limits, and transformation (e.g., log, linear). But the tick labels
    +themselves do not share properties.  This is a feature and not a bug,
    +because you may want to make the tick labels smaller on the upper
    +axes, e.g., in the example below.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +t = np.arange(0.01, 5.0, 0.01)
    +s1 = np.sin(2 * np.pi * t)
    +s2 = np.exp(-t)
    +s3 = np.sin(4 * np.pi * t)
    +
    +ax1 = plt.subplot(311)
    +plt.plot(t, s1)
    +# reduce the fontsize of the tick labels
    +plt.tick_params('x', labelsize=6)
    +
    +# share x only
    +ax2 = plt.subplot(312, sharex=ax1)
    +plt.plot(t, s2)
    +# make these tick labels invisible
    +plt.tick_params('x', labelbottom=False)
    +
    +# share x and y
    +ax3 = plt.subplot(313, sharex=ax1, sharey=ax1)
    +plt.plot(t, s3)
    +plt.xlim(0.01, 5.0)
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: axis
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/subfigures.py b/galleries/examples/subplots_axes_and_figures/subfigures.py
    new file mode 100644
    index 000000000000..cbe62f57d6b1
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/subfigures.py
    @@ -0,0 +1,152 @@
    +"""
    +=================
    +Figure subfigures
    +=================
    +
    +Sometimes it is desirable to have a figure with two different layouts in it.
    +This can be achieved with
    +:doc:`nested gridspecs`,
    +but having a virtual figure with its own artists is helpful, so
    +Matplotlib also has "subfigures", accessed by calling
    +`matplotlib.figure.Figure.add_subfigure` in a way that is analogous to
    +`matplotlib.figure.Figure.add_subplot`, or
    +`matplotlib.figure.Figure.subfigures` to make an array of subfigures.  Note
    +that subfigures can also have their own child subfigures.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def example_plot(ax, fontsize=12, hide_labels=False):
    +    pc = ax.pcolormesh(np.random.randn(30, 30), vmin=-2.5, vmax=2.5)
    +    if not hide_labels:
    +        ax.set_xlabel('x-label', fontsize=fontsize)
    +        ax.set_ylabel('y-label', fontsize=fontsize)
    +        ax.set_title('Title', fontsize=fontsize)
    +    return pc
    +
    +np.random.seed(19680808)
    +# gridspec inside gridspec
    +fig = plt.figure(layout='constrained', figsize=(10, 4))
    +subfigs = fig.subfigures(1, 2, wspace=0.07)
    +
    +axsLeft = subfigs[0].subplots(1, 2, sharey=True)
    +subfigs[0].set_facecolor('0.75')
    +for ax in axsLeft:
    +    pc = example_plot(ax)
    +subfigs[0].suptitle('Left plots', fontsize='x-large')
    +subfigs[0].colorbar(pc, shrink=0.6, ax=axsLeft, location='bottom')
    +
    +axsRight = subfigs[1].subplots(3, 1, sharex=True)
    +for nn, ax in enumerate(axsRight):
    +    pc = example_plot(ax, hide_labels=True)
    +    if nn == 2:
    +        ax.set_xlabel('xlabel')
    +    if nn == 1:
    +        ax.set_ylabel('ylabel')
    +
    +subfigs[1].set_facecolor('0.85')
    +subfigs[1].colorbar(pc, shrink=0.6, ax=axsRight)
    +subfigs[1].suptitle('Right plots', fontsize='x-large')
    +
    +fig.suptitle('Figure suptitle', fontsize='xx-large')
    +
    +plt.show()
    +
    +# %%
    +# It is possible to mix subplots and subfigures using
    +# `matplotlib.figure.Figure.add_subfigure`.  This requires getting
    +# the gridspec that the subplots are laid out on.
    +
    +fig, axs = plt.subplots(2, 3, layout='constrained', figsize=(10, 4))
    +gridspec = axs[0, 0].get_subplotspec().get_gridspec()
    +
    +# clear the left column for the subfigure:
    +for a in axs[:, 0]:
    +    a.remove()
    +
    +# plot data in remaining Axes:
    +for a in axs[:, 1:].flat:
    +    a.plot(np.arange(10))
    +
    +# make the subfigure in the empty gridspec slots:
    +subfig = fig.add_subfigure(gridspec[:, 0])
    +
    +axsLeft = subfig.subplots(1, 2, sharey=True)
    +subfig.set_facecolor('0.75')
    +for ax in axsLeft:
    +    pc = example_plot(ax)
    +subfig.suptitle('Left plots', fontsize='x-large')
    +subfig.colorbar(pc, shrink=0.6, ax=axsLeft, location='bottom')
    +
    +fig.suptitle('Figure suptitle', fontsize='xx-large')
    +plt.show()
    +
    +# %%
    +# Subfigures can have different widths and heights.  This is exactly the
    +# same example as the first example, but *width_ratios* has been changed:
    +
    +fig = plt.figure(layout='constrained', figsize=(10, 4))
    +subfigs = fig.subfigures(1, 2, wspace=0.07, width_ratios=[2, 1])
    +
    +axsLeft = subfigs[0].subplots(1, 2, sharey=True)
    +subfigs[0].set_facecolor('0.75')
    +for ax in axsLeft:
    +    pc = example_plot(ax)
    +subfigs[0].suptitle('Left plots', fontsize='x-large')
    +subfigs[0].colorbar(pc, shrink=0.6, ax=axsLeft, location='bottom')
    +
    +axsRight = subfigs[1].subplots(3, 1, sharex=True)
    +for nn, ax in enumerate(axsRight):
    +    pc = example_plot(ax, hide_labels=True)
    +    if nn == 2:
    +        ax.set_xlabel('xlabel')
    +    if nn == 1:
    +        ax.set_ylabel('ylabel')
    +
    +subfigs[1].set_facecolor('0.85')
    +subfigs[1].colorbar(pc, shrink=0.6, ax=axsRight)
    +subfigs[1].suptitle('Right plots', fontsize='x-large')
    +
    +fig.suptitle('Figure suptitle', fontsize='xx-large')
    +
    +plt.show()
    +
    +# %%
    +# Subfigures can be also be nested:
    +
    +fig = plt.figure(layout='constrained', figsize=(10, 8))
    +
    +fig.suptitle('fig')
    +
    +subfigs = fig.subfigures(1, 2, wspace=0.07)
    +
    +subfigs[0].set_facecolor('coral')
    +subfigs[0].suptitle('subfigs[0]')
    +
    +subfigs[1].set_facecolor('coral')
    +subfigs[1].suptitle('subfigs[1]')
    +
    +subfigsnest = subfigs[0].subfigures(2, 1, height_ratios=[1, 1.4])
    +subfigsnest[0].suptitle('subfigsnest[0]')
    +subfigsnest[0].set_facecolor('r')
    +axsnest0 = subfigsnest[0].subplots(1, 2, sharey=True)
    +for nn, ax in enumerate(axsnest0):
    +    pc = example_plot(ax, hide_labels=True)
    +subfigsnest[0].colorbar(pc, ax=axsnest0)
    +
    +subfigsnest[1].suptitle('subfigsnest[1]')
    +subfigsnest[1].set_facecolor('g')
    +axsnest1 = subfigsnest[1].subplots(3, 1, sharex=True)
    +
    +axsRight = subfigs[1].subplots(2, 2)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: figure
    +#    plot-type: pcolormesh
    +#    level: intermediate
    diff --git a/galleries/examples/subplots_axes_and_figures/subplot.py b/galleries/examples/subplots_axes_and_figures/subplot.py
    new file mode 100644
    index 000000000000..e23b86fa3e9c
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/subplot.py
    @@ -0,0 +1,58 @@
    +"""
    +=================
    +Multiple subplots
    +=================
    +
    +Simple demo with multiple subplots.
    +
    +For more options, see :doc:`/gallery/subplots_axes_and_figures/subplots_demo`.
    +
    +.. redirect-from:: /gallery/subplots_axes_and_figures/subplot_demo
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Create some fake data.
    +x1 = np.linspace(0.0, 5.0)
    +y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
    +x2 = np.linspace(0.0, 2.0)
    +y2 = np.cos(2 * np.pi * x2)
    +
    +# %%
    +# `~.pyplot.subplots()` is the recommended method to generate simple subplot
    +# arrangements:
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1)
    +fig.suptitle('A tale of 2 subplots')
    +
    +ax1.plot(x1, y1, 'o-')
    +ax1.set_ylabel('Damped oscillation')
    +
    +ax2.plot(x2, y2, '.-')
    +ax2.set_xlabel('time (s)')
    +ax2.set_ylabel('Undamped')
    +
    +plt.show()
    +
    +# %%
    +# Subplots can also be generated one at a time using `~.pyplot.subplot()`:
    +
    +plt.subplot(2, 1, 1)
    +plt.plot(x1, y1, 'o-')
    +plt.title('A tale of 2 subplots')
    +plt.ylabel('Damped oscillation')
    +
    +plt.subplot(2, 1, 2)
    +plt.plot(x2, y2, '.-')
    +plt.xlabel('time (s)')
    +plt.ylabel('Undamped')
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/subplot2grid.py b/galleries/examples/subplots_axes_and_figures/subplot2grid.py
    new file mode 100644
    index 000000000000..cd500ccf65b2
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/subplot2grid.py
    @@ -0,0 +1,32 @@
    +"""
    +============
    +subplot2grid
    +============
    +
    +This example demonstrates the use of `.pyplot.subplot2grid` to generate
    +subplots.  Using `.GridSpec`, as demonstrated in
    +:doc:`/gallery/subplots_axes_and_figures/gridspec_customization` is
    +generally preferred.
    +
    +.. redirect-from:: /gallery/userdemo/demo_gridspec01
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def annotate_axes(fig):
    +    for i, ax in enumerate(fig.axes):
    +        ax.text(0.5, 0.5, "ax%d" % (i+1), va="center", ha="center")
    +        ax.tick_params(labelbottom=False, labelleft=False)
    +
    +
    +fig = plt.figure()
    +ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
    +ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
    +ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
    +ax4 = plt.subplot2grid((3, 3), (2, 0))
    +ax5 = plt.subplot2grid((3, 3), (2, 1))
    +
    +annotate_axes(fig)
    +
    +plt.show()
    diff --git a/galleries/examples/subplots_axes_and_figures/subplots_adjust.py b/galleries/examples/subplots_axes_and_figures/subplots_adjust.py
    new file mode 100644
    index 000000000000..8e3b876adfeb
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/subplots_adjust.py
    @@ -0,0 +1,38 @@
    +"""
    +=============================
    +Subplots spacings and margins
    +=============================
    +
    +Adjusting the spacing of margins and subplots using `.pyplot.subplots_adjust`.
    +
    +.. note::
    +   There is also a tool window to adjust the margins and spacings of displayed
    +   figures interactively.  It can be opened via the toolbar or by calling
    +   `.pyplot.subplot_tool`.
    +
    +.. redirect-from:: /gallery/subplots_axes_and_figures/subplot_toolbar
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +plt.subplot(211)
    +plt.imshow(np.random.random((100, 100)))
    +plt.subplot(212)
    +plt.imshow(np.random.random((100, 100)))
    +
    +plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
    +cax = plt.axes((0.85, 0.1, 0.075, 0.8))
    +plt.colorbar(cax=cax)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot
    +#    plot-type: imshow
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/subplots_demo.py b/galleries/examples/subplots_axes_and_figures/subplots_demo.py
    new file mode 100644
    index 000000000000..0e3cb1102230
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/subplots_demo.py
    @@ -0,0 +1,222 @@
    +"""
    +===============================================
    +Create multiple subplots using ``plt.subplots``
    +===============================================
    +
    +`.pyplot.subplots` creates a figure and a grid of subplots with a single call,
    +while providing reasonable control over how the individual plots are created.
    +For more advanced use cases you can use `.GridSpec` for a more general subplot
    +layout or `.Figure.add_subplot` for adding subplots at arbitrary locations
    +within the figure.
    +"""
    +
    +# sphinx_gallery_thumbnail_number = 11
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Some example data to display
    +x = np.linspace(0, 2 * np.pi, 400)
    +y = np.sin(x ** 2)
    +
    +# %%
    +# A figure with just one subplot
    +# """"""""""""""""""""""""""""""
    +#
    +# ``subplots()`` without arguments returns a `.Figure` and a single
    +# `~.axes.Axes`.
    +#
    +# This is actually the simplest and recommended way of creating a single
    +# Figure and Axes.
    +
    +fig, ax = plt.subplots()
    +ax.plot(x, y)
    +ax.set_title('A single plot')
    +
    +# %%
    +# Stacking subplots in one direction
    +# """"""""""""""""""""""""""""""""""
    +#
    +# The first two optional arguments of `.pyplot.subplots` define the number of
    +# rows and columns of the subplot grid.
    +#
    +# When stacking in one direction only, the returned ``axs`` is a 1D numpy array
    +# containing the list of created Axes.
    +
    +fig, axs = plt.subplots(2)
    +fig.suptitle('Vertically stacked subplots')
    +axs[0].plot(x, y)
    +axs[1].plot(x, -y)
    +
    +# %%
    +# If you are creating just a few Axes, it's handy to unpack them immediately to
    +# dedicated variables for each Axes. That way, we can use ``ax1`` instead of
    +# the more verbose ``axs[0]``.
    +
    +fig, (ax1, ax2) = plt.subplots(2)
    +fig.suptitle('Vertically stacked subplots')
    +ax1.plot(x, y)
    +ax2.plot(x, -y)
    +
    +# %%
    +# To obtain side-by-side subplots, pass parameters ``1, 2`` for one row and two
    +# columns.
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +fig.suptitle('Horizontally stacked subplots')
    +ax1.plot(x, y)
    +ax2.plot(x, -y)
    +
    +# %%
    +# Stacking subplots in two directions
    +# """""""""""""""""""""""""""""""""""
    +#
    +# When stacking in two directions, the returned ``axs`` is a 2D NumPy array.
    +#
    +# If you have to set parameters for each subplot it's handy to iterate over
    +# all subplots in a 2D grid using ``for ax in axs.flat:``.
    +
    +fig, axs = plt.subplots(2, 2)
    +axs[0, 0].plot(x, y)
    +axs[0, 0].set_title('Axis [0, 0]')
    +axs[0, 1].plot(x, y, 'tab:orange')
    +axs[0, 1].set_title('Axis [0, 1]')
    +axs[1, 0].plot(x, -y, 'tab:green')
    +axs[1, 0].set_title('Axis [1, 0]')
    +axs[1, 1].plot(x, -y, 'tab:red')
    +axs[1, 1].set_title('Axis [1, 1]')
    +
    +for ax in axs.flat:
    +    ax.set(xlabel='x-label', ylabel='y-label')
    +
    +# Hide x labels and tick labels for top plots and y ticks for right plots.
    +for ax in axs.flat:
    +    ax.label_outer()
    +
    +# %%
    +# You can use tuple-unpacking also in 2D to assign all subplots to dedicated
    +# variables:
    +
    +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
    +fig.suptitle('Sharing x per column, y per row')
    +ax1.plot(x, y)
    +ax2.plot(x, y**2, 'tab:orange')
    +ax3.plot(x, -y, 'tab:green')
    +ax4.plot(x, -y**2, 'tab:red')
    +
    +for ax in fig.get_axes():
    +    ax.label_outer()
    +
    +# %%
    +# Sharing axes
    +# """"""""""""
    +#
    +# By default, each Axes is scaled individually. Thus, if the ranges are
    +# different the tick values of the subplots do not align.
    +
    +fig, (ax1, ax2) = plt.subplots(2)
    +fig.suptitle('Axes values are scaled individually by default')
    +ax1.plot(x, y)
    +ax2.plot(x + 1, -y)
    +
    +# %%
    +# You can use *sharex* or *sharey* to align the horizontal or vertical axis.
    +
    +fig, (ax1, ax2) = plt.subplots(2, sharex=True)
    +fig.suptitle('Aligning x-axis using sharex')
    +ax1.plot(x, y)
    +ax2.plot(x + 1, -y)
    +
    +# %%
    +# Setting *sharex* or *sharey* to ``True`` enables global sharing across the
    +# whole grid, i.e. also the y-axes of vertically stacked subplots have the
    +# same scale when using ``sharey=True``.
    +
    +fig, axs = plt.subplots(3, sharex=True, sharey=True)
    +fig.suptitle('Sharing both axes')
    +axs[0].plot(x, y ** 2)
    +axs[1].plot(x, 0.3 * y, 'o')
    +axs[2].plot(x, y, '+')
    +
    +# %%
    +# For subplots that are sharing axes one set of tick labels is enough. Tick
    +# labels of inner Axes are automatically removed by *sharex* and *sharey*.
    +# Still there remains an unused empty space between the subplots.
    +#
    +# To precisely control the positioning of the subplots, one can explicitly
    +# create a `.GridSpec` with `.Figure.add_gridspec`, and then call its
    +# `~.GridSpecBase.subplots` method.  For example, we can reduce the height
    +# between vertical subplots using ``add_gridspec(hspace=0)``.
    +#
    +# `.label_outer` is a handy method to remove labels and ticks from subplots
    +# that are not at the edge of the grid.
    +
    +fig = plt.figure()
    +gs = fig.add_gridspec(3, hspace=0)
    +axs = gs.subplots(sharex=True, sharey=True)
    +fig.suptitle('Sharing both axes')
    +axs[0].plot(x, y ** 2)
    +axs[1].plot(x, 0.3 * y, 'o')
    +axs[2].plot(x, y, '+')
    +
    +# Hide x labels and tick labels for all but bottom plot.
    +for ax in axs:
    +    ax.label_outer()
    +
    +# %%
    +# Apart from ``True`` and ``False``, both *sharex* and *sharey* accept the
    +# values 'row' and 'col' to share the values only per row or column.
    +
    +fig = plt.figure()
    +gs = fig.add_gridspec(2, 2, hspace=0, wspace=0)
    +(ax1, ax2), (ax3, ax4) = gs.subplots(sharex='col', sharey='row')
    +fig.suptitle('Sharing x per column, y per row')
    +ax1.plot(x, y)
    +ax2.plot(x, y**2, 'tab:orange')
    +ax3.plot(x + 1, -y, 'tab:green')
    +ax4.plot(x + 2, -y**2, 'tab:red')
    +
    +for ax in fig.get_axes():
    +    ax.label_outer()
    +
    +# %%
    +# If you want a more complex sharing structure, you can first create the
    +# grid of Axes with no sharing, and then call `.axes.Axes.sharex` or
    +# `.axes.Axes.sharey` to add sharing info a posteriori.
    +
    +fig, axs = plt.subplots(2, 2)
    +axs[0, 0].plot(x, y)
    +axs[0, 0].set_title("main")
    +axs[1, 0].plot(x, y**2)
    +axs[1, 0].set_title("shares x with main")
    +axs[1, 0].sharex(axs[0, 0])
    +axs[0, 1].plot(x + 1, y + 1)
    +axs[0, 1].set_title("unrelated")
    +axs[1, 1].plot(x + 2, y + 2)
    +axs[1, 1].set_title("also unrelated")
    +fig.tight_layout()
    +
    +# %%
    +# Polar Axes
    +# """"""""""
    +#
    +# The parameter *subplot_kw* of `.pyplot.subplots` controls the subplot
    +# properties (see also `.Figure.add_subplot`). In particular, this can be used
    +# to create a grid of polar Axes.
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2, subplot_kw=dict(projection='polar'))
    +ax1.plot(x, y)
    +ax2.plot(x, y ** 2)
    +
    +plt.show()
    +
    +# %%
    +# .. tags::
    +#
    +#    component: subplot,
    +#    component: axes,
    +#    component: axis
    +#    plot-type: line,
    +#    plot-type: polar
    +#    level: beginner
    +#    purpose: showcase
    diff --git a/galleries/examples/subplots_axes_and_figures/two_scales.py b/galleries/examples/subplots_axes_and_figures/two_scales.py
    new file mode 100644
    index 000000000000..ea31f93c4251
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/two_scales.py
    @@ -0,0 +1,62 @@
    +"""
    +===========================
    +Plots with different scales
    +===========================
    +
    +Two plots on the same Axes with different left and right scales.
    +
    +The trick is to use *two different Axes* that share the same *x* axis.
    +You can use separate `matplotlib.ticker` formatters and locators as
    +desired since the two Axes are independent.
    +
    +Such Axes are generated by calling the `.Axes.twinx` method. Likewise,
    +`.Axes.twiny` is available to generate Axes that share a *y* axis but
    +have different top and bottom scales.
    +
    +See also :doc:`/gallery/subplots_axes_and_figures/secondary_axis` for the case
    +where the two scales are not independent, but related (e.g., the same quantity
    +in two different units).
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Create some mock data
    +t = np.arange(0.01, 10.0, 0.01)
    +data1 = np.exp(t)
    +data2 = np.sin(2 * np.pi * t)
    +
    +fig, ax1 = plt.subplots()
    +
    +color = 'tab:red'
    +ax1.set_xlabel('time (s)')
    +ax1.set_ylabel('exp', color=color)
    +ax1.plot(t, data1, color=color)
    +ax1.tick_params(axis='y', labelcolor=color)
    +
    +ax2 = ax1.twinx()  # instantiate a second Axes that shares the same x-axis
    +
    +color = 'tab:blue'
    +ax2.set_ylabel('sin', color=color)  # we already handled the x-label with ax1
    +ax2.plot(t, data2, color=color)
    +ax2.tick_params(axis='y', labelcolor=color)
    +
    +fig.tight_layout()  # otherwise the right y-label is slightly clipped
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.twinx` / `matplotlib.pyplot.twinx`
    +#    - `matplotlib.axes.Axes.twiny` / `matplotlib.pyplot.twiny`
    +#    - `matplotlib.axes.Axes.tick_params` / `matplotlib.pyplot.tick_params`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: line
    +#    level: beginner
    diff --git a/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py b/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py
    new file mode 100644
    index 000000000000..4453b3ec39f1
    --- /dev/null
    +++ b/galleries/examples/subplots_axes_and_figures/zoom_inset_axes.py
    @@ -0,0 +1,51 @@
    +"""
    +======================
    +Zoom region inset Axes
    +======================
    +
    +Example of an inset Axes and a rectangle showing where the zoom is located.
    +"""
    +
    +import numpy as np
    +
    +from matplotlib import cbook
    +from matplotlib import pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +# make data
    +Z = cbook.get_sample_data("axes_grid/bivariate_normal.npy")  # 15x15 array
    +Z2 = np.zeros((150, 150))
    +ny, nx = Z.shape
    +Z2[30:30+ny, 30:30+nx] = Z
    +extent = (-3, 4, -4, 3)
    +
    +ax.imshow(Z2, extent=extent, origin="lower")
    +
    +# inset Axes....
    +x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9  # subregion of the original image
    +axins = ax.inset_axes(
    +    [0.5, 0.5, 0.47, 0.47],
    +    xlim=(x1, x2), ylim=(y1, y2), xticklabels=[], yticklabels=[])
    +axins.imshow(Z2, extent=extent, origin="lower")
    +
    +ax.indicate_inset_zoom(axins, edgecolor="black")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.inset_axes`
    +#    - `matplotlib.axes.Axes.indicate_inset_zoom`
    +#    - `matplotlib.axes.Axes.imshow`
    +#
    +# .. tags::
    +#
    +#    component: axes
    +#    plot-type: imshow
    +#    level: intermediate
    diff --git a/galleries/examples/text_labels_and_annotations/README.txt b/galleries/examples/text_labels_and_annotations/README.txt
    new file mode 100644
    index 000000000000..565fd8915c86
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/README.txt
    @@ -0,0 +1,4 @@
    +.. _text_labels_and_annotations:
    +
    +Text, labels and annotations
    +============================
    diff --git a/galleries/examples/text_labels_and_annotations/accented_text.py b/galleries/examples/text_labels_and_annotations/accented_text.py
    new file mode 100644
    index 000000000000..7ff080afefd9
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/accented_text.py
    @@ -0,0 +1,36 @@
    +r"""
    +=============
    +Accented text
    +=============
    +
    +Matplotlib supports accented characters via TeX mathtext or Unicode.
    +
    +Using mathtext, the following accents are provided: \\hat, \\breve, \\grave,
    +\\bar, \\acute, \\tilde, \\vec, \\dot, \\ddot.  All of them have the same
    +syntax, e.g. \\bar{o} yields "o overbar", \\ddot{o} yields "o umlaut".
    +Shortcuts such as \\"o \\'e \\`e \\~n \\.x \\^y are also supported.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +# Mathtext demo
    +fig, ax = plt.subplots()
    +ax.plot(range(10))
    +ax.set_title(r'$\ddot{o}\acute{e}\grave{e}\hat{O}'
    +             r'\breve{i}\bar{A}\tilde{n}\vec{q}$', fontsize=20)
    +
    +# Shorthand is also supported and curly braces are optional
    +ax.set_xlabel(r"""$\"o\ddot o \'e\`e\~n\.x\^y$""", fontsize=20)
    +ax.text(4, 0.5, r"$F=m\ddot{x}$")
    +fig.tight_layout()
    +
    +# %%
    +# You can also use Unicode characters directly in strings.
    +fig, ax = plt.subplots()
    +ax.set_title("GISCARD CHAHUTÉ À L'ASSEMBLÉE")
    +ax.set_xlabel("LE COUP DE DÉ DE DE GAULLE")
    +ax.set_ylabel('André was here!')
    +ax.text(0.2, 0.8, 'Institut für Festkörperphysik', rotation=45)
    +ax.text(0.4, 0.2, 'AVA (check kerning)')
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/angle_annotation.py b/galleries/examples/text_labels_and_annotations/angle_annotation.py
    new file mode 100644
    index 000000000000..178f54863477
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/angle_annotation.py
    @@ -0,0 +1,326 @@
    +"""
    +===========================
    +Scale invariant angle label
    +===========================
    +
    +This example shows how to create a scale invariant angle annotation. It is
    +often useful to mark angles between lines or inside shapes with a circular arc.
    +While Matplotlib provides an `~.patches.Arc`, an inherent problem when directly
    +using it for such purposes is that an arc being circular in data space is not
    +necessarily circular in display space. Also, the arc's radius is often best
    +defined in a coordinate system which is independent of the actual data
    +coordinates - at least if you want to be able to freely zoom into your plot
    +without the annotation growing to infinity.
    +
    +This calls for a solution where the arc's center is defined in data space, but
    +its radius in a physical unit like points or pixels, or as a ratio of the Axes
    +dimension. The following ``AngleAnnotation`` class provides such solution.
    +
    +The example below serves two purposes:
    +
    +* It provides a ready-to-use solution for the problem of easily drawing angles
    +  in graphs.
    +* It shows how to subclass a Matplotlib artist to enhance its functionality, as
    +  well as giving a hands-on example on how to use Matplotlib's :ref:`transform
    +  system `.
    +
    +If mainly interested in the former, you may copy the below class and jump to
    +the :ref:`angle-annotation-usage` section.
    +"""
    +
    +# %%
    +# AngleAnnotation class
    +# ---------------------
    +# The essential idea here is to subclass `~.patches.Arc` and set its transform
    +# to the `~.transforms.IdentityTransform`, making the parameters of the arc
    +# defined in pixel space.
    +# We then override the ``Arc``'s attributes ``_center``, ``theta1``,
    +# ``theta2``, ``width`` and ``height`` and make them properties, coupling to
    +# internal methods that calculate the respective parameters each time the
    +# attribute is accessed and thereby ensuring that the arc in pixel space stays
    +# synchronized with the input points and size.
    +# For example, each time the arc's drawing method would query its ``_center``
    +# attribute, instead of receiving the same number all over again, it will
    +# instead receive the result of the ``get_center_in_pixels`` method we defined
    +# in the subclass. This method transforms the center in data coordinates to
    +# pixels via the Axes transform ``ax.transData``. The size and the angles are
    +# calculated in a similar fashion, such that the arc changes its shape
    +# automatically when e.g. zooming or panning interactively.
    +#
    +# The functionality of this class allows to annotate the arc with a text. This
    +# text is a `~.text.Annotation` stored in an attribute ``text``. Since the
    +# arc's position and radius are defined only at draw time, we need to update
    +# the text's position accordingly. This is done by reimplementing the ``Arc``'s
    +# ``draw()`` method to let it call an updating method for the text.
    +#
    +# The arc and the text will be added to the provided Axes at instantiation: it
    +# is hence not strictly necessary to keep a reference to it.
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Arc
    +from matplotlib.transforms import Bbox, IdentityTransform, TransformedBbox
    +
    +
    +class AngleAnnotation(Arc):
    +    """
    +    Draws an arc between two vectors which appears circular in display space.
    +    """
    +    def __init__(self, xy, p1, p2, size=75, unit="points", ax=None,
    +                 text="", textposition="inside", text_kw=None, **kwargs):
    +        """
    +        Parameters
    +        ----------
    +        xy, p1, p2 : tuple or array of two floats
    +            Center position and two points. Angle annotation is drawn between
    +            the two vectors connecting *p1* and *p2* with *xy*, respectively.
    +            Units are data coordinates.
    +
    +        size : float
    +            Diameter of the angle annotation in units specified by *unit*.
    +
    +        unit : str
    +            One of the following strings to specify the unit of *size*:
    +
    +            * "pixels": pixels
    +            * "points": points, use points instead of pixels to not have a
    +              dependence on the DPI
    +            * "axes width", "axes height": relative units of Axes width, height
    +            * "axes min", "axes max": minimum or maximum of relative Axes
    +              width, height
    +
    +        ax : `matplotlib.axes.Axes`
    +            The Axes to add the angle annotation to.
    +
    +        text : str
    +            The text to mark the angle with.
    +
    +        textposition : {"inside", "outside", "edge"}
    +            Whether to show the text in- or outside the arc. "edge" can be used
    +            for custom positions anchored at the arc's edge.
    +
    +        text_kw : dict
    +            Dictionary of arguments passed to the Annotation.
    +
    +        **kwargs
    +            Further parameters are passed to `matplotlib.patches.Arc`. Use this
    +            to specify, color, linewidth etc. of the arc.
    +
    +        """
    +        self.ax = ax or plt.gca()
    +        self._xydata = xy  # in data coordinates
    +        self.vec1 = p1
    +        self.vec2 = p2
    +        self.size = size
    +        self.unit = unit
    +        self.textposition = textposition
    +
    +        super().__init__(self._xydata, size, size, angle=0.0,
    +                         theta1=self.theta1, theta2=self.theta2, **kwargs)
    +
    +        self.set_transform(IdentityTransform())
    +        self.ax.add_patch(self)
    +
    +        self.kw = dict(ha="center", va="center",
    +                       xycoords=IdentityTransform(),
    +                       xytext=(0, 0), textcoords="offset points",
    +                       annotation_clip=True)
    +        self.kw.update(text_kw or {})
    +        self.text = ax.annotate(text, xy=self._center, **self.kw)
    +
    +    def get_size(self):
    +        factor = 1.
    +        if self.unit == "points":
    +            factor = self.ax.figure.dpi / 72.
    +        elif self.unit[:4] == "axes":
    +            b = TransformedBbox(Bbox.unit(), self.ax.transAxes)
    +            dic = {"max": max(b.width, b.height),
    +                   "min": min(b.width, b.height),
    +                   "width": b.width, "height": b.height}
    +            factor = dic[self.unit[5:]]
    +        return self.size * factor
    +
    +    def set_size(self, size):
    +        self.size = size
    +
    +    def get_center_in_pixels(self):
    +        """return center in pixels"""
    +        return self.ax.transData.transform(self._xydata)
    +
    +    def set_center(self, xy):
    +        """set center in data coordinates"""
    +        self._xydata = xy
    +
    +    def get_theta(self, vec):
    +        vec_in_pixels = self.ax.transData.transform(vec) - self._center
    +        return np.rad2deg(np.arctan2(vec_in_pixels[1], vec_in_pixels[0]))
    +
    +    def get_theta1(self):
    +        return self.get_theta(self.vec1)
    +
    +    def get_theta2(self):
    +        return self.get_theta(self.vec2)
    +
    +    def set_theta(self, angle):
    +        pass
    +
    +    # Redefine attributes of the Arc to always give values in pixel space
    +    _center = property(get_center_in_pixels, set_center)
    +    theta1 = property(get_theta1, set_theta)
    +    theta2 = property(get_theta2, set_theta)
    +    width = property(get_size, set_size)
    +    height = property(get_size, set_size)
    +
    +    # The following two methods are needed to update the text position.
    +    def draw(self, renderer):
    +        self.update_text()
    +        super().draw(renderer)
    +
    +    def update_text(self):
    +        c = self._center
    +        s = self.get_size()
    +        angle_span = (self.theta2 - self.theta1) % 360
    +        angle = np.deg2rad(self.theta1 + angle_span / 2)
    +        r = s / 2
    +        if self.textposition == "inside":
    +            r = s / np.interp(angle_span, [60, 90, 135, 180],
    +                                          [3.3, 3.5, 3.8, 4])
    +        self.text.xy = c + r * np.array([np.cos(angle), np.sin(angle)])
    +        if self.textposition == "outside":
    +            def R90(a, r, w, h):
    +                if a < np.arctan(h/2/(r+w/2)):
    +                    return np.sqrt((r+w/2)**2 + (np.tan(a)*(r+w/2))**2)
    +                else:
    +                    c = np.sqrt((w/2)**2+(h/2)**2)
    +                    T = np.arcsin(c * np.cos(np.pi/2 - a + np.arcsin(h/2/c))/r)
    +                    xy = r * np.array([np.cos(a + T), np.sin(a + T)])
    +                    xy += np.array([w/2, h/2])
    +                    return np.sqrt(np.sum(xy**2))
    +
    +            def R(a, r, w, h):
    +                aa = (a % (np.pi/4))*((a % (np.pi/2)) <= np.pi/4) + \
    +                     (np.pi/4 - (a % (np.pi/4)))*((a % (np.pi/2)) >= np.pi/4)
    +                return R90(aa, r, *[w, h][::int(np.sign(np.cos(2*a)))])
    +
    +            bbox = self.text.get_window_extent()
    +            X = R(angle, r, bbox.width, bbox.height)
    +            trans = self.ax.figure.dpi_scale_trans.inverted()
    +            offs = trans.transform(((X-s/2), 0))[0] * 72
    +            self.text.set_position([offs*np.cos(angle), offs*np.sin(angle)])
    +
    +
    +# %%
    +# .. _angle-annotation-usage:
    +#
    +# Usage
    +# -----
    +#
    +# Required arguments to ``AngleAnnotation`` are the center of the arc, *xy*,
    +# and two points, such that the arc spans between the two vectors connecting
    +# *p1* and *p2* with *xy*, respectively. Those are given in data coordinates.
    +# Further arguments are the *size* of the arc and its *unit*. Additionally, a
    +# *text* can be specified, that will be drawn either in- or outside of the arc,
    +# according to the value of *textposition*. Usage of those arguments is shown
    +# below.
    +
    +fig, ax = plt.subplots()
    +fig.canvas.draw()  # Need to draw the figure to define renderer
    +ax.set_title("AngleLabel example")
    +
    +# Plot two crossing lines and label each angle between them with the above
    +# ``AngleAnnotation`` tool.
    +center = (4.5, 650)
    +p1 = [(2.5, 710), (6.0, 605)]
    +p2 = [(3.0, 275), (5.5, 900)]
    +line1, = ax.plot(*zip(*p1))
    +line2, = ax.plot(*zip(*p2))
    +point, = ax.plot(*center, marker="o")
    +
    +am1 = AngleAnnotation(center, p1[1], p2[1], ax=ax, size=75, text=r"$\alpha$")
    +am2 = AngleAnnotation(center, p2[1], p1[0], ax=ax, size=35, text=r"$\beta$")
    +am3 = AngleAnnotation(center, p1[0], p2[0], ax=ax, size=75, text=r"$\gamma$")
    +am4 = AngleAnnotation(center, p2[0], p1[1], ax=ax, size=35, text=r"$\theta$")
    +
    +
    +# Showcase some styling options for the angle arc, as well as the text.
    +p = [(6.0, 400), (5.3, 410), (5.6, 300)]
    +ax.plot(*zip(*p))
    +am5 = AngleAnnotation(p[1], p[0], p[2], ax=ax, size=40, text=r"$\Phi$",
    +                      linestyle="--", color="gray", textposition="outside",
    +                      text_kw=dict(fontsize=16, color="gray"))
    +
    +
    +# %%
    +# ``AngleLabel`` options
    +# ----------------------
    +#
    +# The *textposition* and *unit* keyword arguments may be used to modify the
    +# location of the text label, as shown below:
    +
    +
    +# Helper function to draw angle easily.
    +def plot_angle(ax, pos, angle, length=0.95, acol="C0", **kwargs):
    +    vec2 = np.array([np.cos(np.deg2rad(angle)), np.sin(np.deg2rad(angle))])
    +    xy = np.c_[[length, 0], [0, 0], vec2*length].T + np.array(pos)
    +    ax.plot(*xy.T, color=acol)
    +    return AngleAnnotation(pos, xy[0], xy[2], ax=ax, **kwargs)
    +
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
    +fig.suptitle("AngleLabel keyword arguments")
    +fig.canvas.draw()  # Need to draw the figure to define renderer
    +
    +# Showcase different text positions.
    +ax1.margins(y=0.4)
    +ax1.set_title("textposition")
    +kw = dict(size=75, unit="points", text=r"$60°$")
    +
    +am6 = plot_angle(ax1, (2.0, 0), 60, textposition="inside", **kw)
    +am7 = plot_angle(ax1, (3.5, 0), 60, textposition="outside", **kw)
    +am8 = plot_angle(ax1, (5.0, 0), 60, textposition="edge",
    +                 text_kw=dict(bbox=dict(boxstyle="round", fc="w")), **kw)
    +am9 = plot_angle(ax1, (6.5, 0), 60, textposition="edge",
    +                 text_kw=dict(xytext=(30, 20), arrowprops=dict(arrowstyle="->",
    +                              connectionstyle="arc3,rad=-0.2")), **kw)
    +
    +for x, text in zip([2.0, 3.5, 5.0, 6.5], ['"inside"', '"outside"', '"edge"',
    +                                          '"edge", custom arrow']):
    +    ax1.annotate(text, xy=(x, 0), xycoords=ax1.get_xaxis_transform(),
    +                 bbox=dict(boxstyle="round", fc="w"), ha="left", fontsize=8,
    +                 annotation_clip=True)
    +
    +# Showcase different size units. The effect of this can best be observed
    +# by interactively changing the figure size
    +ax2.margins(y=0.4)
    +ax2.set_title("unit")
    +kw = dict(text=r"$60°$", textposition="outside")
    +
    +am10 = plot_angle(ax2, (2.0, 0), 60, size=50, unit="pixels", **kw)
    +am11 = plot_angle(ax2, (3.5, 0), 60, size=50, unit="points", **kw)
    +am12 = plot_angle(ax2, (5.0, 0), 60, size=0.25, unit="axes min", **kw)
    +am13 = plot_angle(ax2, (6.5, 0), 60, size=0.25, unit="axes max", **kw)
    +
    +for x, text in zip([2.0, 3.5, 5.0, 6.5], ['"pixels"', '"points"',
    +                                          '"axes min"', '"axes max"']):
    +    ax2.annotate(text, xy=(x, 0), xycoords=ax2.get_xaxis_transform(),
    +                 bbox=dict(boxstyle="round", fc="w"), ha="left", fontsize=8,
    +                 annotation_clip=True)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches.Arc`
    +#    - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate`
    +#    - `matplotlib.text.Annotation`
    +#    - `matplotlib.transforms.IdentityTransform`
    +#    - `matplotlib.transforms.TransformedBbox`
    +#    - `matplotlib.transforms.Bbox`
    diff --git a/galleries/examples/text_labels_and_annotations/angles_on_bracket_arrows.py b/galleries/examples/text_labels_and_annotations/angles_on_bracket_arrows.py
    new file mode 100644
    index 000000000000..a23c7adc3897
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/angles_on_bracket_arrows.py
    @@ -0,0 +1,67 @@
    +"""
    +===================================
    +Angle annotations on bracket arrows
    +===================================
    +
    +This example shows how to add angle annotations to bracket arrow styles
    +created using `.FancyArrowPatch`. *angleA* and *angleB* are measured from a
    +vertical line as positive (to the left) or negative (to the right). Blue
    +`.FancyArrowPatch` arrows indicate the directions of *angleA* and *angleB*
    +from the vertical and axes text annotate the angle sizes.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import FancyArrowPatch
    +
    +
    +def get_point_of_rotated_vertical(origin, line_length, degrees):
    +    """Return xy coordinates of the vertical line end rotated by degrees."""
    +    rad = np.deg2rad(-degrees)
    +    return [origin[0] + line_length * np.sin(rad),
    +            origin[1] + line_length * np.cos(rad)]
    +
    +
    +fig, ax = plt.subplots()
    +ax.set(xlim=(0, 6), ylim=(-1, 5))
    +ax.set_title("Orientation of the bracket arrows relative to angleA and angleB")
    +
    +style = ']-['
    +for i, angle in enumerate([-40, 0, 60]):
    +    y = 2*i
    +    arrow_centers = ((1, y), (5, y))
    +    vlines = ((1, y + 0.5), (5, y + 0.5))
    +    anglesAB = (angle, -angle)
    +    bracketstyle = f"{style}, angleA={anglesAB[0]}, angleB={anglesAB[1]}"
    +    bracket = FancyArrowPatch(*arrow_centers, arrowstyle=bracketstyle,
    +                              mutation_scale=42)
    +    ax.add_patch(bracket)
    +    ax.text(3, y + 0.05, bracketstyle, ha="center", va="bottom", fontsize=14)
    +    ax.vlines([line[0] for line in vlines], [y, y], [line[1] for line in vlines],
    +              linestyles="--", color="C0")
    +    # Get the top coordinates for the drawn patches at A and B
    +    patch_tops = [get_point_of_rotated_vertical(center, 0.5, angle)
    +                  for center, angle in zip(arrow_centers, anglesAB)]
    +    # Define the connection directions for the annotation arrows
    +    connection_dirs = (1, -1) if angle > 0 else (-1, 1)
    +    # Add arrows and annotation text
    +    arrowstyle = "Simple, tail_width=0.5, head_width=4, head_length=8"
    +    for vline, dir, patch_top, angle in zip(vlines, connection_dirs,
    +                                            patch_tops, anglesAB):
    +        kw = dict(connectionstyle=f"arc3,rad={dir * 0.5}",
    +                  arrowstyle=arrowstyle, color="C0")
    +        ax.add_patch(FancyArrowPatch(vline, patch_top, **kw))
    +        ax.text(vline[0] - dir * 0.15, y + 0.7, f'{angle}°', ha="center",
    +                va="center")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches.ArrowStyle`
    diff --git a/galleries/examples/text_labels_and_annotations/annotation_basic.py b/galleries/examples/text_labels_and_annotations/annotation_basic.py
    new file mode 100644
    index 000000000000..45211558ec44
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/annotation_basic.py
    @@ -0,0 +1,36 @@
    +"""
    +=================
    +Annotating a plot
    +=================
    +
    +This example shows how to annotate a plot with an arrow pointing to provided
    +coordinates. We modify the defaults of the arrow, to "shrink" it.
    +
    +For a complete overview of the annotation capabilities, also see the
    +:ref:`annotation tutorial`.
    +
    +.. redirect-from:: /gallery/pyplots/annotation_basic
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, ax = plt.subplots()
    +
    +t = np.arange(0.0, 5.0, 0.01)
    +s = np.cos(2*np.pi*t)
    +line, = ax.plot(t, s, lw=2)
    +
    +ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            )
    +ax.set_ylim(-2, 2)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate`
    diff --git a/galleries/examples/text_labels_and_annotations/annotation_demo.py b/galleries/examples/text_labels_and_annotations/annotation_demo.py
    new file mode 100644
    index 000000000000..562948bcc512
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/annotation_demo.py
    @@ -0,0 +1,388 @@
    +"""
    +==============
    +Annotate plots
    +==============
    +
    +The following examples show ways to annotate plots in Matplotlib.
    +This includes highlighting specific points of interest and using various
    +visual tools to call attention to this point. For a more complete and in-depth
    +description of the annotation and text tools in Matplotlib, see the
    +:ref:`tutorial on annotation `.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.patches import Ellipse
    +from matplotlib.text import OffsetFrom
    +
    +# %%
    +# Specifying text points and annotation points
    +# --------------------------------------------
    +#
    +# You must specify an annotation point ``xy=(x, y)`` to annotate this point.
    +# Additionally, you may specify a text point ``xytext=(x, y)`` for the location
    +# of the text for this annotation.  Optionally, you can specify the coordinate
    +# system of *xy* and *xytext* with one of the following strings for *xycoords*
    +# and *textcoords* (default is 'data')::
    +#
    +#  'figure points'   : points from the lower left corner of the figure
    +#  'figure pixels'   : pixels from the lower left corner of the figure
    +#  'figure fraction' : (0, 0) is lower left of figure and (1, 1) is upper right
    +#  'axes points'     : points from lower left corner of the Axes
    +#  'axes pixels'     : pixels from lower left corner of the Axes
    +#  'axes fraction'   : (0, 0) is lower left of Axes and (1, 1) is upper right
    +#  'offset points'   : Specify an offset (in points) from the xy value
    +#  'offset pixels'   : Specify an offset (in pixels) from the xy value
    +#  'data'            : use the Axes data coordinate system
    +#
    +# Note: for physical coordinate systems (points or pixels) the origin is the
    +# (bottom, left) of the figure or Axes.
    +#
    +# Optionally, you can specify arrow properties which draws and arrow
    +# from the text to the annotated point by giving a dictionary of arrow
    +# properties
    +#
    +# Valid keys are::
    +#
    +#   width : the width of the arrow in points
    +#   frac  : the fraction of the arrow length occupied by the head
    +#   headwidth : the width of the base of the arrow head in points
    +#   shrink : move the tip and base some percent away from the
    +#            annotated point and text
    +#   any key for matplotlib.patches.polygon  (e.g., facecolor)
    +
    +# Create our figure and data we'll use for plotting
    +fig, ax = plt.subplots(figsize=(4, 4))
    +
    +t = np.arange(0.0, 5.0, 0.01)
    +s = np.cos(2*np.pi*t)
    +
    +# Plot a line and add some simple annotations
    +line, = ax.plot(t, s)
    +ax.annotate('figure pixels',
    +            xy=(10, 10), xycoords='figure pixels')
    +ax.annotate('figure points',
    +            xy=(107, 110), xycoords='figure points',
    +            fontsize=12)
    +ax.annotate('figure fraction',
    +            xy=(.025, .975), xycoords='figure fraction',
    +            horizontalalignment='left', verticalalignment='top',
    +            fontsize=20)
    +
    +# The following examples show off how these arrows are drawn.
    +
    +ax.annotate('point offset from data',
    +            xy=(3, 1), xycoords='data',
    +            xytext=(-10, 90), textcoords='offset points',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='center', verticalalignment='bottom')
    +
    +ax.annotate('axes fraction',
    +            xy=(2, 1), xycoords='data',
    +            xytext=(0.36, 0.68), textcoords='axes fraction',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='right', verticalalignment='top')
    +
    +# You may also use negative points or pixels to specify from (right, top).
    +# E.g., (-10, 10) is 10 points to the left of the right side of the Axes and 10
    +# points above the bottom
    +
    +ax.annotate('pixel offset from axes fraction',
    +            xy=(1, 0), xycoords='axes fraction',
    +            xytext=(-20, 20), textcoords='offset pixels',
    +            horizontalalignment='right',
    +            verticalalignment='bottom')
    +
    +ax.set(xlim=(-1, 5), ylim=(-3, 5))
    +
    +
    +# %%
    +# Using multiple coordinate systems and axis types
    +# ------------------------------------------------
    +#
    +# You can specify the *xypoint* and the *xytext* in different positions and
    +# coordinate systems, and optionally turn on a connecting line and mark the
    +# point with a marker.  Annotations work on polar Axes too.
    +#
    +# In the example below, the *xy* point is in native coordinates (*xycoords*
    +# defaults to 'data').  For a polar Axes, this is in (theta, radius) space.
    +# The text in the example is placed in the fractional figure coordinate system.
    +# Text keyword arguments like horizontal and vertical alignment are respected.
    +
    +fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), figsize=(3, 3))
    +r = np.arange(0, 1, 0.001)
    +theta = 2*2*np.pi*r
    +line, = ax.plot(theta, r)
    +
    +ind = 800
    +thisr, thistheta = r[ind], theta[ind]
    +ax.plot([thistheta], [thisr], 'o')
    +ax.annotate('a polar annotation',
    +            xy=(thistheta, thisr),  # theta, radius
    +            xytext=(0.05, 0.05),    # fraction, fraction
    +            textcoords='figure fraction',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='left',
    +            verticalalignment='bottom')
    +
    +# %%
    +# You can also use polar notation on a cartesian Axes.  Here the native
    +# coordinate system ('data') is cartesian, so you need to specify the
    +# xycoords and textcoords as 'polar' if you want to use (theta, radius).
    +
    +el = Ellipse((0, 0), 10, 20, facecolor='r', alpha=0.5)
    +
    +fig, ax = plt.subplots(subplot_kw=dict(aspect='equal'))
    +ax.add_artist(el)
    +el.set_clip_box(ax.bbox)
    +ax.annotate('the top',
    +            xy=(np.pi/2., 10.),      # theta, radius
    +            xytext=(np.pi/3, 20.),   # theta, radius
    +            xycoords='polar',
    +            textcoords='polar',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='left',
    +            verticalalignment='bottom',
    +            clip_on=True)  # clip to the Axes bounding box
    +
    +ax.set(xlim=[-20, 20], ylim=[-20, 20])
    +
    +
    +# %%
    +# Customizing arrow and bubble styles
    +# -----------------------------------
    +#
    +# The arrow between *xytext* and the annotation point, as well as the bubble
    +# that covers the annotation text, are highly customizable. Below are a few
    +# parameter options as well as their resulting output.
    +
    +fig, ax = plt.subplots(figsize=(8, 5))
    +
    +t = np.arange(0.0, 5.0, 0.01)
    +s = np.cos(2*np.pi*t)
    +line, = ax.plot(t, s, lw=3)
    +
    +ax.annotate(
    +    'straight',
    +    xy=(0, 1), xycoords='data',
    +    xytext=(-50, 30), textcoords='offset points',
    +    arrowprops=dict(arrowstyle="->"))
    +ax.annotate(
    +    'arc3,\nrad 0.2',
    +    xy=(0.5, -1), xycoords='data',
    +    xytext=(-80, -60), textcoords='offset points',
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="arc3,rad=.2"))
    +ax.annotate(
    +    'arc,\nangle 50',
    +    xy=(1., 1), xycoords='data',
    +    xytext=(-90, 50), textcoords='offset points',
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="arc,angleA=0,armA=50,rad=10"))
    +ax.annotate(
    +    'arc,\narms',
    +    xy=(1.5, -1), xycoords='data',
    +    xytext=(-80, -60), textcoords='offset points',
    +    arrowprops=dict(
    +        arrowstyle="->",
    +        connectionstyle="arc,angleA=0,armA=40,angleB=-90,armB=30,rad=7"))
    +ax.annotate(
    +    'angle,\nangle 90',
    +    xy=(2., 1), xycoords='data',
    +    xytext=(-70, 30), textcoords='offset points',
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
    +ax.annotate(
    +    'angle3,\nangle -90',
    +    xy=(2.5, -1), xycoords='data',
    +    xytext=(-80, -60), textcoords='offset points',
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="angle3,angleA=0,angleB=-90"))
    +ax.annotate(
    +    'angle,\nround',
    +    xy=(3., 1), xycoords='data',
    +    xytext=(-60, 30), textcoords='offset points',
    +    bbox=dict(boxstyle="round", fc="0.8"),
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
    +ax.annotate(
    +    'angle,\nround4',
    +    xy=(3.5, -1), xycoords='data',
    +    xytext=(-70, -80), textcoords='offset points',
    +    size=20,
    +    bbox=dict(boxstyle="round4,pad=.5", fc="0.8"),
    +    arrowprops=dict(arrowstyle="->",
    +                    connectionstyle="angle,angleA=0,angleB=-90,rad=10"))
    +ax.annotate(
    +    'angle,\nshrink',
    +    xy=(4., 1), xycoords='data',
    +    xytext=(-60, 30), textcoords='offset points',
    +    bbox=dict(boxstyle="round", fc="0.8"),
    +    arrowprops=dict(arrowstyle="->",
    +                    shrinkA=0, shrinkB=10,
    +                    connectionstyle="angle,angleA=0,angleB=90,rad=10"))
    +# You can pass an empty string to get only annotation arrows rendered
    +ax.annotate('', xy=(4., 1.), xycoords='data',
    +            xytext=(4.5, -1), textcoords='data',
    +            arrowprops=dict(arrowstyle="<->",
    +                            connectionstyle="bar",
    +                            ec="k",
    +                            shrinkA=5, shrinkB=5))
    +
    +ax.set(xlim=(-1, 5), ylim=(-4, 3))
    +
    +# %%
    +# We'll create another figure so that it doesn't get too cluttered
    +fig, ax = plt.subplots()
    +
    +el = Ellipse((2, -1), 0.5, 0.5)
    +ax.add_patch(el)
    +
    +ax.annotate('$->$',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(-150, -140), textcoords='offset points',
    +            bbox=dict(boxstyle="round", fc="0.8"),
    +            arrowprops=dict(arrowstyle="->",
    +                            patchB=el,
    +                            connectionstyle="angle,angleA=90,angleB=0,rad=10"))
    +ax.annotate('arrow\nfancy',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(-100, 60), textcoords='offset points',
    +            size=20,
    +            arrowprops=dict(arrowstyle="fancy",
    +                            fc="0.6", ec="none",
    +                            patchB=el,
    +                            connectionstyle="angle3,angleA=0,angleB=-90"))
    +ax.annotate('arrow\nsimple',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(100, 60), textcoords='offset points',
    +            size=20,
    +            arrowprops=dict(arrowstyle="simple",
    +                            fc="0.6", ec="none",
    +                            patchB=el,
    +                            connectionstyle="arc3,rad=0.3"))
    +ax.annotate('wedge',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(-100, -100), textcoords='offset points',
    +            size=20,
    +            arrowprops=dict(arrowstyle="wedge,tail_width=0.7",
    +                            fc="0.6", ec="none",
    +                            patchB=el,
    +                            connectionstyle="arc3,rad=-0.3"))
    +ax.annotate('bubble,\ncontours',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(0, -70), textcoords='offset points',
    +            size=20,
    +            bbox=dict(boxstyle="round",
    +                      fc=(1.0, 0.7, 0.7),
    +                      ec=(1., .5, .5)),
    +            arrowprops=dict(arrowstyle="wedge,tail_width=1.",
    +                            fc=(1.0, 0.7, 0.7), ec=(1., .5, .5),
    +                            patchA=None,
    +                            patchB=el,
    +                            relpos=(0.2, 0.8),
    +                            connectionstyle="arc3,rad=-0.1"))
    +ax.annotate('bubble',
    +            xy=(2., -1), xycoords='data',
    +            xytext=(55, 0), textcoords='offset points',
    +            size=20, va="center",
    +            bbox=dict(boxstyle="round", fc=(1.0, 0.7, 0.7), ec="none"),
    +            arrowprops=dict(arrowstyle="wedge,tail_width=1.",
    +                            fc=(1.0, 0.7, 0.7), ec="none",
    +                            patchA=None,
    +                            patchB=el,
    +                            relpos=(0.2, 0.5)))
    +
    +ax.set(xlim=(-1, 5), ylim=(-5, 3))
    +
    +# %%
    +# More examples of coordinate systems
    +# -----------------------------------
    +#
    +# Below we'll show a few more examples of coordinate systems and how the
    +# location of annotations may be specified.
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +
    +bbox_args = dict(boxstyle="round", fc="0.8")
    +arrow_args = dict(arrowstyle="->")
    +
    +# Here we'll demonstrate the extents of the coordinate system and how
    +# we place annotating text.
    +
    +ax1.annotate('figure fraction : 0, 0', xy=(0, 0), xycoords='figure fraction',
    +             xytext=(20, 20), textcoords='offset points',
    +             ha="left", va="bottom",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +ax1.annotate('figure fraction : 1, 1', xy=(1, 1), xycoords='figure fraction',
    +             xytext=(-20, -20), textcoords='offset points',
    +             ha="right", va="top",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +ax1.annotate('axes fraction : 0, 0', xy=(0, 0), xycoords='axes fraction',
    +             xytext=(20, 20), textcoords='offset points',
    +             ha="left", va="bottom",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +ax1.annotate('axes fraction : 1, 1', xy=(1, 1), xycoords='axes fraction',
    +             xytext=(-20, -20), textcoords='offset points',
    +             ha="right", va="top",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +# It is also possible to generate draggable annotations
    +
    +an1 = ax1.annotate('Drag me 1', xy=(.5, .7), xycoords='data',
    +                   ha="center", va="center",
    +                   bbox=bbox_args)
    +
    +an2 = ax1.annotate('Drag me 2', xy=(.5, .5), xycoords=an1,
    +                   xytext=(.5, .3), textcoords='axes fraction',
    +                   ha="center", va="center",
    +                   bbox=bbox_args,
    +                   arrowprops=dict(patchB=an1.get_bbox_patch(),
    +                                   connectionstyle="arc3,rad=0.2",
    +                                   **arrow_args))
    +an1.draggable()
    +an2.draggable()
    +
    +an3 = ax1.annotate('', xy=(.5, .5), xycoords=an2,
    +                   xytext=(.5, .5), textcoords=an1,
    +                   ha="center", va="center",
    +                   bbox=bbox_args,
    +                   arrowprops=dict(patchA=an1.get_bbox_patch(),
    +                                   patchB=an2.get_bbox_patch(),
    +                                   connectionstyle="arc3,rad=0.2",
    +                                   **arrow_args))
    +
    +# Finally we'll show off some more complex annotation and placement
    +
    +text = ax2.annotate('xy=(0, 1)\nxycoords=("data", "axes fraction")',
    +                    xy=(0, 1), xycoords=("data", 'axes fraction'),
    +                    xytext=(0, -20), textcoords='offset points',
    +                    ha="center", va="top",
    +                    bbox=bbox_args,
    +                    arrowprops=arrow_args)
    +
    +ax2.annotate('xy=(0.5, 0)\nxycoords=artist',
    +             xy=(0.5, 0.), xycoords=text,
    +             xytext=(0, -20), textcoords='offset points',
    +             ha="center", va="top",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +ax2.annotate('xy=(0.8, 0.5)\nxycoords=ax1.transData',
    +             xy=(0.8, 0.5), xycoords=ax1.transData,
    +             xytext=(10, 10),
    +             textcoords=OffsetFrom(ax2.bbox, (0, 0), "points"),
    +             ha="left", va="bottom",
    +             bbox=bbox_args,
    +             arrowprops=arrow_args)
    +
    +ax2.set(xlim=[-2, 2], ylim=[-2, 2])
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/annotation_polar.py b/galleries/examples/text_labels_and_annotations/annotation_polar.py
    new file mode 100644
    index 000000000000..c2418519cf8c
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/annotation_polar.py
    @@ -0,0 +1,43 @@
    +"""
    +====================
    +Annotate polar plots
    +====================
    +
    +This example shows how to create an annotation on a polar graph.
    +
    +For a complete overview of the annotation capabilities, also see the
    +:ref:`annotations`.
    +
    +.. redirect-from:: /gallery/pyplots/annotation_polar
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig = plt.figure()
    +ax = fig.add_subplot(projection='polar')
    +r = np.arange(0, 1, 0.001)
    +theta = 2 * 2*np.pi * r
    +line, = ax.plot(theta, r, color='#ee8d18', lw=3)
    +
    +ind = 800
    +thisr, thistheta = r[ind], theta[ind]
    +ax.plot([thistheta], [thisr], 'o')
    +ax.annotate('a polar annotation',
    +            xy=(thistheta, thisr),  # theta, radius
    +            xytext=(0.05, 0.05),    # fraction, fraction
    +            textcoords='figure fraction',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='left',
    +            verticalalignment='bottom',
    +            )
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.projections.polar`
    +#    - `matplotlib.axes.Axes.annotate` / `matplotlib.pyplot.annotate`
    diff --git a/galleries/examples/text_labels_and_annotations/arrow_demo.py b/galleries/examples/text_labels_and_annotations/arrow_demo.py
    new file mode 100644
    index 000000000000..11c6c3ec0e5d
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/arrow_demo.py
    @@ -0,0 +1,159 @@
    +"""
    +==========
    +Arrow Demo
    +==========
    +
    +Three ways of drawing arrows to encode arrow "strength" (e.g., transition
    +probabilities in a Markov model) using arrow length, width, or alpha (opacity).
    +"""
    +
    +import itertools
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +
    +def make_arrow_graph(ax, data, size=4, display='length', shape='right',
    +                     max_arrow_width=0.03, arrow_sep=0.02, alpha=0.5,
    +                     normalize_data=False, ec=None, labelcolor=None,
    +                     **kwargs):
    +    """
    +    Makes an arrow plot.
    +
    +    Parameters
    +    ----------
    +    ax
    +        The Axes where the graph is drawn.
    +    data
    +        Dict with probabilities for the bases and pair transitions.
    +    size
    +        Size of the plot, in inches.
    +    display : {'length', 'width', 'alpha'}
    +        The arrow property to change.
    +    shape : {'full', 'left', 'right'}
    +        For full or half arrows.
    +    max_arrow_width : float
    +        Maximum width of an arrow, in data coordinates.
    +    arrow_sep : float
    +        Separation between arrows in a pair, in data coordinates.
    +    alpha : float
    +        Maximum opacity of arrows.
    +    **kwargs
    +        `.FancyArrow` properties, e.g. *linewidth* or *edgecolor*.
    +    """
    +
    +    ax.set(xlim=(-0.25, 1.25), ylim=(-0.25, 1.25), xticks=[], yticks=[],
    +           title=f'flux encoded as arrow {display}')
    +    max_text_size = size * 12
    +    min_text_size = size
    +    label_text_size = size * 4
    +
    +    bases = 'ATGC'
    +    coords = {
    +        'A': np.array([0, 1]),
    +        'T': np.array([1, 1]),
    +        'G': np.array([0, 0]),
    +        'C': np.array([1, 0]),
    +    }
    +    colors = {'A': 'r', 'T': 'k', 'G': 'g', 'C': 'b'}
    +
    +    for base in bases:
    +        fontsize = np.clip(max_text_size * data[base]**(1/2),
    +                           min_text_size, max_text_size)
    +        ax.text(*coords[base], f'${base}_3$',
    +                color=colors[base], size=fontsize,
    +                horizontalalignment='center', verticalalignment='center',
    +                weight='bold')
    +
    +    arrow_h_offset = 0.25  # data coordinates, empirically determined
    +    max_arrow_length = 1 - 2 * arrow_h_offset
    +    max_head_width = 2.5 * max_arrow_width
    +    max_head_length = 2 * max_arrow_width
    +    sf = 0.6  # max arrow size represents this in data coords
    +
    +    if normalize_data:
    +        # find maximum value for rates, i.e. where keys are 2 chars long
    +        max_val = max((v for k, v in data.items() if len(k) == 2), default=0)
    +        # divide rates by max val, multiply by arrow scale factor
    +        for k, v in data.items():
    +            data[k] = v / max_val * sf
    +
    +    # iterate over strings 'AT', 'TA', 'AG', 'GA', etc.
    +    for pair in map(''.join, itertools.permutations(bases, 2)):
    +        # set the length of the arrow
    +        if display == 'length':
    +            length = (max_head_length
    +                      + data[pair] / sf * (max_arrow_length - max_head_length))
    +        else:
    +            length = max_arrow_length
    +        # set the transparency of the arrow
    +        if display == 'alpha':
    +            alpha = min(data[pair] / sf, alpha)
    +        # set the width of the arrow
    +        if display == 'width':
    +            scale = data[pair] / sf
    +            width = max_arrow_width * scale
    +            head_width = max_head_width * scale
    +            head_length = max_head_length * scale
    +        else:
    +            width = max_arrow_width
    +            head_width = max_head_width
    +            head_length = max_head_length
    +
    +        fc = colors[pair[0]]
    +
    +        cp0 = coords[pair[0]]
    +        cp1 = coords[pair[1]]
    +        # unit vector in arrow direction
    +        delta = cos, sin = (cp1 - cp0) / np.hypot(*(cp1 - cp0))
    +        x_pos, y_pos = (
    +            (cp0 + cp1) / 2  # midpoint
    +            - delta * length / 2  # half the arrow length
    +            + np.array([-sin, cos]) * arrow_sep  # shift outwards by arrow_sep
    +        )
    +        ax.arrow(
    +            x_pos, y_pos, cos * length, sin * length,
    +            fc=fc, ec=ec or fc, alpha=alpha, width=width,
    +            head_width=head_width, head_length=head_length, shape=shape,
    +            length_includes_head=True,
    +            **kwargs
    +        )
    +
    +        # figure out coordinates for text:
    +        # if drawing relative to base: x and y are same as for arrow
    +        # dx and dy are one arrow width left and up
    +        orig_positions = {
    +            'base': [3 * max_arrow_width, 3 * max_arrow_width],
    +            'center': [length / 2, 3 * max_arrow_width],
    +            'tip': [length - 3 * max_arrow_width, 3 * max_arrow_width],
    +        }
    +        # for diagonal arrows, put the label at the arrow base
    +        # for vertical or horizontal arrows, center the label
    +        where = 'base' if (cp0 != cp1).all() else 'center'
    +        # rotate based on direction of arrow (cos, sin)
    +        M = [[cos, -sin], [sin, cos]]
    +        x, y = np.dot(M, orig_positions[where]) + [x_pos, y_pos]
    +        label = r'$r_{_{\mathrm{%s}}}$' % (pair,)
    +        ax.text(x, y, label, size=label_text_size, ha='center', va='center',
    +                color=labelcolor or fc)
    +
    +
    +if __name__ == '__main__':
    +    data = {  # test data
    +        'A': 0.4, 'T': 0.3, 'G': 0.6, 'C': 0.2,
    +        'AT': 0.4, 'AC': 0.3, 'AG': 0.2,
    +        'TA': 0.2, 'TC': 0.3, 'TG': 0.4,
    +        'CT': 0.2, 'CG': 0.3, 'CA': 0.2,
    +        'GA': 0.1, 'GT': 0.4, 'GC': 0.1,
    +    }
    +
    +    size = 4
    +    fig = plt.figure(figsize=(3 * size, size), layout="constrained")
    +    axs = fig.subplot_mosaic([["length", "width", "alpha"]])
    +
    +    for display, ax in axs.items():
    +        make_arrow_graph(
    +            ax, data, display=display, linewidth=0.001, edgecolor=None,
    +            normalize_data=True, size=size)
    +
    +    plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/autowrap.py b/galleries/examples/text_labels_and_annotations/autowrap.py
    new file mode 100644
    index 000000000000..ea65b0be9992
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/autowrap.py
    @@ -0,0 +1,32 @@
    +"""
    +==============
    +Auto-wrap text
    +==============
    +
    +Matplotlib can wrap text automatically, but if it's too long, the text will
    +still be displayed slightly outside the boundaries of the axis.
    +
    +Note: Auto-wrapping does not work together with
    +``savefig(..., bbox_inches='tight')``. The 'tight' setting rescales the canvas
    +to accommodate all content and happens before wrapping. This affects
    +``%matplotlib inline`` in IPython and Jupyter notebooks where the inline
    +setting uses ``bbox_inches='tight'`` by default when saving the image to
    +embed.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure()
    +plt.axis((0, 10, 0, 10))
    +t = ("This is a really long string that I'd rather have wrapped so that it "
    +     "doesn't go outside of the figure, but if it's long enough it will go "
    +     "off the top or bottom!")
    +plt.text(4, 1, t, ha='left', rotation=15, wrap=True)
    +plt.text(6, 5, t, ha='left', rotation=15, wrap=True)
    +plt.text(5, 5, t, ha='right', rotation=-15, wrap=True)
    +plt.text(5, 10, t, fontsize=18, style='oblique', ha='center',
    +         va='top', wrap=True)
    +plt.text(3, 4, t, family='serif', style='italic', ha='right', wrap=True)
    +plt.text(-1, 0, t, ha='left', rotation=-15, wrap=True)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/custom_legends.py b/galleries/examples/text_labels_and_annotations/custom_legends.py
    new file mode 100644
    index 000000000000..58eb47be0a93
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/custom_legends.py
    @@ -0,0 +1,74 @@
    +"""
    +======================
    +Compose custom legends
    +======================
    +
    +Composing custom legends piece-by-piece.
    +
    +.. note::
    +
    +   For more information on creating and customizing legends, see the following
    +   pages:
    +
    +   * :ref:`legend_guide`
    +   * :doc:`/gallery/text_labels_and_annotations/legend_demo`
    +
    +Sometimes you don't want a legend that is explicitly tied to data that
    +you have plotted. For example, say you have plotted 10 lines, but don't
    +want a legend item to show up for each one. If you simply plot the lines
    +and call ``ax.legend()``, you will get the following:
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# sphinx_gallery_thumbnail_number = 2
    +import matplotlib as mpl
    +from matplotlib import cycler
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +# %%
    +N = 10
    +data = (np.geomspace(1, 10, 100) + np.random.randn(N, 100)).T
    +cmap = plt.colormaps["coolwarm"]
    +mpl.rcParams['axes.prop_cycle'] = cycler(color=cmap(np.linspace(0, 1, N)))
    +
    +fig, ax = plt.subplots()
    +lines = ax.plot(data)
    +
    +# %%
    +# Since the data does not have any labels, creating a legend requires
    +# us to define the icons and labels.
    +# In this case, we can compose a legend using Matplotlib objects that aren't
    +# explicitly tied to the data that was plotted. For example:
    +
    +from matplotlib.lines import Line2D
    +
    +custom_lines = [Line2D([0], [0], color=cmap(0.), lw=4),
    +                Line2D([0], [0], color=cmap(.5), lw=4),
    +                Line2D([0], [0], color=cmap(1.), lw=4)]
    +
    +fig, ax = plt.subplots()
    +lines = ax.plot(data)
    +ax.legend(custom_lines, ['Cold', 'Medium', 'Hot'])
    +
    +
    +# %%
    +# There are many other Matplotlib objects that can be used in this way. In the
    +# code below we've listed a few common ones.
    +
    +from matplotlib.lines import Line2D
    +from matplotlib.patches import Patch
    +
    +legend_elements = [Line2D([0], [0], color='b', lw=4, label='Line'),
    +                   Line2D([0], [0], marker='o', color='w', label='Scatter',
    +                          markerfacecolor='g', markersize=15),
    +                   Patch(facecolor='orange', edgecolor='r',
    +                         label='Color Patch')]
    +
    +# Create the figure
    +fig, ax = plt.subplots()
    +ax.legend(handles=legend_elements, loc='center')
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/date.py b/galleries/examples/text_labels_and_annotations/date.py
    new file mode 100644
    index 000000000000..880e62e36714
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/date.py
    @@ -0,0 +1,63 @@
    +"""
    +================
    +Date tick labels
    +================
    +
    +Matplotlib date plotting is done by converting date instances into
    +days since an epoch (by default 1970-01-01T00:00:00). The
    +:mod:`matplotlib.dates` module provides the converter functions `.date2num`
    +and `.num2date` that convert `datetime.datetime` and `numpy.datetime64`
    +objects to and from Matplotlib's internal representation.  These data
    +types are registered with the unit conversion mechanism described in
    +:mod:`matplotlib.units`, so the conversion happens automatically for the user.
    +The registration process also sets the default tick ``locator`` and
    +``formatter`` for the axis to be `~.matplotlib.dates.AutoDateLocator` and
    +`~.matplotlib.dates.AutoDateFormatter`.
    +
    +An alternative formatter is the `~.dates.ConciseDateFormatter`,
    +used in the second ``Axes`` below (see
    +:doc:`/gallery/ticks/date_concise_formatter`), which often removes the need to
    +rotate the tick labels. The last ``Axes`` formats the dates manually, using
    +`~.dates.DateFormatter` to format the dates using the format strings documented
    +at `datetime.date.strftime`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.cbook as cbook
    +import matplotlib.dates as mdates
    +
    +# Load a numpy record array from yahoo csv data with fields date, open, high,
    +# low, close, volume, adj_close from the mpl-data/sample_data directory. The
    +# record array stores the date as an np.datetime64 with a day unit ('D') in
    +# the date column.
    +data = cbook.get_sample_data('goog.npz')['price_data']
    +
    +fig, axs = plt.subplots(3, 1, figsize=(6.4, 7), layout='constrained')
    +# common to all three:
    +for ax in axs:
    +    ax.plot('date', 'adj_close', data=data)
    +    # Major ticks every half year, minor ticks every month,
    +    ax.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1, 7)))
    +    ax.xaxis.set_minor_locator(mdates.MonthLocator())
    +    ax.grid(True)
    +    ax.set_ylabel(r'Price [\$]')
    +
    +# different formats:
    +ax = axs[0]
    +ax.set_title('DefaultFormatter', loc='left', y=0.85, x=0.02, fontsize='medium')
    +
    +ax = axs[1]
    +ax.set_title('ConciseFormatter', loc='left', y=0.85, x=0.02, fontsize='medium')
    +ax.xaxis.set_major_formatter(
    +    mdates.ConciseDateFormatter(ax.xaxis.get_major_locator()))
    +
    +ax = axs[2]
    +ax.set_title('Manual DateFormatter', loc='left', y=0.85, x=0.02,
    +             fontsize='medium')
    +# Text in the x-axis will be displayed in 'YYYY-mm' format.
    +ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%b'))
    +# Rotates and right-aligns the x labels so they don't crowd each other.
    +ax.xaxis.set_tick_params(rotation=30, rotation_mode='xtick')
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/demo_annotation_box.py b/galleries/examples/text_labels_and_annotations/demo_annotation_box.py
    new file mode 100644
    index 000000000000..e6c21bd69107
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/demo_annotation_box.py
    @@ -0,0 +1,118 @@
    +"""
    +===================
    +AnnotationBbox demo
    +===================
    +
    +`.AnnotationBbox` creates an annotation using an `.OffsetBox`, and
    +provides more fine-grained control than `.Axes.annotate`.  This example
    +demonstrates the use of AnnotationBbox together with three different
    +OffsetBoxes: `.TextArea`, `.DrawingArea`, and `.OffsetImage`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.cbook import get_sample_data
    +from matplotlib.offsetbox import AnnotationBbox, DrawingArea, OffsetImage, TextArea
    +from matplotlib.patches import Circle
    +
    +fig, ax = plt.subplots()
    +
    +# Define a 1st position to annotate (display it with a marker)
    +xy = (0.5, 0.7)
    +ax.plot(xy[0], xy[1], ".r")
    +
    +# Annotate the 1st position with a text box ('Test 1')
    +offsetbox = TextArea("Test 1")
    +
    +ab = AnnotationBbox(offsetbox, xy,
    +                    xybox=(-20, 40),
    +                    xycoords='data',
    +                    boxcoords="offset points",
    +                    arrowprops=dict(arrowstyle="->"),
    +                    bboxprops=dict(boxstyle="sawtooth"))
    +ax.add_artist(ab)
    +
    +# Annotate the 1st position with another text box ('Test')
    +offsetbox = TextArea("Test")
    +
    +ab = AnnotationBbox(offsetbox, xy,
    +                    xybox=(1.02, xy[1]),
    +                    xycoords='data',
    +                    boxcoords=("axes fraction", "data"),
    +                    box_alignment=(0., 0.5),
    +                    arrowprops=dict(arrowstyle="->"))
    +ax.add_artist(ab)
    +
    +# Define a 2nd position to annotate (don't display with a marker this time)
    +xy = [0.3, 0.55]
    +
    +# Annotate the 2nd position with a circle patch
    +da = DrawingArea(20, 20, 0, 0)
    +p = Circle((10, 10), 10)
    +da.add_artist(p)
    +
    +ab = AnnotationBbox(da, xy,
    +                    xybox=(1., xy[1]),
    +                    xycoords='data',
    +                    boxcoords=("axes fraction", "data"),
    +                    box_alignment=(0.2, 0.5),
    +                    arrowprops=dict(arrowstyle="->"),
    +                    bboxprops=dict(alpha=0.5))
    +
    +ax.add_artist(ab)
    +
    +# Annotate the 2nd position with an image (a generated array of pixels)
    +arr = np.arange(100).reshape((10, 10))
    +im = OffsetImage(arr, zoom=2)
    +im.image.axes = ax
    +
    +ab = AnnotationBbox(im, xy,
    +                    xybox=(-50., 50.),
    +                    xycoords='data',
    +                    boxcoords="offset points",
    +                    pad=0.3,
    +                    arrowprops=dict(arrowstyle="->"))
    +
    +ax.add_artist(ab)
    +
    +# Annotate the 2nd position with another image (a Grace Hopper portrait)
    +with get_sample_data("grace_hopper.jpg") as file:
    +    arr_img = plt.imread(file)
    +
    +imagebox = OffsetImage(arr_img, zoom=0.2)
    +imagebox.image.axes = ax
    +
    +ab = AnnotationBbox(imagebox, xy,
    +                    xybox=(120., -80.),
    +                    xycoords='data',
    +                    boxcoords="offset points",
    +                    pad=0.5,
    +                    arrowprops=dict(
    +                        arrowstyle="->",
    +                        connectionstyle="angle,angleA=0,angleB=90,rad=3")
    +                    )
    +
    +ax.add_artist(ab)
    +
    +# Fix the display limits to see everything
    +ax.set_xlim(0, 1)
    +ax.set_ylim(0, 1)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches.Circle`
    +#    - `matplotlib.offsetbox.TextArea`
    +#    - `matplotlib.offsetbox.DrawingArea`
    +#    - `matplotlib.offsetbox.OffsetImage`
    +#    - `matplotlib.offsetbox.AnnotationBbox`
    +#    - `matplotlib.cbook.get_sample_data`
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.pyplot.imread`
    diff --git a/galleries/examples/text_labels_and_annotations/demo_text_path.py b/galleries/examples/text_labels_and_annotations/demo_text_path.py
    new file mode 100644
    index 000000000000..ae79ff937093
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/demo_text_path.py
    @@ -0,0 +1,122 @@
    +"""
    +======================
    +Using a text as a Path
    +======================
    +
    +`~matplotlib.text.TextPath` creates a `.Path` that is the outline of the
    +characters of a text. The resulting path can be employed e.g. as a clip path
    +for an image.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.cbook import get_sample_data
    +from matplotlib.image import BboxImage
    +from matplotlib.offsetbox import AnchoredOffsetbox, AnnotationBbox, AuxTransformBox
    +from matplotlib.patches import PathPatch, Shadow
    +from matplotlib.text import TextPath
    +from matplotlib.transforms import IdentityTransform
    +
    +
    +class PathClippedImagePatch(PathPatch):
    +    """
    +    The given image is used to draw the face of the patch. Internally,
    +    it uses BboxImage whose clippath set to the path of the patch.
    +
    +    FIXME : The result is currently dpi dependent.
    +    """
    +
    +    def __init__(self, path, bbox_image, **kwargs):
    +        super().__init__(path, **kwargs)
    +        self.bbox_image = BboxImage(
    +            self.get_window_extent, norm=None, origin=None)
    +        self.bbox_image.set_data(bbox_image)
    +
    +    def set_facecolor(self, color):
    +        """Simply ignore facecolor."""
    +        super().set_facecolor("none")
    +
    +    def draw(self, renderer=None):
    +        # the clip path must be updated every draw. any solution? -JJ
    +        self.bbox_image.set_clip_path(self._path, self.get_transform())
    +        self.bbox_image.draw(renderer)
    +        super().draw(renderer)
    +
    +
    +if __name__ == "__main__":
    +
    +    fig, (ax1, ax2) = plt.subplots(2)
    +
    +    # EXAMPLE 1
    +
    +    arr = plt.imread(get_sample_data("grace_hopper.jpg"))
    +
    +    text_path = TextPath((0, 0), "!?", size=150)
    +    p = PathClippedImagePatch(text_path, arr, ec="k")
    +
    +    # make offset box
    +    offsetbox = AuxTransformBox(IdentityTransform())
    +    offsetbox.add_artist(p)
    +
    +    # make anchored offset box
    +    ao = AnchoredOffsetbox(loc='upper left', child=offsetbox, frameon=True,
    +                           borderpad=0.2)
    +    ax1.add_artist(ao)
    +
    +    # another text
    +    for usetex, ypos, string in [
    +            (False, 0.25, r"textpath supports mathtext"),
    +            (True, 0.05, r"textpath supports \TeX"),
    +    ]:
    +        text_path = TextPath((0, 0), string, size=20, usetex=usetex)
    +
    +        p1 = PathPatch(text_path, ec="w", lw=3, fc="w", alpha=0.9)
    +        p2 = PathPatch(text_path, ec="none", fc="k")
    +
    +        offsetbox2 = AuxTransformBox(IdentityTransform())
    +        offsetbox2.add_artist(p1)
    +        offsetbox2.add_artist(p2)
    +
    +        ab = AnnotationBbox(offsetbox2, (0.95, ypos),
    +                            xycoords='axes fraction',
    +                            boxcoords="offset points",
    +                            box_alignment=(1., 0.),
    +                            frameon=False,
    +                            )
    +        ax1.add_artist(ab)
    +
    +    ax1.imshow([[0, 1, 2], [1, 2, 3]], cmap="gist_gray_r",
    +               interpolation="bilinear", aspect="auto")
    +
    +    # EXAMPLE 2
    +
    +    arr = np.arange(256).reshape(1, 256)
    +
    +    for usetex, xpos, string in [
    +            (False, 0.25,
    +             r"$\left[\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}\right]$!"),
    +            (True, 0.75,
    +             r"$\displaystyle\left[\sum_{n=1}^\infty"
    +             r"\frac{-e^{i\pi}}{2^n}\right]$!"),
    +    ]:
    +        text_path = TextPath((0, 0), string, size=40, usetex=usetex)
    +        text_patch = PathClippedImagePatch(text_path, arr, ec="none")
    +        shadow1 = Shadow(text_patch, 1, -1, fc="none", ec="0.6", lw=3)
    +        shadow2 = Shadow(text_patch, 1, -1, fc="0.3", ec="none")
    +
    +        # make offset box
    +        offsetbox = AuxTransformBox(IdentityTransform())
    +        offsetbox.add_artist(shadow1)
    +        offsetbox.add_artist(shadow2)
    +        offsetbox.add_artist(text_patch)
    +
    +        # place the anchored offset box using AnnotationBbox
    +        ab = AnnotationBbox(offsetbox, (xpos, 0.5), box_alignment=(0.5, 0.5))
    +
    +        ax2.add_artist(ab)
    +
    +    ax2.set_xlim(0, 1)
    +    ax2.set_ylim(0, 1)
    +
    +    plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py b/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py
    new file mode 100644
    index 000000000000..9cb7f30302fc
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/demo_text_rotation_mode.py
    @@ -0,0 +1,88 @@
    +r"""
    +==================
    +Text rotation mode
    +==================
    +
    +This example illustrates the effect of ``rotation_mode`` on the positioning
    +of rotated text.
    +
    +Rotated `.Text`\s are created by passing the parameter ``rotation`` to
    +the constructor or the Axes' method `~.axes.Axes.text`.
    +
    +The actual positioning depends on the additional parameters
    +``horizontalalignment``, ``verticalalignment`` and ``rotation_mode``.
    +``rotation_mode`` determines the order of rotation and alignment:
    +
    +- ``rotation_mode='default'`` (or None) first rotates the text and then aligns
    +  the bounding box of the rotated text.
    +- ``rotation_mode='anchor'`` aligns the unrotated text and then rotates the
    +  text around the point of alignment.
    +
    +.. redirect-from:: /gallery/text_labels_and_annotations/text_rotation
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def test_rotation_mode(fig, mode):
    +    ha_list = ["left", "center", "right"]
    +    va_list = ["top", "center", "baseline", "bottom"]
    +    axs = fig.subplots(len(va_list), len(ha_list), sharex=True, sharey=True,
    +                       subplot_kw=dict(aspect=1),
    +                       gridspec_kw=dict(hspace=0, wspace=0))
    +
    +    # labels and title
    +    for ha, ax in zip(ha_list, axs[-1, :]):
    +        ax.set_xlabel(ha)
    +    for va, ax in zip(va_list, axs[:, 0]):
    +        ax.set_ylabel(va)
    +    axs[0, 1].set_title(f"rotation_mode='{mode}'", size="large")
    +
    +    kw = (
    +        {} if mode == "default" else
    +        {"bbox": dict(boxstyle="square,pad=0.", ec="none", fc="C1", alpha=0.3)}
    +    )
    +
    +    texts = {}
    +
    +    # use a different text alignment in each Axes
    +    for i, va in enumerate(va_list):
    +        for j, ha in enumerate(ha_list):
    +            ax = axs[i, j]
    +            # prepare Axes layout
    +            ax.set(xticks=[], yticks=[])
    +            ax.axvline(0.5, color="skyblue", zorder=0)
    +            ax.axhline(0.5, color="skyblue", zorder=0)
    +            ax.plot(0.5, 0.5, color="C0", marker="o", zorder=1)
    +            # add text with rotation and alignment settings
    +            tx = ax.text(0.5, 0.5, "Tpg",
    +                         size="x-large", rotation=40,
    +                         horizontalalignment=ha, verticalalignment=va,
    +                         rotation_mode=mode, **kw)
    +            texts[ax] = tx
    +
    +    if mode == "default":
    +        # highlight bbox
    +        fig.canvas.draw()
    +        for ax, text in texts.items():
    +            bb = text.get_window_extent().transformed(ax.transData.inverted())
    +            rect = plt.Rectangle((bb.x0, bb.y0), bb.width, bb.height,
    +                                 facecolor="C1", alpha=0.3, zorder=2)
    +            ax.add_patch(rect)
    +
    +
    +fig = plt.figure(figsize=(8, 5))
    +subfigs = fig.subfigures(1, 2)
    +test_rotation_mode(subfigs[0], "default")
    +test_rotation_mode(subfigs[1], "anchor")
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.text` / `matplotlib.pyplot.text`
    diff --git a/galleries/examples/text_labels_and_annotations/dfrac_demo.py b/galleries/examples/text_labels_and_annotations/dfrac_demo.py
    new file mode 100644
    index 000000000000..6d751f367722
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/dfrac_demo.py
    @@ -0,0 +1,28 @@
    +r"""
    +=========================================
    +The difference between \\dfrac and \\frac
    +=========================================
    +
    +In this example, the differences between the \\dfrac and \\frac TeX macros are
    +illustrated; in particular, the difference between display style and text style
    +fractions when using Mathtext.
    +
    +.. versionadded:: 2.1
    +
    +.. note::
    +    To use \\dfrac with the LaTeX engine (text.usetex : True), you need to
    +    import the amsmath package with :rc:`text.latex.preamble`, which is
    +    an unsupported feature; therefore, it is probably a better idea to just
    +    use the \\displaystyle option before the \\frac macro to get this behavior
    +    with the LaTeX engine.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure(figsize=(5.25, 0.75))
    +fig.text(0.5, 0.3, r'\dfrac: $\dfrac{a}{b}$',
    +         horizontalalignment='center', verticalalignment='center')
    +fig.text(0.5, 0.7, r'\frac: $\frac{a}{b}$',
    +         horizontalalignment='center', verticalalignment='center')
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/engineering_formatter.py b/galleries/examples/text_labels_and_annotations/engineering_formatter.py
    new file mode 100644
    index 000000000000..372297a81d57
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/engineering_formatter.py
    @@ -0,0 +1,44 @@
    +"""
    +=======================================
    +Format ticks using engineering notation
    +=======================================
    +
    +Use of the engineering Formatter.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.ticker import EngFormatter
    +
    +# Fixing random state for reproducibility
    +prng = np.random.RandomState(19680801)
    +
    +# Create artificial data to plot.
    +# The x data span over several decades to demonstrate several SI prefixes.
    +xs = np.logspace(1, 9, 100)
    +ys = (0.8 + 0.4 * prng.uniform(size=100)) * np.log10(xs)**2
    +
    +# Figure width is doubled (2*6.4) to display nicely 2 subplots side by side.
    +fig, (ax0, ax1) = plt.subplots(nrows=2, figsize=(7, 9.6))
    +for ax in (ax0, ax1):
    +    ax.set_xscale('log')
    +
    +# Demo of the default settings, with a user-defined unit label.
    +ax0.set_title('Full unit ticklabels, w/ default precision & space separator')
    +formatter0 = EngFormatter(unit='Hz')
    +ax0.xaxis.set_major_formatter(formatter0)
    +ax0.plot(xs, ys)
    +ax0.set_xlabel('Frequency')
    +
    +# Demo of the options `places` (number of digit after decimal point) and
    +# `sep` (separator between the number and the prefix/unit).
    +ax1.set_title('SI-prefix only ticklabels, 1-digit precision & '
    +              'thin space separator')
    +formatter1 = EngFormatter(places=1, sep="\N{THIN SPACE}")  # U+2009
    +ax1.xaxis.set_major_formatter(formatter1)
    +ax1.plot(xs, ys)
    +ax1.set_xlabel('Frequency [Hz]')
    +
    +plt.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/fancyarrow_demo.py b/galleries/examples/text_labels_and_annotations/fancyarrow_demo.py
    new file mode 100644
    index 000000000000..6a2700d20515
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/fancyarrow_demo.py
    @@ -0,0 +1,64 @@
    +"""
    +================================
    +Annotation arrow style reference
    +================================
    +
    +Overview of the available `.ArrowStyle` settings. These are used for the *arrowstyle*
    +parameter of `~.Axes.annotate` and `.FancyArrowPatch`.
    +
    +Each style can be configured with a set of parameters, which are stated along with
    +their default values.
    +"""
    +
    +import inspect
    +import itertools
    +import re
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.patches import ArrowStyle
    +
    +styles = ArrowStyle.get_styles()
    +ncol = 2
    +nrow = (len(styles) + 1) // ncol
    +gridspec_kw = dict(wspace=0, hspace=0.05, left=0, right=1, bottom=0, top=1)
    +fig, axs = plt.subplots(1 + nrow, ncol,
    +                        figsize=(4 * ncol, 1 + nrow), gridspec_kw=gridspec_kw)
    +for ax in axs.flat:
    +    ax.set_xlim(-0.1, 4)
    +    ax.set_axis_off()
    +for ax in axs[0, :]:
    +    ax.text(0, 0.5, "arrowstyle", size="large", color="tab:blue")
    +    ax.text(1.4, .5, "default parameters", size="large")
    +for ax, (stylename, stylecls) in zip(axs[1:, :].T.flat, styles.items()):
    +    # draw dot and annotation with arrowstyle
    +    l, = ax.plot(1.25, 0, "o", color="darkgrey")
    +    ax.annotate(stylename, (1.25, 0), (0, 0),
    +                size="large", color="tab:blue", va="center", family="monospace",
    +                arrowprops=dict(
    +                    arrowstyle=stylename, connectionstyle="arc3,rad=0",
    +                    color="black", shrinkA=5, shrinkB=5, patchB=l,
    +                ),
    +                bbox=dict(boxstyle="square", fc="w", ec="darkgrey"))
    +    # draw default parameters
    +    # wrap at every nth comma (n = 1 or 2, depending on text length)
    +    s = str(inspect.signature(stylecls))[1:-1]
    +    n = 2 if s.count(',') > 3 else 1
    +    ax.text(1.4, 0,
    +            re.sub(', ', lambda m, c=itertools.count(1): m.group()
    +                   if next(c) % n else '\n', s),
    +            verticalalignment="center", color="0.3")
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.patches`
    +#    - `matplotlib.patches.ArrowStyle`
    +#    - ``matplotlib.patches.ArrowStyle.get_styles``
    +#    - `matplotlib.axes.Axes.annotate`
    diff --git a/galleries/examples/text_labels_and_annotations/fancytextbox_demo.py b/galleries/examples/text_labels_and_annotations/fancytextbox_demo.py
    new file mode 100644
    index 000000000000..940ed9349170
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/fancytextbox_demo.py
    @@ -0,0 +1,26 @@
    +"""
    +==================
    +Styling text boxes
    +==================
    +
    +This example shows how to style text boxes using *bbox* parameters.
    +"""
    +import matplotlib.pyplot as plt
    +
    +plt.text(0.6, 0.7, "eggs", size=50, rotation=30.,
    +         ha="center", va="center",
    +         bbox=dict(boxstyle="round",
    +                   ec=(1., 0.5, 0.5),
    +                   fc=(1., 0.8, 0.8),
    +                   )
    +         )
    +
    +plt.text(0.55, 0.6, "spam", size=50, rotation=-25.,
    +         ha="right", va="top",
    +         bbox=dict(boxstyle="square",
    +                   ec=(1., 0.5, 0.5),
    +                   fc=(1., 0.8, 0.8),
    +                   )
    +         )
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/figlegend_demo.py b/galleries/examples/text_labels_and_annotations/figlegend_demo.py
    new file mode 100644
    index 000000000000..64253430b085
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/figlegend_demo.py
    @@ -0,0 +1,33 @@
    +"""
    +==================
    +Figure legend demo
    +==================
    +
    +Rather than plotting a legend on each axis, a legend for all the artists
    +on all the sub-axes of a figure can be plotted instead.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, axs = plt.subplots(1, 2, layout='constrained')
    +
    +x = np.arange(0.0, 4*np.pi, 0.2)
    +axs[0].plot(x, np.sin(x), label='Line 1')
    +axs[0].plot(x, np.exp(-x/2), marker='o', label='Line 2')
    +axs[1].plot(x, np.sin(x), color='tab:green', label='Line 3')
    +axs[1].plot(x, np.exp(-x/4), color='tab:red', marker='^', label='Line 4')
    +
    +fig.legend(loc='outside right upper')
    +
    +plt.show()
    +
    +# %%
    +# The outside positioning is discussed in detail here:
    +# https://matplotlib.org/stable/users/explain/axes/legend_guide.html#figure-legends
    +#
    +#
    +# .. seealso::
    +#
    +#    The :ref:`legend_guide` contains an in depth discussion on the configuration
    +#    options for legends.
    diff --git a/galleries/examples/text_labels_and_annotations/font_family_rc.py b/galleries/examples/text_labels_and_annotations/font_family_rc.py
    new file mode 100644
    index 000000000000..bdf993b76a9e
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/font_family_rc.py
    @@ -0,0 +1,71 @@
    +"""
    +=========================
    +Configure the font family
    +=========================
    +
    +You can explicitly set which font family is picked up, either by specifying
    +family names of fonts installed on user's system, or generic-families
    +(e.g., 'serif', 'sans-serif', 'monospace', 'fantasy' or 'cursive'),
    +or a combination of both.
    +(see :ref:`text_props`)
    +
    +In the example below, we are overriding the default sans-serif generic family
    +to include a specific (Tahoma) font. (Note that the best way to achieve this
    +would simply be to prepend 'Tahoma' in 'font.family')
    +
    +The default family is set with the font.family rcparam,
    +e.g. ::
    +
    +  rcParams['font.family'] = 'sans-serif'
    +
    +and for the font.family you set a list of font styles to try to find
    +in order::
    +
    +  rcParams['font.sans-serif'] = ['Tahoma', 'DejaVu Sans',
    +                                 'Lucida Grande', 'Verdana']
    +
    +.. redirect-from:: /gallery/font_family_rc_sgskip
    +
    +The ``font.family`` defaults are OS dependent and can be viewed with:
    +"""
    +import matplotlib.pyplot as plt
    +
    +print(plt.rcParams["font.sans-serif"][0])
    +print(plt.rcParams["font.monospace"][0])
    +
    +
    +# %%
    +# Choose default sans-serif font
    +
    +def print_text(text):
    +    fig, ax = plt.subplots(figsize=(6, 1), facecolor="#eefade")
    +    ax.text(0.5, 0.5, text, ha='center', va='center', size=40)
    +    ax.axis("off")
    +    plt.show()
    +
    +
    +plt.rcParams["font.family"] = "sans-serif"
    +print_text("Hello World! 01")
    +
    +
    +# %%
    +# Choose sans-serif font and specify to it to "Nimbus Sans"
    +
    +plt.rcParams["font.family"] = "sans-serif"
    +plt.rcParams["font.sans-serif"] = ["Nimbus Sans"]
    +print_text("Hello World! 02")
    +
    +
    +# %%
    +# Choose default monospace font
    +
    +plt.rcParams["font.family"] = "monospace"
    +print_text("Hello World! 03")
    +
    +
    +# %%
    +# Choose monospace font and specify to it to "FreeMono"
    +
    +plt.rcParams["font.family"] = "monospace"
    +plt.rcParams["font.monospace"] = ["FreeMono"]
    +print_text("Hello World! 04")
    diff --git a/galleries/examples/text_labels_and_annotations/font_file.py b/galleries/examples/text_labels_and_annotations/font_file.py
    new file mode 100644
    index 000000000000..b4a58808d716
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/font_file.py
    @@ -0,0 +1,41 @@
    +r"""
    +====================
    +Using ttf font files
    +====================
    +
    +Although it is usually not a good idea to explicitly point to a single ttf file
    +for a font instance, you can do so by passing a `pathlib.Path` instance as the
    +*font* parameter.  Note that passing paths as `str`\s is intentionally not
    +supported, but you can simply wrap `str`\s in `pathlib.Path`\s as needed.
    +
    +Here, we use the Computer Modern roman font (``cmr10``) shipped with
    +Matplotlib.
    +
    +For a more flexible solution, see
    +:doc:`/gallery/text_labels_and_annotations/font_family_rc` and
    +:doc:`/gallery/text_labels_and_annotations/fonts_demo`.
    +"""
    +
    +from pathlib import Path
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib as mpl
    +
    +fig, ax = plt.subplots()
    +
    +fpath = Path(mpl.get_data_path(), "fonts/ttf/cmr10.ttf")
    +ax.set_title(f'This is a special font: {fpath.name}', font=fpath)
    +ax.set_xlabel('This is the default font')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.set_title`
    diff --git a/galleries/examples/text_labels_and_annotations/font_table.py b/galleries/examples/text_labels_and_annotations/font_table.py
    new file mode 100644
    index 000000000000..490f627b1033
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/font_table.py
    @@ -0,0 +1,123 @@
    +"""
    +==========
    +Font table
    +==========
    +
    +Matplotlib's font support is provided by the FreeType library.
    +
    +Here, we use `~.Axes.table` to draw a table that shows the glyphs by Unicode
    +codepoint. For brevity, the table only contains the first 256 glyphs.
    +
    +The example is a full working script. You can download it and use it to
    +investigate a font by running ::
    +
    +    python font_table.py /path/to/font/file
    +"""
    +
    +import os
    +from pathlib import Path
    +import unicodedata
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.font_manager as fm
    +from matplotlib.ft2font import FT2Font
    +
    +
    +def print_glyphs(path):
    +    """
    +    Print the all glyphs in the given font file to stdout.
    +
    +    Parameters
    +    ----------
    +    path : str or None
    +        The path to the font file.  If None, use Matplotlib's default font.
    +    """
    +    if path is None:
    +        path = fm.findfont(fm.FontProperties())  # The default font.
    +
    +    font = FT2Font(path)
    +
    +    charmap = font.get_charmap()
    +    max_indices_len = len(str(max(charmap.values())))
    +
    +    print("The font face contains the following glyphs:")
    +    for char_code, glyph_index in charmap.items():
    +        char = chr(char_code)
    +        name = unicodedata.name(
    +                char,
    +                f"{char_code:#x} ({font.get_glyph_name(glyph_index)})")
    +        print(f"{glyph_index:>{max_indices_len}} {char} {name}")
    +
    +
    +def draw_font_table(path):
    +    """
    +    Draw a font table of the first 255 chars of the given font.
    +
    +    Parameters
    +    ----------
    +    path : str or None
    +        The path to the font file.  If None, use Matplotlib's default font.
    +    """
    +    if path is None:
    +        path = fm.findfont(fm.FontProperties())  # The default font.
    +
    +    font = FT2Font(path)
    +    # A charmap is a mapping of "character codes" (in the sense of a character
    +    # encoding, e.g. latin-1) to glyph indices (i.e. the internal storage table
    +    # of the font face).
    +    # In FreeType>=2.1, a Unicode charmap (i.e. mapping Unicode codepoints)
    +    # is selected by default.  Moreover, recent versions of FreeType will
    +    # automatically synthesize such a charmap if the font does not include one
    +    # (this behavior depends on the font format; for example it is present
    +    # since FreeType 2.0 for Type 1 fonts but only since FreeType 2.8 for
    +    # TrueType (actually, SFNT) fonts).
    +    # The code below (specifically, the ``chr(char_code)`` call) assumes that
    +    # we have indeed selected a Unicode charmap.
    +    codes = font.get_charmap().items()
    +
    +    labelc = [f"{i:X}" for i in range(16)]
    +    labelr = [f"{i:02X}" for i in range(0, 16*16, 16)]
    +    chars = [["" for c in range(16)] for r in range(16)]
    +
    +    for char_code, glyph_index in codes:
    +        if char_code >= 256:
    +            continue
    +        row, col = divmod(char_code, 16)
    +        chars[row][col] = chr(char_code)
    +
    +    fig, ax = plt.subplots(figsize=(8, 4))
    +    ax.set_title(os.path.basename(path))
    +    ax.set_axis_off()
    +
    +    table = ax.table(
    +        cellText=chars,
    +        rowLabels=labelr,
    +        colLabels=labelc,
    +        rowColours=["palegreen"] * 16,
    +        colColours=["palegreen"] * 16,
    +        cellColours=[[".95" for c in range(16)] for r in range(16)],
    +        cellLoc='center',
    +        loc='upper left',
    +    )
    +    for key, cell in table.get_celld().items():
    +        row, col = key
    +        if row > 0 and col > -1:  # Beware of table's idiosyncratic indexing...
    +            cell.set_text_props(font=Path(path))
    +
    +    fig.tight_layout()
    +    plt.show()
    +
    +
    +if __name__ == "__main__":
    +    from argparse import ArgumentParser
    +
    +    parser = ArgumentParser(description="Display a font table.")
    +    parser.add_argument("path", nargs="?", help="Path to the font file.")
    +    parser.add_argument("--print-all", action="store_true",
    +                        help="Additionally, print all chars to stdout.")
    +    args = parser.parse_args()
    +
    +    if args.print_all:
    +        print_glyphs(args.path)
    +    draw_font_table(args.path)
    diff --git a/galleries/examples/text_labels_and_annotations/fonts_demo.py b/galleries/examples/text_labels_and_annotations/fonts_demo.py
    new file mode 100644
    index 000000000000..821ee278c4ba
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/fonts_demo.py
    @@ -0,0 +1,64 @@
    +"""
    +==================================
    +Fonts demo (object-oriented style)
    +==================================
    +
    +Set font properties using setters.
    +
    +See :doc:`fonts_demo_kw` to achieve the same effect using keyword arguments.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.font_manager import FontProperties
    +
    +fig = plt.figure()
    +alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'}
    +yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2]
    +heading_font = FontProperties(size='large')
    +
    +# Show family options
    +fig.text(0.1, 0.9, 'family', fontproperties=heading_font, **alignment)
    +families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace']
    +for k, family in enumerate(families):
    +    font = FontProperties(family=[family])
    +    fig.text(0.1, yp[k], family, fontproperties=font, **alignment)
    +
    +# Show style options
    +styles = ['normal', 'italic', 'oblique']
    +fig.text(0.3, 0.9, 'style', fontproperties=heading_font, **alignment)
    +for k, style in enumerate(styles):
    +    font = FontProperties(family='sans-serif', style=style)
    +    fig.text(0.3, yp[k], style, fontproperties=font, **alignment)
    +
    +# Show variant options
    +variants = ['normal', 'small-caps']
    +fig.text(0.5, 0.9, 'variant', fontproperties=heading_font, **alignment)
    +for k, variant in enumerate(variants):
    +    font = FontProperties(family='serif', variant=variant)
    +    fig.text(0.5, yp[k], variant, fontproperties=font, **alignment)
    +
    +# Show weight options
    +weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black']
    +fig.text(0.7, 0.9, 'weight', fontproperties=heading_font, **alignment)
    +for k, weight in enumerate(weights):
    +    font = FontProperties(weight=weight)
    +    fig.text(0.7, yp[k], weight, fontproperties=font, **alignment)
    +
    +# Show size options
    +sizes = [
    +    'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']
    +fig.text(0.9, 0.9, 'size', fontproperties=heading_font, **alignment)
    +for k, size in enumerate(sizes):
    +    font = FontProperties(size=size)
    +    fig.text(0.9, yp[k], size, fontproperties=font, **alignment)
    +
    +# Show bold italic
    +font = FontProperties(style='italic', weight='bold', size='x-small')
    +fig.text(0.3, 0.1, 'bold italic', fontproperties=font, **alignment)
    +font = FontProperties(style='italic', weight='bold', size='medium')
    +fig.text(0.3, 0.2, 'bold italic', fontproperties=font, **alignment)
    +font = FontProperties(style='italic', weight='bold', size='x-large')
    +fig.text(0.3, 0.3, 'bold italic', fontproperties=font, **alignment)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/fonts_demo_kw.py b/galleries/examples/text_labels_and_annotations/fonts_demo_kw.py
    new file mode 100644
    index 000000000000..ca19222f878d
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/fonts_demo_kw.py
    @@ -0,0 +1,56 @@
    +"""
    +==============================
    +Fonts demo (keyword arguments)
    +==============================
    +
    +Set font properties using keyword arguments.
    +
    +See :doc:`fonts_demo` to achieve the same effect using setters.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure()
    +alignment = {'horizontalalignment': 'center', 'verticalalignment': 'baseline'}
    +yp = [0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2]
    +
    +# Show family options
    +fig.text(0.1, 0.9, 'family', size='large', **alignment)
    +families = ['serif', 'sans-serif', 'cursive', 'fantasy', 'monospace']
    +for k, family in enumerate(families):
    +    fig.text(0.1, yp[k], family, family=family, **alignment)
    +
    +# Show style options
    +fig.text(0.3, 0.9, 'style', **alignment)
    +styles = ['normal', 'italic', 'oblique']
    +for k, style in enumerate(styles):
    +    fig.text(0.3, yp[k], style, family='sans-serif', style=style, **alignment)
    +
    +# Show variant options
    +fig.text(0.5, 0.9, 'variant', **alignment)
    +variants = ['normal', 'small-caps']
    +for k, variant in enumerate(variants):
    +    fig.text(0.5, yp[k], variant, family='serif', variant=variant, **alignment)
    +
    +# Show weight options
    +fig.text(0.7, 0.9, 'weight', **alignment)
    +weights = ['light', 'normal', 'medium', 'semibold', 'bold', 'heavy', 'black']
    +for k, weight in enumerate(weights):
    +    fig.text(0.7, yp[k], weight, weight=weight, **alignment)
    +
    +# Show size options
    +fig.text(0.9, 0.9, 'size', **alignment)
    +sizes = [
    +    'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']
    +for k, size in enumerate(sizes):
    +    fig.text(0.9, yp[k], size, size=size, **alignment)
    +
    +# Show bold italic
    +fig.text(0.3, 0.1, 'bold italic',
    +         style='italic', weight='bold', size='x-small', **alignment)
    +fig.text(0.3, 0.2, 'bold italic',
    +         style='italic', weight='bold', size='medium', **alignment)
    +fig.text(0.3, 0.3, 'bold italic',
    +         style='italic', weight='bold', size='x-large', **alignment)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/label_subplots.py b/galleries/examples/text_labels_and_annotations/label_subplots.py
    new file mode 100644
    index 000000000000..aa7b94cb74fe
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/label_subplots.py
    @@ -0,0 +1,74 @@
    +"""
    +==================
    +Labelling subplots
    +==================
    +
    +Labelling subplots is relatively straightforward, and varies, so Matplotlib
    +does not have a general method for doing this.
    +
    +We showcase two methods to position text at a given physical offset (in
    +fontsize units or in points) away from a corner of the Axes: one using
    +`~.Axes.annotate`, and one using `.ScaledTranslation`.
    +
    +For convenience, this example uses `.pyplot.subplot_mosaic` and subplot
    +labels as keys for the subplots.  However, the approach also works with
    +`.pyplot.subplots` or keys that are different from what you want to label the
    +subplot with.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.transforms import ScaledTranslation
    +
    +# %%
    +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']],
    +                              layout='constrained')
    +for label, ax in axs.items():
    +    # Use Axes.annotate to put the label
    +    # - at the top left corner (axes fraction (0, 1)),
    +    # - offset half-a-fontsize right and half-a-fontsize down
    +    #   (offset fontsize (+0.5, -0.5)),
    +    # i.e. just inside the axes.
    +    ax.annotate(
    +        label,
    +        xy=(0, 1), xycoords='axes fraction',
    +        xytext=(+0.5, -0.5), textcoords='offset fontsize',
    +        fontsize='medium', verticalalignment='top', fontfamily='serif',
    +        bbox=dict(facecolor='0.7', edgecolor='none', pad=3.0))
    +
    +# %%
    +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']],
    +                              layout='constrained')
    +for label, ax in axs.items():
    +    # Use ScaledTranslation to put the label
    +    # - at the top left corner (axes fraction (0, 1)),
    +    # - offset 20 pixels left and 7 pixels up (offset points (-20, +7)),
    +    # i.e. just outside the axes.
    +    ax.text(
    +        0.0, 1.0, label, transform=(
    +            ax.transAxes + ScaledTranslation(-20/72, +7/72, fig.dpi_scale_trans)),
    +        fontsize='medium', va='bottom', fontfamily='serif')
    +
    +# %%
    +# If we want it aligned with the title, either incorporate in the title or
    +# use the *loc* keyword argument:
    +
    +fig, axs = plt.subplot_mosaic([['a)', 'c)'], ['b)', 'c)'], ['d)', 'd)']],
    +                              layout='constrained')
    +for label, ax in axs.items():
    +    ax.set_title('Normal Title', fontstyle='italic')
    +    ax.set_title(label, fontfamily='serif', loc='left', fontsize='medium')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.subplot_mosaic` /
    +#      `matplotlib.pyplot.subplot_mosaic`
    +#    - `matplotlib.axes.Axes.set_title`
    +#    - `matplotlib.axes.Axes.annotate`
    diff --git a/galleries/examples/text_labels_and_annotations/legend.py b/galleries/examples/text_labels_and_annotations/legend.py
    new file mode 100644
    index 000000000000..82e7fdcc4544
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/legend.py
    @@ -0,0 +1,44 @@
    +"""
    +===============================
    +Legend using pre-defined labels
    +===============================
    +
    +Defining legend labels with plots.
    +"""
    +
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Make some fake data.
    +a = b = np.arange(0, 3, .02)
    +c = np.exp(a)
    +d = c[::-1]
    +
    +# Create plots with pre-defined labels.
    +fig, ax = plt.subplots()
    +ax.plot(a, c, 'k--', label='Model length')
    +ax.plot(a, d, 'k:', label='Data length')
    +ax.plot(a, c + d, 'k', label='Total message length')
    +
    +legend = ax.legend(loc='upper center', shadow=True, fontsize='x-large')
    +
    +# Put a nicer background color on the legend.
    +legend.get_frame().set_facecolor('C0')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    +#    - `matplotlib.axes.Axes.legend` / `matplotlib.pyplot.legend`
    +#
    +# .. seealso::
    +#
    +#    The :ref:`legend_guide` contains an in depth discussion on the configuration
    +#    options for legends.
    diff --git a/galleries/examples/text_labels_and_annotations/legend_demo.py b/galleries/examples/text_labels_and_annotations/legend_demo.py
    new file mode 100644
    index 000000000000..ea948eb4ba14
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/legend_demo.py
    @@ -0,0 +1,187 @@
    +"""
    +===========
    +Legend Demo
    +===========
    +
    +There are many ways to create and customize legends in Matplotlib. Below
    +we'll show a few examples for how to do so.
    +
    +First we'll show off how to make a legend for specific lines.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.collections as mcol
    +from matplotlib.legend_handler import HandlerLineCollection, HandlerTuple
    +from matplotlib.lines import Line2D
    +
    +t1 = np.arange(0.0, 2.0, 0.1)
    +t2 = np.arange(0.0, 2.0, 0.01)
    +
    +fig, ax = plt.subplots()
    +
    +# note that plot returns a list of lines.  The "l1, = plot" usage
    +# extracts the first element of the list into l1 using tuple
    +# unpacking.  So l1 is a Line2D instance, not a sequence of lines
    +l1, = ax.plot(t2, np.exp(-t2))
    +l2, l3 = ax.plot(t2, np.sin(2 * np.pi * t2), '--o', t1, np.log(1 + t1), '.')
    +l4, = ax.plot(t2, np.exp(-t2) * np.sin(2 * np.pi * t2), 's-.')
    +
    +ax.legend((l2, l4), ('oscillatory', 'damped'), loc='upper right', shadow=True)
    +ax.set_xlabel('time')
    +ax.set_ylabel('volts')
    +ax.set_title('Damped oscillation')
    +plt.show()
    +
    +
    +# %%
    +# Next we'll demonstrate plotting more complex labels.
    +
    +x = np.linspace(0, 1)
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1)
    +
    +# Plot the lines y=x**n for n=1..4.
    +for n in range(1, 5):
    +    ax0.plot(x, x**n, label=f"{n=}")
    +leg = ax0.legend(loc="upper left", bbox_to_anchor=[0, 1],
    +                 ncols=2, shadow=True, title="Legend", fancybox=True)
    +leg.get_title().set_color("red")
    +
    +# Demonstrate some more complex labels.
    +ax1.plot(x, x**2, label="multi\nline")
    +half_pi = np.linspace(0, np.pi / 2)
    +ax1.plot(np.sin(half_pi), np.cos(half_pi), label=r"$\frac{1}{2}\pi$")
    +ax1.plot(x, 2**(x**2), label="$2^{x^2}$")
    +ax1.legend(shadow=True, fancybox=True)
    +
    +plt.show()
    +
    +
    +# %%
    +# Here we attach legends to more complex plots.
    +
    +fig, axs = plt.subplots(3, 1, layout="constrained")
    +top_ax, middle_ax, bottom_ax = axs
    +
    +top_ax.bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, label="Bar 1",
    +           align="center")
    +top_ax.bar([0.5, 1.5, 2.5], [0.3, 0.2, 0.2], color="red", width=0.4,
    +           label="Bar 2", align="center")
    +top_ax.legend()
    +
    +middle_ax.errorbar([0, 1, 2], [2, 3, 1], xerr=0.4, fmt="s", label="test 1")
    +middle_ax.errorbar([0, 1, 2], [3, 2, 4], yerr=0.3, fmt="o", label="test 2")
    +middle_ax.errorbar([0, 1, 2], [1, 1, 3], xerr=0.4, yerr=0.3, fmt="^",
    +                   label="test 3")
    +middle_ax.legend()
    +
    +bottom_ax.stem([0.3, 1.5, 2.7], [1, 3.6, 2.7], label="stem test")
    +bottom_ax.legend()
    +
    +plt.show()
    +
    +# %%
    +# Now we'll showcase legend entries with more than one legend key.
    +
    +fig, (ax1, ax2) = plt.subplots(2, 1, layout='constrained')
    +
    +# First plot: two legend keys for a single entry
    +p1 = ax1.scatter([1], [5], c='r', marker='s', s=100)
    +p2 = ax1.scatter([3], [2], c='b', marker='o', s=100)
    +# `plot` returns a list, but we want the handle - thus the comma on the left
    +p3, = ax1.plot([1, 5], [4, 4], 'm-d')
    +
    +# Assign two of the handles to the same legend entry by putting them in a tuple
    +# and using a generic handler map (which would be used for any additional
    +# tuples of handles like (p1, p3)).
    +l = ax1.legend([(p1, p3), p2], ['two keys', 'one key'], scatterpoints=1,
    +               numpoints=1, handler_map={tuple: HandlerTuple(ndivide=None)})
    +
    +# Second plot: plot two bar charts on top of each other and change the padding
    +# between the legend keys
    +x_left = [1, 2, 3]
    +y_pos = [1, 3, 2]
    +y_neg = [2, 1, 4]
    +
    +rneg = ax2.bar(x_left, y_neg, width=0.5, color='w', hatch='///', label='-1')
    +rpos = ax2.bar(x_left, y_pos, width=0.5, color='k', label='+1')
    +
    +# Treat each legend entry differently by using specific `HandlerTuple`s
    +l = ax2.legend([(rpos, rneg), (rneg, rpos)], ['pad!=0', 'pad=0'],
    +               handler_map={(rpos, rneg): HandlerTuple(ndivide=None),
    +                            (rneg, rpos): HandlerTuple(ndivide=None, pad=0.)})
    +plt.show()
    +
    +# %%
    +# Finally, it is also possible to write custom classes that define
    +# how to stylize legends.
    +
    +
    +class HandlerDashedLines(HandlerLineCollection):
    +    """
    +    Custom Handler for LineCollection instances.
    +    """
    +    def create_artists(self, legend, orig_handle,
    +                       xdescent, ydescent, width, height, fontsize, trans):
    +        # figure out how many lines there are
    +        numlines = len(orig_handle.get_segments())
    +        xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent,
    +                                             width, height, fontsize)
    +        leglines = []
    +        # divide the vertical space where the lines will go
    +        # into equal parts based on the number of lines
    +        ydata = np.full_like(xdata, height / (numlines + 1))
    +        # for each line, create the line at the proper location
    +        # and set the dash pattern
    +        for i in range(numlines):
    +            legline = Line2D(xdata, ydata * (numlines - i) - ydescent)
    +            self.update_prop(legline, orig_handle, legend)
    +            # set color, dash pattern, and linewidth to that
    +            # of the lines in linecollection
    +            try:
    +                color = orig_handle.get_colors()[i]
    +            except IndexError:
    +                color = orig_handle.get_colors()[0]
    +            try:
    +                dashes = orig_handle.get_dashes()[i]
    +            except IndexError:
    +                dashes = orig_handle.get_dashes()[0]
    +            try:
    +                lw = orig_handle.get_linewidths()[i]
    +            except IndexError:
    +                lw = orig_handle.get_linewidths()[0]
    +            if dashes[1] is not None:
    +                legline.set_dashes(dashes[1])
    +            legline.set_color(color)
    +            legline.set_transform(trans)
    +            legline.set_linewidth(lw)
    +            leglines.append(legline)
    +        return leglines
    +
    +x = np.linspace(0, 5, 100)
    +
    +fig, ax = plt.subplots()
    +colors = plt.rcParams['axes.prop_cycle'].by_key()['color'][:5]
    +styles = ['solid', 'dashed', 'dashed', 'dashed', 'solid']
    +for i, color, style in zip(range(5), colors, styles):
    +    ax.plot(x, np.sin(x) - .1 * i, c=color, ls=style)
    +
    +# make proxy artists
    +# make list of one line -- doesn't matter what the coordinates are
    +line = [[(0, 0)]]
    +# set up the proxy artist
    +lc = mcol.LineCollection(5 * line, linestyles=styles, colors=colors)
    +# create the legend
    +ax.legend([lc], ['multi-line'], handler_map={type(lc): HandlerDashedLines()},
    +          handlelength=2.5, handleheight=3)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. seealso::
    +#
    +#    The :ref:`legend_guide` contains an in depth discussion on the configuration
    +#    options for legends.
    diff --git a/galleries/examples/text_labels_and_annotations/line_with_text.py b/galleries/examples/text_labels_and_annotations/line_with_text.py
    new file mode 100644
    index 000000000000..22f5580b33ba
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/line_with_text.py
    @@ -0,0 +1,89 @@
    +"""
    +=======================
    +Artist within an artist
    +=======================
    +
    +Override basic methods so an artist can contain another
    +artist.  In this case, the line contains a Text instance to label it.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.lines as lines
    +import matplotlib.text as mtext
    +import matplotlib.transforms as mtransforms
    +
    +
    +class MyLine(lines.Line2D):
    +    def __init__(self, *args, **kwargs):
    +        # we'll update the position when the line data is set
    +        self.text = mtext.Text(0, 0, '')
    +        super().__init__(*args, **kwargs)
    +
    +        # we can't access the label attr until *after* the line is
    +        # initiated
    +        self.text.set_text(self.get_label())
    +
    +    def set_figure(self, figure):
    +        self.text.set_figure(figure)
    +        super().set_figure(figure)
    +
    +    # Override the Axes property setter to set Axes on our children as well.
    +    @lines.Line2D.axes.setter
    +    def axes(self, new_axes):
    +        self.text.axes = new_axes
    +        lines.Line2D.axes.fset(self, new_axes)  # Call the superclass property setter.
    +
    +    def set_transform(self, transform):
    +        # 2 pixel offset
    +        texttrans = transform + mtransforms.Affine2D().translate(2, 2)
    +        self.text.set_transform(texttrans)
    +        super().set_transform(transform)
    +
    +    def set_data(self, x, y):
    +        if len(x):
    +            self.text.set_position((x[-1], y[-1]))
    +
    +        super().set_data(x, y)
    +
    +    def draw(self, renderer):
    +        # draw my label at the end of the line with 2 pixel offset
    +        super().draw(renderer)
    +        self.text.draw(renderer)
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig, ax = plt.subplots()
    +x, y = np.random.rand(2, 20)
    +line = MyLine(x, y, mfc='red', ms=12, label='line label')
    +line.text.set_color('red')
    +line.text.set_fontsize(16)
    +
    +ax.add_line(line)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.lines`
    +#    - `matplotlib.lines.Line2D`
    +#    - `matplotlib.lines.Line2D.set_data`
    +#    - `matplotlib.artist`
    +#    - `matplotlib.artist.Artist`
    +#    - `matplotlib.artist.Artist.draw`
    +#    - `matplotlib.artist.Artist.set_transform`
    +#    - `matplotlib.text`
    +#    - `matplotlib.text.Text`
    +#    - `matplotlib.text.Text.set_color`
    +#    - `matplotlib.text.Text.set_fontsize`
    +#    - `matplotlib.text.Text.set_position`
    +#    - `matplotlib.axes.Axes.add_line`
    +#    - `matplotlib.transforms`
    +#    - `matplotlib.transforms.Affine2D`
    diff --git a/galleries/examples/text_labels_and_annotations/mathtext_asarray.py b/galleries/examples/text_labels_and_annotations/mathtext_asarray.py
    new file mode 100644
    index 000000000000..3c8a763451ea
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/mathtext_asarray.py
    @@ -0,0 +1,60 @@
    +"""
    +=======================
    +Convert texts to images
    +=======================
    +"""
    +
    +from io import BytesIO
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.figure import Figure
    +from matplotlib.transforms import IdentityTransform
    +
    +
    +def text_to_rgba(s, *, dpi, **kwargs):
    +    # To convert a text string to an image, we can:
    +    # - draw it on an empty and transparent figure;
    +    # - save the figure to a temporary buffer using ``bbox_inches="tight",
    +    #   pad_inches=0`` which will pick the correct area to save;
    +    # - load the buffer using ``plt.imread``.
    +    #
    +    # (If desired, one can also directly save the image to the filesystem.)
    +    fig = Figure(facecolor="none")
    +    fig.text(0, 0, s, **kwargs)
    +    with BytesIO() as buf:
    +        fig.savefig(buf, dpi=dpi, format="png", bbox_inches="tight",
    +                    pad_inches=0)
    +        buf.seek(0)
    +        rgba = plt.imread(buf)
    +    return rgba
    +
    +
    +fig = plt.figure()
    +rgba1 = text_to_rgba(r"IQ: $\sigma_i=15$", color="blue", fontsize=20, dpi=200)
    +rgba2 = text_to_rgba(r"some other string", color="red", fontsize=20, dpi=200)
    +# One can then draw such text images to a Figure using `.Figure.figimage`.
    +fig.figimage(rgba1, 100, 50)
    +fig.figimage(rgba2, 100, 150)
    +
    +# One can also directly draw texts to a figure with positioning
    +# in pixel coordinates by using `.Figure.text` together with
    +# `.transforms.IdentityTransform`.
    +fig.text(100, 250, r"IQ: $\sigma_i=15$", color="blue", fontsize=20,
    +         transform=IdentityTransform())
    +fig.text(100, 350, r"some other string", color="red", fontsize=20,
    +         transform=IdentityTransform())
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.figimage`
    +#    - `matplotlib.figure.Figure.text`
    +#    - `matplotlib.transforms.IdentityTransform`
    +#    - `matplotlib.image.imread`
    diff --git a/galleries/examples/text_labels_and_annotations/mathtext_demo.py b/galleries/examples/text_labels_and_annotations/mathtext_demo.py
    new file mode 100644
    index 000000000000..c41596b91f49
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/mathtext_demo.py
    @@ -0,0 +1,26 @@
    +"""
    +========
    +Mathtext
    +========
    +
    +Use Matplotlib's internal LaTeX parser and layout engine.  For true LaTeX
    +rendering, see the text.usetex option.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +ax.plot([1, 2, 3], label=r'$\sqrt{x^2}$')
    +ax.legend()
    +
    +ax.set_xlabel(r'$\Delta_i^j$', fontsize=20)
    +ax.set_ylabel(r'$\Delta_{i+1}^j$', fontsize=20)
    +ax.set_title(r'$\Delta_i^j \hspace{0.4} \mathrm{versus} \hspace{0.4} '
    +             r'\Delta_{i+1}^j$', fontsize=20)
    +
    +tex = r'$\mathcal{R}\prod_{i=\alpha_{i+1}}^\infty a_i\sin(2 \pi f x_i)$'
    +ax.text(1, 1.6, tex, fontsize=20, va='bottom')
    +
    +fig.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/mathtext_examples.py b/galleries/examples/text_labels_and_annotations/mathtext_examples.py
    new file mode 100644
    index 000000000000..cf395f0daf0e
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/mathtext_examples.py
    @@ -0,0 +1,120 @@
    +"""
    +========================
    +Mathematical expressions
    +========================
    +
    +Selected features of Matplotlib's math rendering engine.
    +"""
    +import re
    +import subprocess
    +import sys
    +
    +import matplotlib.pyplot as plt
    +
    +# Selection of features following "Writing mathematical expressions" tutorial,
    +# with randomly picked examples.
    +mathtext_demos = {
    +    "Header demo":
    +        r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = "
    +        r"U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} "
    +        r"\int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ "
    +        r"U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_"
    +        r"{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$",
    +
    +    "Subscripts and superscripts":
    +        r"$\alpha_i > \beta_i,\ "
    +        r"\alpha_{i+1}^j = {\rm sin}(2\pi f_j t_i) e^{-5 t_i/\tau},\ "
    +        r"\ldots$",
    +
    +    "Fractions, binomials and stacked numbers":
    +        r"$\frac{3}{4},\ \binom{3}{4},\ \genfrac{}{}{0}{}{3}{4},\ "
    +        r"\left(\frac{5 - \frac{1}{x}}{4}\right),\ \ldots$",
    +
    +    "Radicals":
    +        r"$\sqrt{2},\ \sqrt[3]{x},\ \ldots$",
    +
    +    "Fonts":
    +        r"$\mathrm{Roman}\ , \ \mathit{Italic}\ , \ \mathtt{Typewriter} \ "
    +        r"\mathrm{or}\ \mathcal{CALLIGRAPHY}$",
    +
    +    "Accents":
    +        r"$\acute a,\ \bar a,\ \breve a,\ \dot a,\ \ddot a, \ \grave a, \ "
    +        r"\hat a,\ \tilde a,\ \vec a,\ \widehat{xyz},\ \widetilde{xyz},\ "
    +        r"\ldots$",
    +
    +    "Greek, Hebrew":
    +        r"$\alpha,\ \beta,\ \chi,\ \delta,\ \lambda,\ \mu,\ "
    +        r"\Delta,\ \Gamma,\ \Omega,\ \Phi,\ \Pi,\ \Upsilon,\ \nabla,\ "
    +        r"\aleph,\ \beth,\ \daleth,\ \gimel,\ \ldots$",
    +
    +    "Delimiters, functions and Symbols":
    +        r"$\coprod,\ \int,\ \oint,\ \prod,\ \sum,\ "
    +        r"\log,\ \sin,\ \approx,\ \oplus,\ \star,\ \varpropto,\ "
    +        r"\infty,\ \partial,\ \Re,\ \leftrightsquigarrow, \ \ldots$",
    +}
    +n_lines = len(mathtext_demos)
    +
    +
    +def doall():
    +    # Colors used in Matplotlib online documentation.
    +    mpl_grey_rgb = (51 / 255, 51 / 255, 51 / 255)
    +
    +    # Creating figure and axis.
    +    fig = plt.figure(figsize=(7, 7))
    +    ax = fig.add_axes((0.01, 0.01, 0.98, 0.90),
    +                      facecolor="white", frameon=True)
    +    ax.set_xlim(0, 1)
    +    ax.set_ylim(0, 1)
    +    ax.set_title("Matplotlib's math rendering engine",
    +                 color=mpl_grey_rgb, fontsize=14, weight='bold')
    +    ax.set_xticks([])
    +    ax.set_yticks([])
    +
    +    # Gap between lines in axes coords
    +    line_axesfrac = 1 / n_lines
    +
    +    # Plot header demonstration formula.
    +    full_demo = mathtext_demos['Header demo']
    +    ax.annotate(full_demo,
    +                xy=(0.5, 1. - 0.59 * line_axesfrac),
    +                color='tab:orange', ha='center', fontsize=20)
    +
    +    # Plot feature demonstration formulae.
    +    for i_line, (title, demo) in enumerate(mathtext_demos.items()):
    +        print(i_line, demo)
    +        if i_line == 0:
    +            continue
    +
    +        baseline = 1 - i_line * line_axesfrac
    +        baseline_next = baseline - line_axesfrac
    +        fill_color = ['white', 'tab:blue'][i_line % 2]
    +        ax.axhspan(baseline, baseline_next, color=fill_color, alpha=0.2)
    +        ax.annotate(f'{title}:',
    +                    xy=(0.06, baseline - 0.3 * line_axesfrac),
    +                    color=mpl_grey_rgb, weight='bold')
    +        ax.annotate(demo,
    +                    xy=(0.04, baseline - 0.75 * line_axesfrac),
    +                    color=mpl_grey_rgb, fontsize=16)
    +
    +    plt.show()
    +
    +
    +if '--latex' in sys.argv:
    +    # Run: python mathtext_examples.py --latex
    +    # Need amsmath and amssymb packages.
    +    with open("mathtext_examples.ltx", "w") as fd:
    +        fd.write("\\documentclass{article}\n")
    +        fd.write("\\usepackage{amsmath, amssymb}\n")
    +        fd.write("\\begin{document}\n")
    +        fd.write("\\begin{enumerate}\n")
    +
    +        for s in mathtext_demos.values():
    +            s = re.sub(r"(?`_ used in LaTeX
    +rendering.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +circle123 = "\N{CIRCLED DIGIT ONE}\N{CIRCLED DIGIT TWO}\N{CIRCLED DIGIT THREE}"
    +
    +tests = [
    +    r'$%s\;\mathrm{%s}\;\mathbf{%s}$' % ((circle123,) * 3),
    +    r'$\mathsf{Sans \Omega}\;\mathrm{\mathsf{Sans \Omega}}\;'
    +    r'\mathbf{\mathsf{Sans \Omega}}$',
    +    r'$\mathtt{Monospace}$',
    +    r'$\mathcal{CALLIGRAPHIC}$',
    +    r'$\mathbb{Blackboard\;\pi}$',
    +    r'$\mathrm{\mathbb{Blackboard\;\pi}}$',
    +    r'$\mathbf{\mathbb{Blackboard\;\pi}}$',
    +    r'$\mathfrak{Fraktur}\;\mathbf{\mathfrak{Fraktur}}$',
    +    r'$\mathscr{Script}$',
    +]
    +
    +fig = plt.figure(figsize=(8, len(tests) + 2))
    +for i, s in enumerate(tests[::-1]):
    +    fig.text(0, (i + .5) / len(tests), s, fontsize=32)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/tex_demo.py b/galleries/examples/text_labels_and_annotations/tex_demo.py
    new file mode 100644
    index 000000000000..df040c5a866a
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/tex_demo.py
    @@ -0,0 +1,92 @@
    +"""
    +===============================
    +Render math equations using TeX
    +===============================
    +
    +You can use TeX to render all of your Matplotlib text by setting
    +:rc:`text.usetex` to True.  This requires that you have TeX and the other
    +dependencies described in the :ref:`usetex` tutorial properly
    +installed on your system.  Matplotlib caches processed TeX expressions, so that
    +only the first occurrence of an expression triggers a TeX compilation. Later
    +occurrences reuse the rendered image from the cache and are thus faster.
    +
    +Unicode input is supported, e.g. for the y-axis label in this example.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +plt.rcParams['text.usetex'] = True
    +
    +
    +t = np.linspace(0.0, 1.0, 100)
    +s = np.cos(4 * np.pi * t) + 2
    +
    +fig, ax = plt.subplots(figsize=(6, 4), tight_layout=True)
    +ax.plot(t, s)
    +
    +ax.set_xlabel(r'\textbf{time (s)}')
    +ax.set_ylabel('\\textit{Velocity (\N{DEGREE SIGN}/sec)}', fontsize=16)
    +ax.set_title(r'\TeX\ is Number $\displaystyle\sum_{n=1}^\infty'
    +             r'\frac{-e^{i\pi}}{2^n}$!', fontsize=16, color='r')
    +
    +# %%
    +# A more complex example.
    +
    +fig, ax = plt.subplots()
    +# interface tracking profiles
    +N = 500
    +delta = 0.6
    +X = np.linspace(-1, 1, N)
    +ax.plot(X, (1 - np.tanh(4 * X / delta)) / 2,    # phase field tanh profiles
    +        X, (1.4 + np.tanh(4 * X / delta)) / 4, "C2",  # composition profile
    +        X, X < 0, "k--")                        # sharp interface
    +
    +# legend
    +ax.legend(("phase field", "level set", "sharp interface"),
    +          shadow=True, loc=(0.01, 0.48), handlelength=1.5, fontsize=16)
    +
    +# the arrow
    +ax.annotate("", xy=(-delta / 2., 0.1), xytext=(delta / 2., 0.1),
    +            arrowprops=dict(arrowstyle="<->", connectionstyle="arc3"))
    +ax.text(0, 0.1, r"$\delta$",
    +        color="black", fontsize=24,
    +        horizontalalignment="center", verticalalignment="center",
    +        bbox=dict(boxstyle="round", fc="white", ec="black", pad=0.2))
    +
    +# Use tex in labels
    +ax.set_xticks([-1, 0, 1])
    +ax.set_xticklabels(["$-1$", r"$\pm 0$", "$+1$"], color="k", size=20)
    +
    +# Left Y-axis labels, combine math mode and text mode
    +ax.set_ylabel(r"\bf{phase field} $\phi$", color="C0", fontsize=20)
    +ax.set_yticks([0, 0.5, 1])
    +ax.set_yticklabels([r"\bf{0}", r"\bf{.5}", r"\bf{1}"], color="k", size=20)
    +
    +# Right Y-axis labels
    +ax.text(1.02, 0.5, r"\bf{level set} $\phi$",
    +        color="C2", fontsize=20, rotation=90,
    +        horizontalalignment="left", verticalalignment="center",
    +        clip_on=False, transform=ax.transAxes)
    +
    +# Use multiline environment inside a `text`.
    +# level set equations
    +eq1 = (r"\begin{eqnarray*}"
    +       r"|\nabla\phi| &=& 1,\\"
    +       r"\frac{\partial \phi}{\partial t} + U|\nabla \phi| &=& 0 "
    +       r"\end{eqnarray*}")
    +ax.text(1, 0.9, eq1, color="C2", fontsize=18,
    +        horizontalalignment="right", verticalalignment="top")
    +
    +# phase field equations
    +eq2 = (r"\begin{eqnarray*}"
    +       r"\mathcal{F} &=& \int f\left( \phi, c \right) dV, \\ "
    +       r"\frac{ \partial \phi } { \partial t } &=& -M_{ \phi } "
    +       r"\frac{ \delta \mathcal{F} } { \delta \phi }"
    +       r"\end{eqnarray*}")
    +ax.text(0.18, 0.18, eq2, color="C0", fontsize=16)
    +
    +ax.text(-1, .30, r"gamma: $\gamma$", color="r", fontsize=20)
    +ax.text(-1, .18, r"Omega: $\Omega$", color="b", fontsize=20)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/text_alignment.py b/galleries/examples/text_labels_and_annotations/text_alignment.py
    new file mode 100644
    index 000000000000..4c0351e0bbc5
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/text_alignment.py
    @@ -0,0 +1,119 @@
    +"""
    +==============
    +Text alignment
    +==============
    +
    +Texts are aligned relative to their anchor point depending on the properties
    +``horizontalalignment`` (default: ``left``) and ``verticalalignment``
    +(default: ``baseline``.)
    +
    +.. redirect-from:: /gallery/pyplots/text_layout
    +
    +.. plot::
    +
    +    import matplotlib.pyplot as plt
    +    import numpy as np
    +
    +    y = [0.22, 0.34, 0.5, 0.56, 0.78]
    +    x = [0.17, 0.5, 0.855]
    +    X, Y = np.meshgrid(x, y)
    +
    +    fig, ax = plt.subplots(figsize=(6, 4), dpi=100)
    +    ax.set(xlim=(0, 1), ylim=(0, 1), xticks=[], yticks=[])
    +    ax.spines[:].set_visible(False)
    +    ax.text(0.5, 0.5, 'plot', fontsize=128, ha='center', va='center', zorder=1)
    +    ax.hlines(y, x[0], x[-1], color='grey')
    +    ax.vlines(x, y[0], y[-1], color='grey')
    +    ax.plot(X.ravel(), Y.ravel(), 'o')
    +    pad_x = 0.02
    +    pad_y = 0.04
    +    ax.text(x[0] - pad_x, y[0], 'bottom', ha='right', va='center')
    +    ax.text(x[0] - pad_x, y[1], 'baseline', ha='right', va='center')
    +    ax.text(x[0] - pad_x, y[2], 'center', ha='right', va='center')
    +    ax.text(x[0] - pad_x, y[3], 'center_baseline', ha='right', va='center')
    +    ax.text(x[0] - pad_x, y[4], 'top', ha='right', va='center')
    +    ax.text(x[0], y[0] - pad_y, 'left', ha='center', va='top')
    +    ax.text(x[1], y[0] - pad_y, 'center', ha='center', va='top')
    +    ax.text(x[2], y[0] - pad_y, 'right', ha='center', va='top')
    +    ax.set_xlabel('horizontalalignment', fontsize=14)
    +    ax.set_ylabel('verticalalignment', fontsize=14, labelpad=35)
    +    ax.set_title(
    +        'Relative position of text anchor point depending on alignment')
    +    plt.show()
    +
    +"""
    +
    +# %%
    +# The following plot uses this to align text relative to a plotted rectangle.
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +# Build a rectangle in axes coords
    +left, width = .25, .5
    +bottom, height = .25, .5
    +right = left + width
    +top = bottom + height
    +p = plt.Rectangle((left, bottom), width, height, fill=False)
    +p.set_transform(ax.transAxes)
    +p.set_clip_on(False)
    +ax.add_patch(p)
    +
    +ax.text(left, bottom, 'left top',
    +        horizontalalignment='left',
    +        verticalalignment='top',
    +        transform=ax.transAxes)
    +
    +ax.text(left, bottom, 'left bottom',
    +        horizontalalignment='left',
    +        verticalalignment='bottom',
    +        transform=ax.transAxes)
    +
    +ax.text(right, top, 'right bottom',
    +        horizontalalignment='right',
    +        verticalalignment='bottom',
    +        transform=ax.transAxes)
    +
    +ax.text(right, top, 'right top',
    +        horizontalalignment='right',
    +        verticalalignment='top',
    +        transform=ax.transAxes)
    +
    +ax.text(right, bottom, 'center top',
    +        horizontalalignment='center',
    +        verticalalignment='top',
    +        transform=ax.transAxes)
    +
    +ax.text(left, 0.5 * (bottom + top), 'right center',
    +        horizontalalignment='right',
    +        verticalalignment='center',
    +        rotation='vertical',
    +        transform=ax.transAxes)
    +
    +ax.text(left, 0.5 * (bottom + top), 'left center',
    +        horizontalalignment='left',
    +        verticalalignment='center',
    +        rotation='vertical',
    +        transform=ax.transAxes)
    +
    +ax.text(0.5 * (left + right), 0.5 * (bottom + top), 'middle',
    +        horizontalalignment='center',
    +        verticalalignment='center',
    +        transform=ax.transAxes)
    +
    +ax.text(right, 0.5 * (bottom + top), 'centered',
    +        horizontalalignment='center',
    +        verticalalignment='center',
    +        rotation='vertical',
    +        transform=ax.transAxes)
    +
    +ax.text(left, top, 'rotated\nwith newlines',
    +        horizontalalignment='center',
    +        verticalalignment='center',
    +        rotation=45,
    +        transform=ax.transAxes)
    +
    +ax.set_axis_off()
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/text_commands.py b/galleries/examples/text_labels_and_annotations/text_commands.py
    new file mode 100644
    index 000000000000..0650ff53bd5d
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/text_commands.py
    @@ -0,0 +1,58 @@
    +"""
    +===============
    +Text properties
    +===============
    +
    +Plotting text of many different kinds.
    +
    +.. redirect-from:: /gallery/pyplots/text_commands
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure()
    +fig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')
    +
    +ax = fig.add_subplot()
    +fig.subplots_adjust(top=0.85)
    +ax.set_title('axes title')
    +
    +ax.set_xlabel('xlabel')
    +ax.set_ylabel('ylabel')
    +
    +ax.text(3, 8, 'boxed italics text in data coords', style='italic',
    +        bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})
    +
    +ax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)
    +
    +ax.text(3, 2, 'Unicode: Institut f\374r Festk\366rperphysik')
    +
    +ax.text(0.95, 0.01, 'colored text in axes coords',
    +        verticalalignment='bottom', horizontalalignment='right',
    +        transform=ax.transAxes,
    +        color='green', fontsize=15)
    +
    +
    +ax.plot([2], [1], 'o')
    +ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),
    +            arrowprops=dict(facecolor='black', shrink=0.05))
    +
    +ax.set(xlim=(0, 10), ylim=(0, 10))
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.suptitle`
    +#    - `matplotlib.figure.Figure.add_subplot`
    +#    - `matplotlib.figure.Figure.subplots_adjust`
    +#    - `matplotlib.axes.Axes.set_title`
    +#    - `matplotlib.axes.Axes.set_xlabel`
    +#    - `matplotlib.axes.Axes.set_ylabel`
    +#    - `matplotlib.axes.Axes.text`
    +#    - `matplotlib.axes.Axes.annotate`
    diff --git a/galleries/examples/text_labels_and_annotations/text_fontdict.py b/galleries/examples/text_labels_and_annotations/text_fontdict.py
    new file mode 100644
    index 000000000000..b8ff4c7c9353
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/text_fontdict.py
    @@ -0,0 +1,30 @@
    +"""
    +=======================================================
    +Controlling style of text and labels using a dictionary
    +=======================================================
    +
    +This example shows how to share parameters across many text objects and labels
    +by creating a dictionary of options passed across several functions.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +font = {'family': 'serif',
    +        'color':  'darkred',
    +        'weight': 'normal',
    +        'size': 16,
    +        }
    +
    +x = np.linspace(0.0, 5.0, 100)
    +y = np.cos(2*np.pi*x) * np.exp(-x)
    +
    +plt.plot(x, y, 'k')
    +plt.title('Damped exponential decay', fontdict=font)
    +plt.text(2, 0.65, r'$\cos(2 \pi t) \exp(-t)$', fontdict=font)
    +plt.xlabel('time (s)', fontdict=font)
    +plt.ylabel('voltage (mV)', fontdict=font)
    +
    +# Tweak spacing to prevent clipping of ylabel
    +plt.subplots_adjust(left=0.15)
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/text_rotation_relative_to_line.py b/galleries/examples/text_labels_and_annotations/text_rotation_relative_to_line.py
    new file mode 100644
    index 000000000000..78bc9a647af9
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/text_rotation_relative_to_line.py
    @@ -0,0 +1,32 @@
    +"""
    +=======================================
    +Text rotation angle in data coordinates
    +=======================================
    +
    +Text objects in matplotlib are normally rotated with respect to the
    +screen coordinate system (i.e., 45 degrees rotation plots text along a
    +line that is in between horizontal and vertical no matter how the axes
    +are changed).  However, at times one wants to rotate text with respect
    +to something on the plot.  In this case, the correct angle won't be
    +the angle of that object in the plot coordinate system, but the angle
    +that that object APPEARS in the screen coordinate system.  This angle
    +can be determined automatically by setting the parameter
    +*transform_rotates_text*, as shown in the example below.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +# Plot diagonal line (45 degrees in data coordinates)
    +ax.plot(range(0, 8), range(0, 8))
    +ax.set_xlim([-10, 10])
    +
    +# Plot text
    +ax.text(-8, 0, 'text 45° in screen coordinates', fontsize=18,
    +        rotation=45, rotation_mode='anchor')
    +ax.text(0, 0, 'text 45° in data coordinates', fontsize=18,
    +        rotation=45, rotation_mode='anchor',
    +        transform_rotates_text=True)
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/titles_demo.py b/galleries/examples/text_labels_and_annotations/titles_demo.py
    new file mode 100644
    index 000000000000..6fc0e350fdb2
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/titles_demo.py
    @@ -0,0 +1,59 @@
    +"""
    +=================
    +Title positioning
    +=================
    +
    +Matplotlib can display plot titles centered, flush with the left side of
    +a set of Axes, and flush with the right side of a set of Axes.
    +
    +"""
    +import matplotlib.pyplot as plt
    +
    +plt.plot(range(10))
    +
    +plt.title('Center Title')
    +plt.title('Left Title', loc='left')
    +plt.title('Right Title', loc='right')
    +
    +plt.show()
    +
    +# %%
    +# The vertical position is automatically chosen to avoid decorations
    +# (i.e. labels and ticks) on the topmost x-axis:
    +
    +fig, axs = plt.subplots(1, 2, layout='constrained')
    +
    +ax = axs[0]
    +ax.plot(range(10))
    +ax.xaxis.set_label_position('top')
    +ax.set_xlabel('X-label')
    +ax.set_title('Center Title')
    +
    +ax = axs[1]
    +ax.plot(range(10))
    +ax.xaxis.set_label_position('top')
    +ax.xaxis.tick_top()
    +ax.set_xlabel('X-label')
    +ax.set_title('Center Title')
    +plt.show()
    +
    +# %%
    +# Automatic positioning can be turned off by manually specifying the *y*
    +# keyword argument for the title or setting :rc:`axes.titley` in the rcParams.
    +
    +fig, axs = plt.subplots(1, 2, layout='constrained')
    +
    +ax = axs[0]
    +ax.plot(range(10))
    +ax.xaxis.set_label_position('top')
    +ax.set_xlabel('X-label')
    +ax.set_title('Manual y', y=1.0, pad=-14)
    +
    +plt.rcParams['axes.titley'] = 1.0    # y is in axes-relative coordinates.
    +plt.rcParams['axes.titlepad'] = -14  # pad is in points...
    +ax = axs[1]
    +ax.plot(range(10))
    +ax.set_xlabel('X-label')
    +ax.set_title('rcParam y')
    +
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/unicode_minus.py b/galleries/examples/text_labels_and_annotations/unicode_minus.py
    new file mode 100644
    index 000000000000..ce7dba70089d
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/unicode_minus.py
    @@ -0,0 +1,28 @@
    +"""
    +=============
    +Unicode minus
    +=============
    +
    +By default, tick labels at negative values are rendered using a `Unicode
    +minus`__ (U+2212) rather than an ASCII hyphen (U+002D).  This can be controlled
    +by setting :rc:`axes.unicode_minus`.
    +
    +__ https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes
    +
    +The replacement is performed at draw time of the tick labels (usually during a
    +`.pyplot.show()` or `.pyplot.savefig()` call). Therefore, all tick labels of
    +the figure follow the same setting and we cannot demonstrate both glyphs on
    +real tick labels of the same figure simultaneously.
    +
    +Instead, this example simply showcases the difference between the two glyphs
    +in a magnified font.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure(figsize=(4, 2))
    +fig.text(.15, .6, "Unicode minus:", fontsize=20)
    +fig.text(.85, .6, "\N{MINUS SIGN}1", ha='right', fontsize=20)
    +fig.text(.15, .3, "ASCII hyphen:", fontsize=20)
    +fig.text(.85, .3, "-1", ha='right', fontsize=20)
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/usetex_baseline_test.py b/galleries/examples/text_labels_and_annotations/usetex_baseline_test.py
    new file mode 100644
    index 000000000000..e529b1c8b2de
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/usetex_baseline_test.py
    @@ -0,0 +1,25 @@
    +"""
    +====================
    +Usetex text baseline
    +====================
    +
    +Comparison of text baselines computed for mathtext and usetex.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +plt.rcParams.update({"mathtext.fontset": "cm", "mathtext.rm": "serif"})
    +axs = plt.figure(figsize=(2 * 3, 6.5)).subplots(1, 2)
    +for ax, usetex in zip(axs, [False, True]):
    +    ax.axvline(0, color="r")
    +    test_strings = ["lg", r"$\frac{1}{2}\pi$", r"$p^{3^A}$", r"$p_{3_2}$"]
    +    for i, s in enumerate(test_strings):
    +        ax.axhline(i, color="r")
    +        ax.text(0., 3 - i, s,
    +                usetex=usetex,
    +                verticalalignment="baseline",
    +                size=50,
    +                bbox=dict(pad=0, ec="k", fc="none"))
    +    ax.set(xlim=(-0.1, 1.1), ylim=(-.8, 3.9), xticks=[], yticks=[],
    +           title=f"usetex={usetex}\n")
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/usetex_fonteffects.py b/galleries/examples/text_labels_and_annotations/usetex_fonteffects.py
    new file mode 100644
    index 000000000000..ba1c944536cb
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/usetex_fonteffects.py
    @@ -0,0 +1,29 @@
    +"""
    +===================
    +Usetex font effects
    +===================
    +
    +This script demonstrates that font effects specified in your pdftex.map
    +are now supported in usetex mode.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def setfont(font):
    +    return rf'\font\a {font} at 14pt\a '
    +
    +
    +fig = plt.figure()
    +for y, font, text in zip(
    +    range(5),
    +    ['ptmr8r', 'ptmri8r', 'ptmro8r', 'ptmr8rn', 'ptmrr8re'],
    +    [f'Nimbus Roman No9 L {x}'
    +     for x in ['', 'Italics (real italics for comparison)',
    +               '(slanted)', '(condensed)', '(extended)']],
    +):
    +    fig.text(.1, 1 - (y + 1) / 6, setfont(font) + text, usetex=True)
    +
    +fig.suptitle('Usetex font effects')
    +# Would also work if saving to pdf.
    +plt.show()
    diff --git a/galleries/examples/text_labels_and_annotations/watermark_text.py b/galleries/examples/text_labels_and_annotations/watermark_text.py
    new file mode 100644
    index 000000000000..12fa022cba9c
    --- /dev/null
    +++ b/galleries/examples/text_labels_and_annotations/watermark_text.py
    @@ -0,0 +1,32 @@
    +"""
    +==============
    +Text watermark
    +==============
    +
    +A watermark effect can be achieved by drawing a semi-transparent text.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +fig, ax = plt.subplots()
    +ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7, mfc='orange')
    +ax.grid()
    +
    +ax.text(0.5, 0.5, 'created with matplotlib', transform=ax.transAxes,
    +        fontsize=40, color='gray', alpha=0.5,
    +        ha='center', va='center', rotation=30)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.figure.Figure.text`
    diff --git a/galleries/examples/ticks/README.txt b/galleries/examples/ticks/README.txt
    new file mode 100644
    index 000000000000..82441a0d9ee7
    --- /dev/null
    +++ b/galleries/examples/ticks/README.txt
    @@ -0,0 +1,4 @@
    +.. _ticks_examples:
    +
    +Ticks
    +=====
    diff --git a/galleries/examples/ticks/align_ticklabels.py b/galleries/examples/ticks/align_ticklabels.py
    new file mode 100644
    index 000000000000..ec36e0db4d07
    --- /dev/null
    +++ b/galleries/examples/ticks/align_ticklabels.py
    @@ -0,0 +1,32 @@
    +"""
    +=================
    +Align tick labels
    +=================
    +
    +By default, tick labels are aligned towards the axis. This means the set of
    +*y* tick labels appear right-aligned. Because the alignment reference point
    +is on the axis, left-aligned tick labels would overlap the plotting area.
    +To achieve a good-looking left-alignment, you have to additionally increase
    +the padding.
    +"""
    +import matplotlib.pyplot as plt
    +
    +population = {
    +    "Sydney": 5.2,
    +    "Mexico City": 8.8,
    +    "São Paulo": 12.2,
    +    "Istanbul": 15.9,
    +    "Lagos": 15.9,
    +    "Shanghai": 21.9,
    +}
    +
    +fig, ax = plt.subplots(layout="constrained")
    +ax.barh(population.keys(), population.values())
    +ax.set_xlabel('Population (in millions)')
    +
    +# left-align all ticklabels
    +for ticklabel in ax.get_yticklabels():
    +    ticklabel.set_horizontalalignment("left")
    +
    +# increase padding
    +ax.tick_params("y", pad=70)
    diff --git a/galleries/examples/ticks/auto_ticks.py b/galleries/examples/ticks/auto_ticks.py
    new file mode 100644
    index 000000000000..df7318b639d0
    --- /dev/null
    +++ b/galleries/examples/ticks/auto_ticks.py
    @@ -0,0 +1,49 @@
    +"""
    +====================================
    +Automatically setting tick positions
    +====================================
    +
    +Setting the behavior of tick auto-placement.
    +
    +By default, Matplotlib will choose the number of ticks and tick positions so
    +that there is a reasonable number of ticks on the axis and they are located
    +at "round" numbers.
    +
    +As a result, there may be no ticks on the edges of the plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +np.random.seed(19680801)
    +
    +fig, ax = plt.subplots()
    +dots = np.linspace(0.3, 1.2, 10)
    +X, Y = np.meshgrid(dots, dots)
    +x, y = X.ravel(), Y.ravel()
    +ax.scatter(x, y, c=x+y)
    +plt.show()
    +
    +# %%
    +# If you want to keep ticks at round numbers, and also have ticks at the edges
    +# you can switch :rc:`axes.autolimit_mode` to 'round_numbers'. This expands the
    +# axis limits to the next round number.
    +
    +plt.rcParams['axes.autolimit_mode'] = 'round_numbers'
    +
    +# Note: The limits are calculated at draw-time. Therefore, when using
    +# :rc:`axes.autolimit_mode` in a context manager, it is important that
    +# the ``show()`` command is within the context.
    +
    +fig, ax = plt.subplots()
    +ax.scatter(x, y, c=x+y)
    +plt.show()
    +
    +# %%
    +# The round numbers autolimit_mode is still respected if you set an additional
    +# margin around the data using `.Axes.set_xmargin` / `.Axes.set_ymargin`:
    +
    +fig, ax = plt.subplots()
    +ax.scatter(x, y, c=x+y)
    +ax.set_xmargin(0.8)
    +plt.show()
    diff --git a/galleries/examples/ticks/centered_ticklabels.py b/galleries/examples/ticks/centered_ticklabels.py
    new file mode 100644
    index 000000000000..fa338ec590a9
    --- /dev/null
    +++ b/galleries/examples/ticks/centered_ticklabels.py
    @@ -0,0 +1,42 @@
    +"""
    +===========================
    +Center labels between ticks
    +===========================
    +
    +Tick labels are aligned relative to their associated tick, and are by default
    +centered.
    +
    +However, there is no direct way to center the labels between ticks. To fake
    +this behavior, one can place a minor tick in between the major ticks. Then
    +label the minor tick, and hide the minor tick lines and the major tick labels.
    +
    +Here is an example that labels the months, centered between the ticks.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +import matplotlib.cbook as cbook
    +import matplotlib.dates as dates
    +import matplotlib.ticker as ticker
    +
    +# Load some financial data; Google's stock price
    +r = cbook.get_sample_data('goog.npz')['price_data']
    +r = r[-250:]  # get the last 250 days
    +
    +fig, ax = plt.subplots()
    +ax.plot(r["date"], r["adj_close"])
    +
    +ax.xaxis.set_major_locator(dates.MonthLocator())
    +# 16 is a slight approximation since months differ in number of days.
    +ax.xaxis.set_minor_locator(dates.MonthLocator(bymonthday=16))
    +
    +# The NullFormatter removes the major tick labels
    +ax.xaxis.set_major_formatter(ticker.NullFormatter())
    +ax.xaxis.set_minor_formatter(dates.DateFormatter('%b'))
    +
    +# Remove the minor tick lines
    +ax.tick_params(axis='x', which='minor', tick1On=False, tick2On=False)
    +
    +imid = len(r) // 2
    +ax.set_xlabel(str(r["date"][imid].item().year))
    +plt.show()
    diff --git a/galleries/examples/ticks/colorbar_tick_labelling_demo.py b/galleries/examples/ticks/colorbar_tick_labelling_demo.py
    new file mode 100644
    index 000000000000..6436748a46ec
    --- /dev/null
    +++ b/galleries/examples/ticks/colorbar_tick_labelling_demo.py
    @@ -0,0 +1,64 @@
    +"""
    +=======================
    +Colorbar Tick Labelling
    +=======================
    +
    +Vertical colorbars have ticks, tick labels, and labels visible on the *y* axis,
    +horizontal colorbars on the *x* axis. The ``ticks`` parameter can be used to
    +set the ticks and the ``format`` parameter can be used to format the tick labels
    +of the visible colorbar Axes. For further adjustments, the ``yaxis`` or
    +``xaxis`` Axes of the colorbar can be retrieved using its ``ax`` property.
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as mticker
    +
    +# Fixing random state for reproducibility
    +rng = np.random.default_rng(seed=19680801)
    +
    +# %%
    +# Make plot with vertical (default) colorbar
    +
    +fig, ax = plt.subplots()
    +
    +data = rng.standard_normal((250, 250))
    +
    +cax = ax.imshow(data, vmin=-1, vmax=1, cmap='coolwarm')
    +ax.set_title('Gaussian noise with vertical colorbar')
    +
    +# Add colorbar, make sure to specify tick locations to match desired ticklabels
    +cbar = fig.colorbar(cax,
    +                    ticks=[-1, 0, 1],
    +                    format=mticker.FixedFormatter(['< -1', '0', '> 1']),
    +                    extend='both'
    +                    )
    +labels = cbar.ax.get_yticklabels()
    +labels[0].set_verticalalignment('top')
    +labels[-1].set_verticalalignment('bottom')
    +
    +# %%
    +# Make plot with horizontal colorbar
    +
    +fig, ax = plt.subplots()
    +
    +data = np.clip(data, -1, 1)
    +
    +cax = ax.imshow(data, cmap='afmhot')
    +ax.set_title('Gaussian noise with horizontal colorbar')
    +
    +# Add colorbar and adjust ticks afterwards
    +cbar = fig.colorbar(cax, orientation='horizontal')
    +cbar.set_ticks(ticks=[-1, 0, 1], labels=['Low', 'Medium', 'High'])
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.colorbar.Colorbar.set_ticks`
    +#    - `matplotlib.figure.Figure.colorbar` / `matplotlib.pyplot.colorbar`
    diff --git a/galleries/examples/ticks/custom_ticker1.py b/galleries/examples/ticks/custom_ticker1.py
    new file mode 100644
    index 000000000000..2e6a86a8fc1c
    --- /dev/null
    +++ b/galleries/examples/ticks/custom_ticker1.py
    @@ -0,0 +1,35 @@
    +"""
    +=============
    +Custom Ticker
    +=============
    +
    +The :mod:`matplotlib.ticker` module defines many preset tickers, but was
    +primarily designed for extensibility, i.e., to support user customized ticking.
    +
    +In this example, a user defined function is used to format the ticks in
    +millions of dollars on the y-axis.
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +
    +def millions(x, pos):
    +    """The two arguments are the value and tick position."""
    +    return f'${x*1e-6:1.1f}M'
    +
    +
    +fig, ax = plt.subplots()
    +# set_major_formatter internally creates a FuncFormatter from the callable.
    +ax.yaxis.set_major_formatter(millions)
    +money = [1.5e5, 2.5e6, 5.5e6, 2.0e7]
    +ax.bar(['Bill', 'Fred', 'Mary', 'Sue'], money)
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    diff --git a/galleries/examples/ticks/date_concise_formatter.py b/galleries/examples/ticks/date_concise_formatter.py
    new file mode 100644
    index 000000000000..fcd1408ea80e
    --- /dev/null
    +++ b/galleries/examples/ticks/date_concise_formatter.py
    @@ -0,0 +1,183 @@
    +"""
    +.. _date_concise_formatter:
    +
    +============================================
    +Format date ticks using ConciseDateFormatter
    +============================================
    +
    +Finding good tick values and formatting the ticks for an axis that
    +has date data is often a challenge.  `~.dates.ConciseDateFormatter` is
    +meant to improve the strings chosen for the ticklabels, and to minimize
    +the strings used in those tick labels as much as possible.
    +
    +.. note::
    +
    +    This formatter is a candidate to become the default date tick formatter
    +    in future versions of Matplotlib.  Please report any issues or
    +    suggestions for improvement to the GitHub repository or mailing list.
    +
    +"""
    +import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.dates as mdates
    +
    +# %%
    +# First, the default formatter.
    +
    +base = datetime.datetime(2005, 2, 1)
    +dates = [base + datetime.timedelta(hours=(2 * i)) for i in range(732)]
    +N = len(dates)
    +np.random.seed(19680801)
    +y = np.cumsum(np.random.randn(N))
    +
    +fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))
    +lims = [(np.datetime64('2005-02'), np.datetime64('2005-04')),
    +        (np.datetime64('2005-02-03'), np.datetime64('2005-02-15')),
    +        (np.datetime64('2005-02-03 11:00'), np.datetime64('2005-02-04 13:20'))]
    +for nn, ax in enumerate(axs):
    +    ax.plot(dates, y)
    +    ax.set_xlim(lims[nn])
    +    ax.tick_params(axis='x', rotation=40, rotation_mode='xtick')
    +axs[0].set_title('Default Date Formatter')
    +plt.show()
    +
    +# %%
    +# The default date formatter is quite verbose, so we have the option of
    +# using `~.dates.ConciseDateFormatter`, as shown below.  Note that
    +# for this example the labels do not need to be rotated as they do for the
    +# default formatter because the labels are as small as possible.
    +
    +fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))
    +for nn, ax in enumerate(axs):
    +    locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
    +    formatter = mdates.ConciseDateFormatter(locator)
    +    ax.xaxis.set_major_locator(locator)
    +    ax.xaxis.set_major_formatter(formatter)
    +
    +    ax.plot(dates, y)
    +    ax.set_xlim(lims[nn])
    +axs[0].set_title('Concise Date Formatter')
    +
    +plt.show()
    +
    +# %%
    +# If all calls to axes that have dates are to be made using this converter,
    +# it is probably most convenient to use the units registry where you do
    +# imports:
    +
    +import matplotlib.units as munits
    +
    +converter = mdates.ConciseDateConverter()
    +munits.registry[np.datetime64] = converter
    +munits.registry[datetime.date] = converter
    +munits.registry[datetime.datetime] = converter
    +
    +fig, axs = plt.subplots(3, 1, figsize=(6, 6), layout='constrained')
    +for nn, ax in enumerate(axs):
    +    ax.plot(dates, y)
    +    ax.set_xlim(lims[nn])
    +axs[0].set_title('Concise Date Formatter')
    +
    +plt.show()
    +
    +# %%
    +# Localization of date formats
    +# ============================
    +#
    +# Dates formats can be localized if the default formats are not desirable by
    +# manipulating one of three lists of strings.
    +#
    +# The ``formatter.formats`` list of formats is for the normal tick labels,
    +# There are six levels: years, months, days, hours, minutes, seconds.
    +# The ``formatter.offset_formats`` is how the "offset" string on the right
    +# of the axis is formatted.  This is usually much more verbose than the tick
    +# labels. Finally, the ``formatter.zero_formats`` are the formats of the
    +# ticks that are "zeros".  These are tick values that are either the first of
    +# the year, month, or day of month, or the zeroth hour, minute, or second.
    +# These are usually the same as the format of
    +# the ticks a level above.  For example if the axis limits mean the ticks are
    +# mostly days, then we label 1 Mar 2005 simply with a "Mar".  If the axis
    +# limits are mostly hours, we label Feb 4 00:00 as simply "Feb-4".
    +#
    +# Note that these format lists can also be passed to `.ConciseDateFormatter`
    +# as optional keyword arguments.
    +#
    +# Here we modify the labels to be "day month year", instead of the ISO
    +# "year month day":
    +
    +fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))
    +
    +for nn, ax in enumerate(axs):
    +    locator = mdates.AutoDateLocator()
    +    formatter = mdates.ConciseDateFormatter(locator)
    +    formatter.formats = ['%y',  # ticks are mostly years
    +                         '%b',       # ticks are mostly months
    +                         '%d',       # ticks are mostly days
    +                         '%H:%M',    # hrs
    +                         '%H:%M',    # min
    +                         '%S.%f', ]  # secs
    +    # these are mostly just the level above...
    +    formatter.zero_formats = [''] + formatter.formats[:-1]
    +    # ...except for ticks that are mostly hours, then it is nice to have
    +    # month-day:
    +    formatter.zero_formats[3] = '%d-%b'
    +
    +    formatter.offset_formats = ['',
    +                                '%Y',
    +                                '%b %Y',
    +                                '%d %b %Y',
    +                                '%d %b %Y',
    +                                '%d %b %Y %H:%M', ]
    +    ax.xaxis.set_major_locator(locator)
    +    ax.xaxis.set_major_formatter(formatter)
    +
    +    ax.plot(dates, y)
    +    ax.set_xlim(lims[nn])
    +axs[0].set_title('Concise Date Formatter')
    +
    +plt.show()
    +
    +# %%
    +# Registering a converter with localization
    +# =========================================
    +#
    +# `.ConciseDateFormatter` doesn't have rcParams entries, but localization can
    +# be accomplished by passing keyword arguments to `.ConciseDateConverter` and
    +# registering the datatypes you will use with the units registry:
    +
    +import datetime
    +
    +formats = ['%y',          # ticks are mostly years
    +           '%b',     # ticks are mostly months
    +           '%d',     # ticks are mostly days
    +           '%H:%M',  # hrs
    +           '%H:%M',  # min
    +           '%S.%f', ]  # secs
    +# these can be the same, except offset by one level....
    +zero_formats = [''] + formats[:-1]
    +# ...except for ticks that are mostly hours, then it's nice to have month-day
    +zero_formats[3] = '%d-%b'
    +offset_formats = ['',
    +                  '%Y',
    +                  '%b %Y',
    +                  '%d %b %Y',
    +                  '%d %b %Y',
    +                  '%d %b %Y %H:%M', ]
    +
    +converter = mdates.ConciseDateConverter(
    +    formats=formats, zero_formats=zero_formats, offset_formats=offset_formats)
    +
    +munits.registry[np.datetime64] = converter
    +munits.registry[datetime.date] = converter
    +munits.registry[datetime.datetime] = converter
    +
    +fig, axs = plt.subplots(3, 1, layout='constrained', figsize=(6, 6))
    +for nn, ax in enumerate(axs):
    +    ax.plot(dates, y)
    +    ax.set_xlim(lims[nn])
    +axs[0].set_title('Concise Date Formatter registered non-default')
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/date_demo_convert.py b/galleries/examples/ticks/date_demo_convert.py
    new file mode 100644
    index 000000000000..c22edf54df9a
    --- /dev/null
    +++ b/galleries/examples/ticks/date_demo_convert.py
    @@ -0,0 +1,39 @@
    +"""
    +=================
    +Date Demo Convert
    +=================
    +
    +"""
    +import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.dates import DateFormatter, DayLocator, HourLocator, drange
    +
    +date1 = datetime.datetime(2000, 3, 2)
    +date2 = datetime.datetime(2000, 3, 6)
    +delta = datetime.timedelta(hours=6)
    +dates = drange(date1, date2, delta)
    +
    +y = np.arange(len(dates))
    +
    +fig, ax = plt.subplots()
    +ax.plot(dates, y**2, 'o')
    +
    +# this is superfluous, since the autoscaler should get it right, but
    +# use date2num and num2date to convert between dates and floats if
    +# you want; both date2num and num2date convert an instance or sequence
    +ax.set_xlim(dates[0], dates[-1])
    +
    +# The hour locator takes the hour or sequence of hours you want to
    +# tick, not the base multiple
    +
    +ax.xaxis.set_major_locator(DayLocator())
    +ax.xaxis.set_minor_locator(HourLocator(range(0, 25, 6)))
    +ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
    +
    +ax.fmt_xdata = DateFormatter('%Y-%m-%d %H:%M:%S')
    +fig.autofmt_xdate()
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/date_demo_rrule.py b/galleries/examples/ticks/date_demo_rrule.py
    new file mode 100644
    index 000000000000..948abde7584d
    --- /dev/null
    +++ b/galleries/examples/ticks/date_demo_rrule.py
    @@ -0,0 +1,44 @@
    +"""
    +=========================================
    +Placing date ticks using recurrence rules
    +=========================================
    +
    +The `iCalender RFC`_ specifies *recurrence rules* (rrules), that define
    +date sequences. You can use rrules in Matplotlib to place date ticks.
    +
    +This example sets custom date ticks on every 5th easter.
    +
    +See https://dateutil.readthedocs.io/en/stable/rrule.html for help with rrules.
    +
    +.. _iCalender RFC: https://tools.ietf.org/html/rfc5545
    +"""
    +import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.dates import YEARLY, DateFormatter, RRuleLocator, drange, rrulewrapper
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +
    +# tick every 5th easter
    +rule = rrulewrapper(YEARLY, byeaster=1, interval=5)
    +loc = RRuleLocator(rule)
    +formatter = DateFormatter('%m/%d/%y')
    +date1 = datetime.date(1952, 1, 1)
    +date2 = datetime.date(2004, 4, 12)
    +delta = datetime.timedelta(days=100)
    +
    +dates = drange(date1, date2, delta)
    +s = np.random.rand(len(dates))  # make up some random y values
    +
    +
    +fig, ax = plt.subplots()
    +plt.plot(dates, s, 'o')
    +ax.xaxis.set_major_locator(loc)
    +ax.xaxis.set_major_formatter(formatter)
    +ax.xaxis.set_tick_params(rotation=30, labelsize=10)
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/date_formatters_locators.py b/galleries/examples/ticks/date_formatters_locators.py
    new file mode 100644
    index 000000000000..8d4922931323
    --- /dev/null
    +++ b/galleries/examples/ticks/date_formatters_locators.py
    @@ -0,0 +1,102 @@
    +"""
    +.. _date_formatters_locators:
    +
    +=================================
    +Date tick locators and formatters
    +=================================
    +
    +This example illustrates the usage and effect of the various date locators and
    +formatters.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# While these appear unused directly, they are used from eval'd strings.
    +from matplotlib.dates import (FR, MO, MONTHLY, SA, SU, TH, TU, WE, AutoDateFormatter,
    +                              AutoDateLocator, ConciseDateFormatter, DateFormatter,
    +                              DayLocator, HourLocator, MicrosecondLocator,
    +                              MinuteLocator, MonthLocator, RRuleLocator, SecondLocator,
    +                              WeekdayLocator, YearLocator, rrulewrapper)
    +import matplotlib.ticker as ticker
    +
    +
    +def plot_axis(ax, locator=None, xmax='2002-02-01', fmt=None, formatter=None):
    +    """Set up common parameters for the Axes in the example."""
    +    ax.spines[['left', 'right', 'top']].set_visible(False)
    +    ax.yaxis.set_major_locator(ticker.NullLocator())
    +    ax.tick_params(which='major', width=1.00, length=5)
    +    ax.tick_params(which='minor', width=0.75, length=2.5)
    +    ax.set_xlim(np.datetime64('2000-02-01'), np.datetime64(xmax))
    +    if locator:
    +        ax.xaxis.set_major_locator(eval(locator))
    +        ax.xaxis.set_major_formatter(DateFormatter(fmt))
    +    else:
    +        ax.xaxis.set_major_formatter(eval(formatter))
    +    ax.text(0.0, 0.2, locator or formatter, transform=ax.transAxes,
    +            fontsize=14, fontname='Monospace', color='tab:blue')
    +
    +# %%
    +# :ref:`date-locators`
    +# --------------------
    +
    +
    +locators = [
    +    # locator as str, xmax, fmt
    +    ('AutoDateLocator(maxticks=8)', '2003-02-01', '%Y-%m'),
    +    ('YearLocator(month=4)', '2003-02-01', '%Y-%m'),
    +    ('MonthLocator(bymonth=[4, 8, 12])', '2003-02-01', '%Y-%m'),
    +    ('DayLocator(interval=180)', '2003-02-01', '%Y-%m-%d'),
    +    ('WeekdayLocator(byweekday=SU, interval=4)', '2000-07-01', '%a %Y-%m-%d'),
    +    ('HourLocator(byhour=range(0, 24, 6))', '2000-02-04', '%H h'),
    +    ('MinuteLocator(interval=15)', '2000-02-01 02:00', '%H:%M'),
    +    ('SecondLocator(bysecond=(0, 30))', '2000-02-01 00:02', '%H:%M:%S'),
    +    ('MicrosecondLocator(interval=1000)', '2000-02-01 00:00:00.005', '%S.%f'),
    +    ('RRuleLocator(rrulewrapper(freq=MONTHLY, \nbyweekday=(MO, TU, WE, TH, FR), '
    +     'bysetpos=-1))', '2000-07-01', '%Y-%m-%d'),
    +]
    +
    +fig, axs = plt.subplots(len(locators), 1, figsize=(8, len(locators) * .8),
    +                        layout='constrained')
    +fig.suptitle('Date Locators')
    +for ax, (locator, xmax, fmt) in zip(axs, locators):
    +    plot_axis(ax, locator, xmax, fmt)
    +
    +# %%
    +# :ref:`date-formatters`
    +# ----------------------
    +
    +formatters = [
    +    'AutoDateFormatter(ax.xaxis.get_major_locator())',
    +    'ConciseDateFormatter(ax.xaxis.get_major_locator())',
    +    'DateFormatter("%b %Y")',
    +]
    +
    +fig, axs = plt.subplots(len(formatters), 1, figsize=(8, len(formatters) * .8),
    +                        layout='constrained')
    +fig.suptitle('Date Formatters')
    +for ax, fmt in zip(axs, formatters):
    +    plot_axis(ax, formatter=fmt)
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.dates.AutoDateLocator`
    +#    - `matplotlib.dates.YearLocator`
    +#    - `matplotlib.dates.MonthLocator`
    +#    - `matplotlib.dates.DayLocator`
    +#    - `matplotlib.dates.WeekdayLocator`
    +#    - `matplotlib.dates.HourLocator`
    +#    - `matplotlib.dates.MinuteLocator`
    +#    - `matplotlib.dates.SecondLocator`
    +#    - `matplotlib.dates.MicrosecondLocator`
    +#    - `matplotlib.dates.RRuleLocator`
    +#    - `matplotlib.dates.rrulewrapper`
    +#    - `matplotlib.dates.DateFormatter`
    +#    - `matplotlib.dates.AutoDateFormatter`
    +#    - `matplotlib.dates.ConciseDateFormatter`
    diff --git a/galleries/examples/ticks/date_index_formatter.py b/galleries/examples/ticks/date_index_formatter.py
    new file mode 100644
    index 000000000000..c5bc6abaf17f
    --- /dev/null
    +++ b/galleries/examples/ticks/date_index_formatter.py
    @@ -0,0 +1,84 @@
    +"""
    +=====================================
    +Custom tick formatter for time series
    +=====================================
    +
    +.. redirect-from:: /gallery/text_labels_and_annotations/date_index_formatter
    +.. redirect-from:: /gallery/ticks/date_index_formatter2
    +
    +When plotting daily data, e.g., financial time series, one often wants
    +to leave out days on which there is no data, for instance weekends, so that
    +the data are plotted at regular intervals without extra spaces for the days
    +with no data.
    +The example shows how to use an 'index formatter' to achieve the desired plot.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.cbook as cbook
    +from matplotlib.dates import DateFormatter, DayLocator
    +import matplotlib.lines as ml
    +from matplotlib.ticker import Formatter
    +
    +# Load a structured numpy array from yahoo csv data with fields date, open, high,
    +# low, close, volume, adj_close from the mpl-data/sample_data directory. The
    +# record array stores the date as an np.datetime64 with a day unit ('D') in
    +# the date column (``r['date']``).
    +r = cbook.get_sample_data('goog.npz')['price_data']
    +r = r[:9]  # get the first 9 days
    +
    +fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6, 6), layout='constrained')
    +fig.get_layout_engine().set(hspace=0.15)
    +
    +# First we'll do it the default way, with gaps on weekends
    +ax1.plot(r["date"], r["adj_close"], 'o-')
    +
    +# Highlight gaps in daily data
    +gaps = np.flatnonzero(np.diff(r["date"]) > np.timedelta64(1, 'D'))
    +for gap in r[['date', 'adj_close']][np.stack((gaps, gaps + 1)).T]:
    +    ax1.plot(gap['date'], gap['adj_close'], 'w--', lw=2)
    +ax1.legend(handles=[ml.Line2D([], [], ls='--', label='Gaps in daily data')])
    +
    +ax1.set_title("Plot y at x Coordinates")
    +ax1.xaxis.set_major_locator(DayLocator())
    +ax1.xaxis.set_major_formatter(DateFormatter('%a'))
    +
    +
    +# Next we'll write a custom index formatter. Below we will plot
    +# the data against an index that goes from 0, 1,  ... len(data).  Instead of
    +# formatting the tick marks as integers, we format as times.
    +def format_date(x, _):
    +    try:
    +        # convert datetime64 to datetime, and use datetime's strftime:
    +        return r["date"][round(x)].item().strftime('%a')
    +    except IndexError:
    +        pass
    +
    +# Create an index plot (x defaults to range(len(y)) if omitted)
    +ax2.plot(r["adj_close"], 'o-')
    +
    +ax2.set_title("Plot y at Index Coordinates Using Custom Formatter")
    +ax2.xaxis.set_major_formatter(format_date)  # internally creates FuncFormatter
    +
    +# %%
    +# Instead of passing a function into `.Axis.set_major_formatter` you can use
    +# any other callable, e.g. an instance of a class that implements __call__:
    +
    +
    +class MyFormatter(Formatter):
    +    def __init__(self, dates, fmt='%a'):
    +        self.dates = dates
    +        self.fmt = fmt
    +
    +    def __call__(self, x, pos=0):
    +        """Return the label for time x at position pos."""
    +        try:
    +            return self.dates[round(x)].item().strftime(self.fmt)
    +        except IndexError:
    +            pass
    +
    +
    +ax2.xaxis.set_major_formatter(MyFormatter(r["date"], '%a'))
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/date_precision_and_epochs.py b/galleries/examples/ticks/date_precision_and_epochs.py
    new file mode 100644
    index 000000000000..eb4926cab68d
    --- /dev/null
    +++ b/galleries/examples/ticks/date_precision_and_epochs.py
    @@ -0,0 +1,158 @@
    +"""
    +=========================
    +Date precision and epochs
    +=========================
    +
    +Matplotlib can handle `.datetime` objects and `numpy.datetime64` objects using
    +a unit converter that recognizes these dates and converts them to floating
    +point numbers.
    +
    +Before Matplotlib 3.3, the default for this conversion returns a float that was
    +days since "0000-12-31T00:00:00".  As of Matplotlib 3.3, the default is
    +days from "1970-01-01T00:00:00".  This allows more resolution for modern
    +dates.  "2020-01-01" with the old epoch converted to 730120, and a 64-bit
    +floating point number has a resolution of 2^{-52}, or approximately
    +14 microseconds, so microsecond precision was lost.  With the new default
    +epoch "2020-01-01" is 10957.0, so the achievable resolution is 0.21
    +microseconds.
    +
    +"""
    +import datetime
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.dates as mdates
    +
    +
    +def _reset_epoch_for_tutorial():
    +    """
    +    Users (and downstream libraries) should not use the private method of
    +    resetting the epoch.
    +    """
    +    mdates._reset_epoch_test_example()
    +
    +
    +# %%
    +# Datetime
    +# --------
    +#
    +# Python `.datetime` objects have microsecond resolution, so with the
    +# old default matplotlib dates could not round-trip full-resolution datetime
    +# objects.
    +
    +old_epoch = '0000-12-31T00:00:00'
    +new_epoch = '1970-01-01T00:00:00'
    +
    +_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    +mdates.set_epoch(old_epoch)  # old epoch (pre MPL 3.3)
    +
    +date1 = datetime.datetime(2000, 1, 1, 0, 10, 0, 12,
    +                          tzinfo=datetime.timezone.utc)
    +mdate1 = mdates.date2num(date1)
    +print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
    +date2 = mdates.num2date(mdate1)
    +print('After Roundtrip:  ', date2)
    +
    +# %%
    +# Note this is only a round-off error, and there is no problem for
    +# dates closer to the old epoch:
    +
    +date1 = datetime.datetime(10, 1, 1, 0, 10, 0, 12,
    +                          tzinfo=datetime.timezone.utc)
    +mdate1 = mdates.date2num(date1)
    +print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
    +date2 = mdates.num2date(mdate1)
    +print('After Roundtrip:  ', date2)
    +
    +# %%
    +# If a user wants to use modern dates at microsecond precision, they
    +# can change the epoch using `.set_epoch`.  However, the epoch has to be
    +# set before any date operations to prevent confusion between different
    +# epochs. Trying to change the epoch later will raise a `RuntimeError`.
    +
    +try:
    +    mdates.set_epoch(new_epoch)  # this is the new MPL 3.3 default.
    +except RuntimeError as e:
    +    print('RuntimeError:', str(e))
    +
    +# %%
    +# For this tutorial, we reset the sentinel using a private method, but users
    +# should just set the epoch once, if at all.
    +
    +_reset_epoch_for_tutorial()  # Just being done for this tutorial.
    +mdates.set_epoch(new_epoch)
    +
    +date1 = datetime.datetime(2020, 1, 1, 0, 10, 0, 12,
    +                          tzinfo=datetime.timezone.utc)
    +mdate1 = mdates.date2num(date1)
    +print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
    +date2 = mdates.num2date(mdate1)
    +print('After Roundtrip:  ', date2)
    +
    +# %%
    +# datetime64
    +# ----------
    +#
    +# `numpy.datetime64` objects have microsecond precision for a much larger
    +# timespace than `.datetime` objects.  However, currently Matplotlib time is
    +# only converted back to datetime objects, which have microsecond resolution,
    +# and years that only span 0000 to 9999.
    +
    +_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    +mdates.set_epoch(new_epoch)
    +
    +date1 = np.datetime64('2000-01-01T00:10:00.000012')
    +mdate1 = mdates.date2num(date1)
    +print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
    +date2 = mdates.num2date(mdate1)
    +print('After Roundtrip:  ', date2)
    +
    +# %%
    +# Plotting
    +# --------
    +#
    +# This all of course has an effect on plotting.  With the old default epoch
    +# the times were rounded during the internal ``date2num`` conversion, leading
    +# to jumps in the data:
    +
    +_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    +mdates.set_epoch(old_epoch)
    +
    +x = np.arange('2000-01-01T00:00:00.0', '2000-01-01T00:00:00.000100',
    +              dtype='datetime64[us]')
    +# simulate the plot being made using the old epoch
    +xold = np.array([mdates.num2date(mdates.date2num(d)) for d in x])
    +y = np.arange(0, len(x))
    +
    +# resetting the Epoch so plots are comparable
    +_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    +mdates.set_epoch(new_epoch)
    +
    +fig, ax = plt.subplots(layout='constrained')
    +ax.plot(xold, y)
    +ax.set_title('Epoch: ' + mdates.get_epoch())
    +ax.xaxis.set_tick_params(rotation=40)
    +plt.show()
    +
    +# %%
    +# For dates plotted using the more recent epoch, the plot is smooth:
    +
    +fig, ax = plt.subplots(layout='constrained')
    +ax.plot(x, y)
    +ax.set_title('Epoch: ' + mdates.get_epoch())
    +ax.xaxis.set_tick_params(rotation=40)
    +plt.show()
    +
    +_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.dates.num2date`
    +#    - `matplotlib.dates.date2num`
    +#    - `matplotlib.dates.set_epoch`
    diff --git a/galleries/examples/ticks/dollar_ticks.py b/galleries/examples/ticks/dollar_ticks.py
    new file mode 100644
    index 000000000000..7abf967c053b
    --- /dev/null
    +++ b/galleries/examples/ticks/dollar_ticks.py
    @@ -0,0 +1,40 @@
    +"""
    +============
    +Dollar ticks
    +============
    +
    +Use a format string to prepend dollar signs on y-axis labels.
    +
    +.. redirect-from:: /gallery/pyplots/dollar_ticks
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +fig, ax = plt.subplots()
    +ax.plot(100*np.random.rand(20))
    +
    +# Use automatic StrMethodFormatter
    +ax.yaxis.set_major_formatter('${x:1.2f}')
    +
    +ax.yaxis.set_tick_params(which='major', labelcolor='green',
    +                         labelleft=False, labelright=True)
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    +#    - `matplotlib.axis.Axis.set_tick_params`
    +#    - `matplotlib.axis.Tick`
    +#    - `matplotlib.ticker.StrMethodFormatter`
    diff --git a/galleries/examples/ticks/engformatter_offset.py b/galleries/examples/ticks/engformatter_offset.py
    new file mode 100644
    index 000000000000..7da2d45a7942
    --- /dev/null
    +++ b/galleries/examples/ticks/engformatter_offset.py
    @@ -0,0 +1,33 @@
    +"""
    +===================================================
    +SI prefixed offsets and natural order of magnitudes
    +===================================================
    +
    +`matplotlib.ticker.EngFormatter` is capable of computing a natural
    +offset for your axis data, and presenting it with a standard SI prefix
    +automatically calculated.
    +
    +Below is an examples of such a plot:
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as mticker
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +UNIT = "Hz"
    +
    +fig, ax = plt.subplots()
    +ax.yaxis.set_major_formatter(mticker.EngFormatter(
    +    useOffset=True,
    +    unit=UNIT
    +))
    +size = 100
    +measurement = np.full(size, 1e9)
    +noise = np.random.uniform(low=-2e3, high=2e3, size=size)
    +ax.plot(measurement + noise)
    +plt.show()
    diff --git a/galleries/examples/ticks/fig_axes_customize_simple.py b/galleries/examples/ticks/fig_axes_customize_simple.py
    new file mode 100644
    index 000000000000..07a569e3d31d
    --- /dev/null
    +++ b/galleries/examples/ticks/fig_axes_customize_simple.py
    @@ -0,0 +1,32 @@
    +"""
    +=========================
    +Fig Axes Customize Simple
    +=========================
    +
    +Customize the background, labels and ticks of a simple plot.
    +
    +.. redirect-from:: /gallery/pyplots/fig_axes_customize_simple
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig = plt.figure()
    +fig.patch.set_facecolor('lightgoldenrodyellow')
    +
    +ax1 = fig.add_axes((0.1, 0.3, 0.4, 0.4))
    +ax1.patch.set_facecolor('lightslategray')
    +
    +ax1.tick_params(axis='x', labelcolor='tab:red', labelrotation=45, labelsize=16)
    +ax1.tick_params(axis='y', color='tab:green', size=25, width=3)
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tick_params`
    +#    - `matplotlib.patches.Patch.set_facecolor`
    diff --git a/galleries/examples/ticks/major_minor_demo.py b/galleries/examples/ticks/major_minor_demo.py
    new file mode 100644
    index 000000000000..9fd93118a9ac
    --- /dev/null
    +++ b/galleries/examples/ticks/major_minor_demo.py
    @@ -0,0 +1,96 @@
    +r"""
    +=====================
    +Major and minor ticks
    +=====================
    +
    +Demonstrate how to use major and minor tickers.
    +
    +The two relevant classes are `.Locator`\s and `.Formatter`\s.  Locators
    +determine where the ticks are, and formatters control the formatting of tick
    +labels.
    +
    +Minor ticks are off by default (using `.NullLocator` and `.NullFormatter`).
    +Minor ticks can be turned on without labels by setting the minor locator.
    +Minor tick labels can be turned on by setting the minor formatter.
    +
    +`.MultipleLocator` places ticks on multiples of some base.
    +`.StrMethodFormatter` uses a format string (e.g., ``'{x:d}'`` or ``'{x:1.2f}'``
    +or ``'{x:1.1f} cm'``) to format the tick labels (the variable in the format
    +string must be ``'x'``).  For a `.StrMethodFormatter`, the string can be passed
    +directly to `.Axis.set_major_formatter` or
    +`.Axis.set_minor_formatter`.  An appropriate `.StrMethodFormatter` will
    +be created and used automatically.
    +
    +`.pyplot.grid` changes the grid settings of the major ticks of the x- and
    +y-axis together.  If you want to control the grid of the minor ticks for a
    +given axis, use for example ::
    +
    +  ax.xaxis.grid(True, which='minor')
    +
    +Note that a given locator or formatter instance can only be used on a single
    +axis (because the locator stores references to the axis data and view limits).
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +from matplotlib.ticker import AutoMinorLocator, MultipleLocator
    +
    +t = np.arange(0.0, 100.0, 0.1)
    +s = np.sin(0.1 * np.pi * t) * np.exp(-t * 0.01)
    +
    +fig, ax = plt.subplots()
    +ax.plot(t, s)
    +
    +# Make a plot with major ticks that are multiples of 20 and minor ticks that
    +# are multiples of 5.  Label major ticks with '.0f' formatting but don't label
    +# minor ticks.  The string is used directly, the `StrMethodFormatter` is
    +# created automatically.
    +ax.xaxis.set_major_locator(MultipleLocator(20))
    +ax.xaxis.set_major_formatter('{x:.0f}')
    +
    +# For the minor ticks, use no labels; default NullFormatter.
    +ax.xaxis.set_minor_locator(MultipleLocator(5))
    +
    +plt.show()
    +
    +# %%
    +# Automatic tick selection for major and minor ticks.
    +#
    +# Use interactive pan and zoom to see how the tick intervals change. There will
    +# be either 4 or 5 minor tick intervals per major interval, depending on the
    +# major interval.
    +#
    +# One can supply an argument to `.AutoMinorLocator` to specify a fixed number
    +# of minor intervals per major interval, e.g. ``AutoMinorLocator(2)`` would
    +# lead to a single minor tick between major ticks.
    +
    +t = np.arange(0.0, 100.0, 0.01)
    +s = np.sin(2 * np.pi * t) * np.exp(-t * 0.01)
    +
    +fig, ax = plt.subplots()
    +ax.plot(t, s)
    +
    +ax.xaxis.set_minor_locator(AutoMinorLocator())
    +
    +ax.tick_params(which='both', width=2)
    +ax.tick_params(which='major', length=7)
    +ax.tick_params(which='minor', length=4, color='r')
    +
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.axis.Axis.set_minor_locator`
    +#    - `matplotlib.ticker.AutoMinorLocator`
    +#    - `matplotlib.ticker.MultipleLocator`
    +#    - `matplotlib.ticker.StrMethodFormatter`
    diff --git a/galleries/examples/ticks/multilevel_ticks.py b/galleries/examples/ticks/multilevel_ticks.py
    new file mode 100644
    index 000000000000..5b8d0ada5ae2
    --- /dev/null
    +++ b/galleries/examples/ticks/multilevel_ticks.py
    @@ -0,0 +1,99 @@
    +"""
    +=========================
    +Multilevel (nested) ticks
    +=========================
    +
    +Sometimes we want another level of tick labels on an axis, perhaps to indicate
    +a grouping of the ticks.
    +
    +Matplotlib does not provide an automated way to do this, but it is relatively
    +straightforward to annotate below the main axis.
    +
    +These examples use `.Axes.secondary_xaxis`, which is one approach. It has the
    +advantage that we can use Matplotlib Locators and Formatters on the axis that
    +does the grouping if we want.
    +
    +This first example creates a secondary xaxis and manually adds the ticks and
    +labels using `.Axes.set_xticks`.  Note that the tick labels have a newline
    +(e.g. ``"\nOughts"``) at the beginning of them to put the second-level tick
    +labels below the main tick labels.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.dates as mdates
    +
    +rng = np.random.default_rng(19680801)
    +
    +fig, ax = plt.subplots(layout='constrained', figsize=(4, 4))
    +
    +ax.plot(np.arange(30))
    +
    +sec = ax.secondary_xaxis(location=0)
    +sec.set_xticks([5, 15, 25], labels=['\nOughts', '\nTeens', '\nTwenties'])
    +
    +# %%
    +# This second example adds a second level of annotation to a categorical axis.
    +# Here we need to note that each animal (category) is assigned an integer, so
    +# ``cats`` is at x=0, ``dogs`` at x=1 etc.  Then we place the ticks on the
    +# second level on an x that is at the middle of the animal class we are trying
    +# to delineate.
    +#
    +# This example also adds tick marks between the classes by adding a second
    +# secondary xaxis, and placing long, wide ticks at the boundaries between the
    +# animal classes.
    +
    +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4))
    +
    +ax.plot(['cats', 'dogs', 'pigs', 'snakes', 'lizards', 'chickens',
    +         'eagles', 'herons', 'buzzards'],
    +        rng.normal(size=9), 'o')
    +
    +# label the classes:
    +sec = ax.secondary_xaxis(location=0)
    +sec.set_xticks([1, 3.5, 6.5], labels=['\n\nMammals', '\n\nReptiles', '\n\nBirds'])
    +sec.tick_params('x', length=0)
    +
    +# lines between the classes:
    +sec2 = ax.secondary_xaxis(location=0)
    +sec2.set_xticks([-0.5, 2.5, 4.5, 8.5], labels=[])
    +sec2.tick_params('x', length=40, width=1.5)
    +ax.set_xlim(-0.6, 8.6)
    +
    +# %%
    +# Dates are another common place where we may want to have a second level of
    +# tick labels.  In this last example, we take advantage of the ability to add
    +# an automatic locator and formatter to the secondary xaxis, which means we do
    +# not need to set the ticks manually.
    +#
    +# This example also differs from the above, in that we placed it at a location
    +# below the main axes ``location=-0.075`` and then we hide the spine by setting
    +# the line width to zero.  That means that our formatter no longer needs the
    +# carriage returns of the previous two examples.
    +
    +fig, ax = plt.subplots(layout='constrained', figsize=(7, 4))
    +
    +time = np.arange(np.datetime64('2020-01-01'), np.datetime64('2020-03-31'),
    +                 np.timedelta64(1, 'D'))
    +
    +ax.plot(time, rng.random(size=len(time)))
    +
    +# just format the days:
    +ax.xaxis.set_major_formatter(mdates.DateFormatter('%d'))
    +
    +# label the months:
    +sec = ax.secondary_xaxis(location=-0.075)
    +sec.xaxis.set_major_locator(mdates.MonthLocator(bymonthday=1))
    +
    +# note the extra spaces in the label to align the month label inside the month.
    +# Note that this could have been done by changing ``bymonthday`` above as well:
    +sec.xaxis.set_major_formatter(mdates.DateFormatter('  %b'))
    +sec.tick_params('x', length=0)
    +sec.spines['bottom'].set_linewidth(0)
    +
    +# label the xaxis, but note for this to look good, it needs to be on the
    +# secondary xaxis.
    +sec.set_xlabel('Dates (2020)')
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/scalarformatter.py b/galleries/examples/ticks/scalarformatter.py
    new file mode 100644
    index 000000000000..eaab5e9a86f2
    --- /dev/null
    +++ b/galleries/examples/ticks/scalarformatter.py
    @@ -0,0 +1,38 @@
    +"""
    +==========================
    +The default tick formatter
    +==========================
    +
    +By default, tick labels are formatted using a `.ScalarFormatter`, which can be
    +configured via `~.axes.Axes.ticklabel_format`.  This example illustrates some
    +possible configurations:
    +
    +- Default.
    +- ``useMathText=True``: Fancy formatting of mathematical expressions.
    +- ``useOffset=False``: Do not use offset notation; see
    +  `.ScalarFormatter.set_useOffset`.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = np.arange(0, 1, .01)
    +fig, axs = plt.subplots(
    +    3, 3, figsize=(9, 9), layout="constrained", gridspec_kw={"hspace": 0.1})
    +
    +for col in axs.T:
    +    col[0].plot(x * 1e5 + 1e10, x * 1e-10 + 1e-5)
    +    col[1].plot(x * 1e5, x * 1e-4)
    +    col[2].plot(-x * 1e5 - 1e10, -x * 1e-5 - 1e-10)
    +
    +for ax in axs[:, 1]:
    +    ax.ticklabel_format(useMathText=True)
    +for ax in axs[:, 2]:
    +    ax.ticklabel_format(useOffset=False)
    +
    +plt.rcParams.update({"axes.titleweight": "bold", "axes.titley": 1.1})
    +axs[0, 0].set_title("default settings")
    +axs[0, 1].set_title("useMathText=True")
    +axs[0, 2].set_title("useOffset=False")
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/tick-formatters.py b/galleries/examples/ticks/tick-formatters.py
    new file mode 100644
    index 000000000000..543aec57e4d2
    --- /dev/null
    +++ b/galleries/examples/ticks/tick-formatters.py
    @@ -0,0 +1,105 @@
    +"""
    +===============
    +Tick formatters
    +===============
    +
    +Tick formatters define how the numeric value associated with a tick on an axis
    +is formatted as a string.
    +
    +This example illustrates the usage and effect of the most common formatters.
    +
    +The tick format is configured via the function `~.Axis.set_major_formatter`
    +or `~.Axis.set_minor_formatter`. It accepts:
    +
    +- a format string, which implicitly creates a `.StrMethodFormatter`.
    +- a function,  implicitly creates a `.FuncFormatter`.
    +- an instance of a `.Formatter` subclass. The most common are
    +
    +  - `.NullFormatter`: No labels on the ticks.
    +  - `.StrMethodFormatter`: Use string `str.format` method.
    +  - `.FormatStrFormatter`: Use %-style formatting.
    +  - `.FuncFormatter`: Define labels through a function.
    +  - `.FixedFormatter`: Set the label strings explicitly.
    +  - `.ScalarFormatter`: Default formatter for scalars: auto-pick the format string.
    +  - `.PercentFormatter`: Format labels as a percentage.
    +
    +  See :ref:`formatters` for a complete list.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib import ticker
    +
    +
    +def setup(ax, title):
    +    """Set up common parameters for the Axes in the example."""
    +    # only show the bottom spine
    +    ax.yaxis.set_major_locator(ticker.NullLocator())
    +    ax.spines[['left', 'right', 'top']].set_visible(False)
    +
    +    # define tick positions
    +    ax.xaxis.set_major_locator(ticker.MultipleLocator(1.00))
    +    ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.25))
    +
    +    ax.xaxis.set_ticks_position('bottom')
    +    ax.tick_params(which='major', width=1.00, length=5)
    +    ax.tick_params(which='minor', width=0.75, length=2.5, labelsize=10)
    +    ax.set_xlim(0, 5)
    +    ax.set_ylim(0, 1)
    +    ax.text(0.0, 0.2, title, transform=ax.transAxes,
    +            fontsize=14, fontname='Monospace', color='tab:blue')
    +
    +
    +fig = plt.figure(figsize=(8, 8), layout='constrained')
    +fig0, fig1, fig2 = fig.subfigures(3, height_ratios=[1.5, 1.5, 7.5])
    +
    +fig0.suptitle('String Formatting', fontsize=16, x=0, ha='left')
    +ax0 = fig0.subplots()
    +
    +setup(ax0, title="'{x} km'")
    +ax0.xaxis.set_major_formatter('{x} km')
    +
    +
    +fig1.suptitle('Function Formatting', fontsize=16, x=0, ha='left')
    +ax1 = fig1.subplots()
    +
    +setup(ax1, title="def(x, pos): return str(x-5)")
    +ax1.xaxis.set_major_formatter(lambda x, pos: str(x-5))
    +
    +
    +fig2.suptitle('Formatter Object Formatting', fontsize=16, x=0, ha='left')
    +axs2 = fig2.subplots(7, 1)
    +
    +setup(axs2[0], title="NullFormatter()")
    +axs2[0].xaxis.set_major_formatter(ticker.NullFormatter())
    +
    +setup(axs2[1], title="StrMethodFormatter('{x:.3f}')")
    +axs2[1].xaxis.set_major_formatter(ticker.StrMethodFormatter("{x:.3f}"))
    +
    +setup(axs2[2], title="FormatStrFormatter('#%d')")
    +axs2[2].xaxis.set_major_formatter(ticker.FormatStrFormatter("#%d"))
    +
    +
    +def fmt_two_digits(x, pos):
    +    return f'[{x:.2f}]'
    +
    +
    +setup(axs2[3], title='FuncFormatter("[{:.2f}]".format)')
    +axs2[3].xaxis.set_major_formatter(ticker.FuncFormatter(fmt_two_digits))
    +
    +setup(axs2[4], title="FixedFormatter(['A', 'B', 'C', 'D', 'E', 'F'])")
    +# FixedFormatter should only be used together with FixedLocator.
    +# Otherwise, one cannot be sure where the labels will end up.
    +positions = [0, 1, 2, 3, 4, 5]
    +labels = ['A', 'B', 'C', 'D', 'E', 'F']
    +axs2[4].xaxis.set_major_locator(ticker.FixedLocator(positions))
    +axs2[4].xaxis.set_major_formatter(ticker.FixedFormatter(labels))
    +
    +setup(axs2[5], title="ScalarFormatter()")
    +axs2[5].xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
    +
    +setup(axs2[6], title="PercentFormatter(xmax=5)")
    +axs2[6].xaxis.set_major_formatter(ticker.PercentFormatter(xmax=5))
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/tick-locators.py b/galleries/examples/ticks/tick-locators.py
    new file mode 100644
    index 000000000000..6cf4afaf22d7
    --- /dev/null
    +++ b/galleries/examples/ticks/tick-locators.py
    @@ -0,0 +1,93 @@
    +"""
    +=============
    +Tick locators
    +=============
    +
    +Tick locators define the position of the ticks.
    +
    +This example illustrates the usage and effect of the most common locators.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as ticker
    +
    +
    +def setup(ax, title):
    +    """Set up common parameters for the Axes in the example."""
    +    # only show the bottom spine
    +    ax.yaxis.set_major_locator(ticker.NullLocator())
    +    ax.spines[['left', 'right', 'top']].set_visible(False)
    +
    +    ax.xaxis.set_ticks_position('bottom')
    +    ax.tick_params(which='major', width=1.00, length=5)
    +    ax.tick_params(which='minor', width=0.75, length=2.5)
    +    ax.set_xlim(0, 5)
    +    ax.set_ylim(0, 1)
    +    ax.text(0.0, 0.2, title, transform=ax.transAxes,
    +            fontsize=14, fontname='Monospace', color='tab:blue')
    +
    +
    +fig, axs = plt.subplots(8, 1, figsize=(8, 6))
    +
    +# Null Locator
    +setup(axs[0], title="NullLocator()")
    +axs[0].xaxis.set_major_locator(ticker.NullLocator())
    +axs[0].xaxis.set_minor_locator(ticker.NullLocator())
    +
    +# Multiple Locator
    +setup(axs[1], title="MultipleLocator(0.5, offset=0.2)")
    +axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5, offset=0.2))
    +axs[1].xaxis.set_minor_locator(ticker.MultipleLocator(0.1))
    +
    +# Fixed Locator
    +setup(axs[2], title="FixedLocator([0, 1, 5])")
    +axs[2].xaxis.set_major_locator(ticker.FixedLocator([0, 1, 5]))
    +axs[2].xaxis.set_minor_locator(ticker.FixedLocator(np.linspace(0.2, 0.8, 4)))
    +
    +# Linear Locator
    +setup(axs[3], title="LinearLocator(numticks=3)")
    +axs[3].xaxis.set_major_locator(ticker.LinearLocator(3))
    +axs[3].xaxis.set_minor_locator(ticker.LinearLocator(31))
    +
    +# Index Locator
    +setup(axs[4], title="IndexLocator(base=0.5, offset=0.25)")
    +axs[4].plot([0]*5, color='white')
    +axs[4].xaxis.set_major_locator(ticker.IndexLocator(base=0.5, offset=0.25))
    +
    +# Auto Locator
    +setup(axs[5], title="AutoLocator()")
    +axs[5].xaxis.set_major_locator(ticker.AutoLocator())
    +axs[5].xaxis.set_minor_locator(ticker.AutoMinorLocator())
    +
    +# MaxN Locator
    +setup(axs[6], title="MaxNLocator(n=4)")
    +axs[6].xaxis.set_major_locator(ticker.MaxNLocator(4))
    +axs[6].xaxis.set_minor_locator(ticker.MaxNLocator(40))
    +
    +# Log Locator
    +setup(axs[7], title="LogLocator(base=10, numticks=15)")
    +axs[7].set_xlim(10**3, 10**10)
    +axs[7].set_xscale('log')
    +axs[7].xaxis.set_major_locator(ticker.LogLocator(base=10, numticks=15))
    +
    +plt.tight_layout()
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The following functions, methods, classes and modules are used in this example:
    +#
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.axis.Axis.set_minor_locator`
    +#    - `matplotlib.ticker.NullLocator`
    +#    - `matplotlib.ticker.MultipleLocator`
    +#    - `matplotlib.ticker.FixedLocator`
    +#    - `matplotlib.ticker.LinearLocator`
    +#    - `matplotlib.ticker.IndexLocator`
    +#    - `matplotlib.ticker.AutoLocator`
    +#    - `matplotlib.ticker.MaxNLocator`
    +#    - `matplotlib.ticker.LogLocator`
    diff --git a/galleries/examples/ticks/tick_label_right.py b/galleries/examples/ticks/tick_label_right.py
    new file mode 100644
    index 000000000000..79eccd8777e7
    --- /dev/null
    +++ b/galleries/examples/ticks/tick_label_right.py
    @@ -0,0 +1,27 @@
    +"""
    +============================================
    +Set default y-axis tick labels on the right
    +============================================
    +
    +We can use :rc:`ytick.labelright`, :rc:`ytick.right`, :rc:`ytick.labelleft`,
    +and :rc:`ytick.left` to control where on the axes ticks and their labels
    +appear. These properties can also be set in ``.matplotlib/matplotlibrc``.
    +
    +"""
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +plt.rcParams['ytick.right'] = plt.rcParams['ytick.labelright'] = True
    +plt.rcParams['ytick.left'] = plt.rcParams['ytick.labelleft'] = False
    +
    +x = np.arange(10)
    +
    +fig, (ax0, ax1) = plt.subplots(2, 1, sharex=True, figsize=(6, 6))
    +
    +ax0.plot(x)
    +ax0.yaxis.tick_left()
    +
    +# use default parameter in rcParams, not calling tick_right()
    +ax1.plot(x)
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/tick_labels_from_values.py b/galleries/examples/ticks/tick_labels_from_values.py
    new file mode 100644
    index 000000000000..0d40597e6261
    --- /dev/null
    +++ b/galleries/examples/ticks/tick_labels_from_values.py
    @@ -0,0 +1,53 @@
    +"""
    +=========================================
    +Setting tick labels from a list of values
    +=========================================
    +
    +Using `.Axes.set_xticks` causes the tick labels to be set on the currently
    +chosen ticks. However, you may want to allow matplotlib to dynamically
    +choose the number of ticks and their spacing.
    +
    +In this case it may be better to determine the tick label from the
    +value at the tick. The following example shows how to do this.
    +
    +NB: The `.ticker.MaxNLocator` is used here to ensure that the tick values
    +take integer values.
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +from matplotlib.ticker import MaxNLocator
    +
    +fig, ax = plt.subplots()
    +xs = range(26)
    +ys = range(26)
    +labels = list('abcdefghijklmnopqrstuvwxyz')
    +
    +
    +def format_fn(tick_val, tick_pos):
    +    if int(tick_val) in xs:
    +        return labels[int(tick_val)]
    +    else:
    +        return ''
    +
    +
    +# A FuncFormatter is created automatically.
    +ax.xaxis.set_major_formatter(format_fn)
    +ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    +ax.plot(xs, ys)
    +plt.show()
    +
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.pyplot.subplots`
    +#    - `matplotlib.axis.Axis.set_major_formatter`
    +#    - `matplotlib.axis.Axis.set_major_locator`
    +#    - `matplotlib.ticker.FuncFormatter`
    +#    - `matplotlib.ticker.MaxNLocator`
    diff --git a/galleries/examples/ticks/tick_xlabel_top.py b/galleries/examples/ticks/tick_xlabel_top.py
    new file mode 100644
    index 000000000000..de2ca569851a
    --- /dev/null
    +++ b/galleries/examples/ticks/tick_xlabel_top.py
    @@ -0,0 +1,32 @@
    +"""
    +==================================
    +Move x-axis tick labels to the top
    +==================================
    +
    +`~.axes.Axes.tick_params` can be used to configure the ticks. *top* and
    +*labeltop* control the visibility tick lines and labels at the top x-axis.
    +To move x-axis ticks from bottom to top, we have to activate the top ticks
    +and deactivate the bottom ticks::
    +
    +    ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False)
    +
    +.. note::
    +
    +    If the change should be made for all future plots and not only the current
    +    Axes, you can adapt the respective config parameters
    +
    +    - :rc:`xtick.top`
    +    - :rc:`xtick.labeltop`
    +    - :rc:`xtick.bottom`
    +    - :rc:`xtick.labelbottom`
    +
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +ax.plot(range(10))
    +ax.tick_params(top=True, labeltop=True, bottom=False, labelbottom=False)
    +ax.set_title('x-ticks moved to the top')
    +
    +plt.show()
    diff --git a/galleries/examples/ticks/ticklabels_rotation.py b/galleries/examples/ticks/ticklabels_rotation.py
    new file mode 100644
    index 000000000000..d337ca827cde
    --- /dev/null
    +++ b/galleries/examples/ticks/ticklabels_rotation.py
    @@ -0,0 +1,33 @@
    +"""
    +===================
    +Rotated tick labels
    +===================
    +"""
    +
    +import matplotlib.pyplot as plt
    +
    +x = [1, 2, 3, 4]
    +y = [1, 4, 9, 6]
    +labels = ['Frogs', 'Hogs', 'Bogs', 'Slogs']
    +
    +fig, ax = plt.subplots()
    +ax.plot(x, y)
    +# A tick label rotation can be set using Axes.tick_params.
    +ax.tick_params("y", rotation=45)
    +# Alternatively, if setting custom labels with set_xticks/set_yticks, it can
    +# be set at the same time as the labels.
    +# For both APIs, the rotation can be an angle in degrees, or one of the strings
    +# "horizontal" or "vertical".
    +ax.set_xticks(x, labels, rotation='vertical')
    +
    +plt.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.axes.Axes.tick_params` / `matplotlib.pyplot.tick_params`
    +#    - `matplotlib.axes.Axes.set_xticks` / `matplotlib.pyplot.xticks`
    diff --git a/galleries/examples/ticks/ticks_too_many.py b/galleries/examples/ticks/ticks_too_many.py
    new file mode 100644
    index 000000000000..b70a281bc54d
    --- /dev/null
    +++ b/galleries/examples/ticks/ticks_too_many.py
    @@ -0,0 +1,76 @@
    +"""
    +=====================
    +Fixing too many ticks
    +=====================
    +
    +One common cause for unexpected tick behavior is passing a list of strings
    +instead of numbers or datetime objects. This can easily happen without notice
    +when reading in a comma-delimited text file. Matplotlib treats lists of strings
    +as *categorical* variables
    +(:doc:`/gallery/lines_bars_and_markers/categorical_variables`), and by default
    +puts one tick per category, and plots them in the order in which they are
    +supplied.  If this is not desired, the solution is to convert the strings to
    +a numeric type as in the following examples.
    +
    +"""
    +
    +# %%
    +# Example 1: Strings can lead to an unexpected order of number ticks
    +# ------------------------------------------------------------------
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +fig, ax = plt.subplots(1, 2, layout='constrained', figsize=(6, 2.5))
    +x = ['1', '5', '2', '3']
    +y = [1, 4, 2, 3]
    +ax[0].plot(x, y, 'd')
    +ax[0].tick_params(axis='x', color='r', labelcolor='r')
    +ax[0].set_xlabel('Categories')
    +ax[0].set_title('Ticks seem out of order / misplaced')
    +
    +# convert to numbers:
    +x = np.asarray(x, dtype='float')
    +ax[1].plot(x, y, 'd')
    +ax[1].set_xlabel('Floats')
    +ax[1].set_title('Ticks as expected')
    +
    +# %%
    +# Example 2: Strings can lead to very many ticks
    +# ----------------------------------------------
    +# If *x* has 100 elements, all strings, then we would have 100 (unreadable)
    +# ticks, and again the solution is to convert the strings to floats:
    +
    +fig, ax = plt.subplots(1, 2, figsize=(6, 2.5))
    +x = [f'{xx}' for xx in np.arange(100)]
    +y = np.arange(100)
    +ax[0].plot(x, y)
    +ax[0].tick_params(axis='x', color='r', labelcolor='r')
    +ax[0].set_title('Too many ticks')
    +ax[0].set_xlabel('Categories')
    +
    +ax[1].plot(np.asarray(x, float), y)
    +ax[1].set_title('x converted to numbers')
    +ax[1].set_xlabel('Floats')
    +
    +# %%
    +# Example 3: Strings can lead to an unexpected order of datetime ticks
    +# --------------------------------------------------------------------
    +# A common case is when dates are read from a CSV file, they need to be
    +# converted from strings to datetime objects to get the proper date locators
    +# and formatters.
    +
    +fig, ax = plt.subplots(1, 2, layout='constrained', figsize=(6, 2.75))
    +x = ['2021-10-01', '2021-11-02', '2021-12-03', '2021-09-01']
    +y = [0, 2, 3, 1]
    +ax[0].plot(x, y, 'd')
    +ax[0].tick_params(axis='x', labelrotation=90, color='r', labelcolor='r')
    +ax[0].set_title('Dates out of order')
    +
    +# convert to datetime64
    +x = np.asarray(x, dtype='datetime64[s]')
    +ax[1].plot(x, y, 'd')
    +ax[1].tick_params(axis='x', labelrotation=90)
    +ax[1].set_title('x converted to datetimes')
    +
    +plt.show()
    diff --git a/galleries/examples/units/README.txt b/galleries/examples/units/README.txt
    new file mode 100644
    index 000000000000..4274635106b8
    --- /dev/null
    +++ b/galleries/examples/units/README.txt
    @@ -0,0 +1,9 @@
    +.. _units_examples:
    +
    +.. _units-examples-index:
    +
    +Units
    +=====
    +
    +These examples cover the many representations of units
    +in Matplotlib.
    diff --git a/galleries/examples/units/annotate_with_units.py b/galleries/examples/units/annotate_with_units.py
    new file mode 100644
    index 000000000000..1a007e986465
    --- /dev/null
    +++ b/galleries/examples/units/annotate_with_units.py
    @@ -0,0 +1,37 @@
    +"""
    +=====================
    +Annotation with units
    +=====================
    +
    +The example illustrates how to create text and arrow
    +annotations using a centimeter-scale plot.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +
    +from basic_units import cm
    +
    +import matplotlib.pyplot as plt
    +
    +fig, ax = plt.subplots()
    +
    +ax.annotate("Note 01", [0.5*cm, 0.5*cm])
    +
    +# xy and text both unitized
    +ax.annotate('local max', xy=(3*cm, 1*cm), xycoords='data',
    +            xytext=(0.8*cm, 0.95*cm), textcoords='data',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='right', verticalalignment='top')
    +
    +# mixing units w/ nonunits
    +ax.annotate('local max', xy=(3*cm, 1*cm), xycoords='data',
    +            xytext=(0.8, 0.95), textcoords='axes fraction',
    +            arrowprops=dict(facecolor='black', shrink=0.05),
    +            horizontalalignment='right', verticalalignment='top')
    +
    +
    +ax.set_xlim(0*cm, 4*cm)
    +ax.set_ylim(0*cm, 4*cm)
    +plt.show()
    diff --git a/galleries/examples/units/artist_tests.py b/galleries/examples/units/artist_tests.py
    new file mode 100644
    index 000000000000..a6c56954b0c8
    --- /dev/null
    +++ b/galleries/examples/units/artist_tests.py
    @@ -0,0 +1,67 @@
    +"""
    +============
    +Artist tests
    +============
    +
    +Test unit support with each of the Matplotlib primitive artist types.
    +
    +The axis handles unit conversions and the artists keep a pointer to their axis
    +parent. You must initialize the artists with the axis instance if you want to
    +use them with unit data, or else they will not know how to convert the units
    +to scalars.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +import random
    +
    +from basic_units import cm, inch
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.collections as collections
    +import matplotlib.lines as lines
    +import matplotlib.patches as patches
    +import matplotlib.text as text
    +
    +fig, ax = plt.subplots()
    +ax.xaxis.set_units(cm)
    +ax.yaxis.set_units(cm)
    +
    +# Fixing random state for reproducibility
    +np.random.seed(19680801)
    +
    +if 0:
    +    # test a line collection
    +    # Not supported at present.
    +    verts = []
    +    for i in range(10):
    +        # a random line segment in inches
    +        verts.append(zip(*inch*10*np.random.rand(2, random.randint(2, 15))))
    +    lc = collections.LineCollection(verts, axes=ax)
    +    ax.add_collection(lc)
    +
    +# test a plain-ol-line
    +line = lines.Line2D([0*cm, 1.5*cm], [0*cm, 2.5*cm],
    +                    lw=2, color='black', axes=ax)
    +ax.add_line(line)
    +
    +if 0:
    +    # test a patch
    +    # Not supported at present.
    +    rect = patches.Rectangle((1*cm, 1*cm), width=5*cm, height=2*cm,
    +                             alpha=0.2, axes=ax)
    +    ax.add_patch(rect)
    +
    +
    +t = text.Text(3*cm, 2.5*cm, 'text label', ha='left', va='bottom', axes=ax)
    +ax.add_artist(t)
    +
    +ax.set_xlim(-1*cm, 10*cm)
    +ax.set_ylim(-1*cm, 10*cm)
    +# ax.xaxis.set_units(inch)
    +ax.grid(True)
    +ax.set_title("Artists with units")
    +plt.show()
    diff --git a/galleries/examples/units/bar_demo2.py b/galleries/examples/units/bar_demo2.py
    new file mode 100644
    index 000000000000..7c0c8215eca4
    --- /dev/null
    +++ b/galleries/examples/units/bar_demo2.py
    @@ -0,0 +1,38 @@
    +"""
    +===================
    +Bar demo with units
    +===================
    +
    +A plot using a variety of centimetre and inch conversions. This example shows
    +how default unit introspection works (ax1), how various keywords can be used to
    +set the x and y units to override the defaults (ax2, ax3, ax4) and how one can
    +set the xlimits using scalars (ax3, current units assumed) or units
    +(conversions applied to get the numbers to current units).
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +from basic_units import cm, inch
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +cms = cm * np.arange(0, 10, 2)
    +bottom = 0 * cm
    +width = 0.8 * cm
    +
    +fig, axs = plt.subplots(2, 2)
    +
    +axs[0, 0].bar(cms, cms, bottom=bottom)
    +
    +axs[0, 1].bar(cms, cms, bottom=bottom, width=width, xunits=cm, yunits=inch)
    +
    +axs[1, 0].bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=cm)
    +axs[1, 0].set_xlim(2, 6)  # scalars are interpreted in current units
    +
    +axs[1, 1].bar(cms, cms, bottom=bottom, width=width, xunits=inch, yunits=inch)
    +axs[1, 1].set_xlim(2 * cm, 6 * cm)  # cm are converted to inches
    +
    +fig.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/units/bar_unit_demo.py b/galleries/examples/units/bar_unit_demo.py
    new file mode 100644
    index 000000000000..05e0dfad902d
    --- /dev/null
    +++ b/galleries/examples/units/bar_unit_demo.py
    @@ -0,0 +1,42 @@
    +"""
    +=========================
    +Group barchart with units
    +=========================
    +
    +This is the same example as
    +:doc:`the barchart` in
    +centimeters.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +
    +from basic_units import cm, inch
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +N = 5
    +tea_means = [15*cm, 10*cm, 8*cm, 12*cm, 5*cm]
    +tea_std = [2*cm, 1*cm, 1*cm, 4*cm, 2*cm]
    +
    +fig, ax = plt.subplots()
    +ax.yaxis.set_units(inch)
    +
    +ind = np.arange(N)    # the x locations for the groups
    +width = 0.35         # the width of the bars
    +ax.bar(ind, tea_means, width, bottom=0*cm, yerr=tea_std, label='Tea')
    +
    +coffee_means = (14*cm, 19*cm, 7*cm, 5*cm, 10*cm)
    +coffee_std = (3*cm, 5*cm, 2*cm, 1*cm, 2*cm)
    +ax.bar(ind + width, coffee_means, width, bottom=0*cm, yerr=coffee_std,
    +       label='Coffee')
    +
    +ax.set_title('Cup height by group and beverage choice')
    +ax.set_xticks(ind + width / 2, labels=['G1', 'G2', 'G3', 'G4', 'G5'])
    +
    +ax.legend()
    +ax.autoscale_view()
    +
    +plt.show()
    diff --git a/galleries/examples/units/basic_units.py b/galleries/examples/units/basic_units.py
    new file mode 100644
    index 000000000000..f7bdcc18b0dc
    --- /dev/null
    +++ b/galleries/examples/units/basic_units.py
    @@ -0,0 +1,400 @@
    +"""
    +.. _basic_units:
    +
    +===========
    +Basic units
    +===========
    +
    +
    +This file implements a units library that supports registering arbitrary units,
    +conversions between units, and math with unitized data. This library also implements a
    +Matplotlib unit converter and registers its units with Matplotlib. This library is used
    +in the examples to demonstrate Matplotlib's unit support. It is only maintained for the
    +purposes of building documentation and should never be used outside of the Matplotlib
    +documentation.
    +
    +"""
    +
    +import itertools
    +import math
    +
    +from packaging.version import parse as parse_version
    +
    +import numpy as np
    +
    +import matplotlib.ticker as ticker
    +import matplotlib.units as units
    +
    +
    +class ProxyDelegate:
    +    def __init__(self, fn_name, proxy_type):
    +        self.proxy_type = proxy_type
    +        self.fn_name = fn_name
    +
    +    def __get__(self, obj, objtype=None):
    +        return self.proxy_type(self.fn_name, obj)
    +
    +
    +class TaggedValueMeta(type):
    +    def __init__(self, name, bases, dict):
    +        for fn_name in self._proxies:
    +            if not hasattr(self, fn_name):
    +                setattr(self, fn_name,
    +                        ProxyDelegate(fn_name, self._proxies[fn_name]))
    +
    +
    +class PassThroughProxy:
    +    def __init__(self, fn_name, obj):
    +        self.fn_name = fn_name
    +        self.target = obj.proxy_target
    +
    +    def __call__(self, *args):
    +        fn = getattr(self.target, self.fn_name)
    +        ret = fn(*args)
    +        return ret
    +
    +
    +class ConvertArgsProxy(PassThroughProxy):
    +    def __init__(self, fn_name, obj):
    +        super().__init__(fn_name, obj)
    +        self.unit = obj.unit
    +
    +    def __call__(self, *args):
    +        converted_args = []
    +        for a in args:
    +            try:
    +                converted_args.append(a.convert_to(self.unit))
    +            except AttributeError:
    +                converted_args.append(TaggedValue(a, self.unit))
    +        converted_args = tuple([c.get_value() for c in converted_args])
    +        return super().__call__(*converted_args)
    +
    +
    +class ConvertReturnProxy(PassThroughProxy):
    +    def __init__(self, fn_name, obj):
    +        super().__init__(fn_name, obj)
    +        self.unit = obj.unit
    +
    +    def __call__(self, *args):
    +        ret = super().__call__(*args)
    +        return (NotImplemented if ret is NotImplemented
    +                else TaggedValue(ret, self.unit))
    +
    +
    +class ConvertAllProxy(PassThroughProxy):
    +    def __init__(self, fn_name, obj):
    +        super().__init__(fn_name, obj)
    +        self.unit = obj.unit
    +
    +    def __call__(self, *args):
    +        converted_args = []
    +        arg_units = [self.unit]
    +        for a in args:
    +            if hasattr(a, 'get_unit') and not hasattr(a, 'convert_to'):
    +                # If this argument has a unit type but no conversion ability,
    +                # this operation is prohibited.
    +                return NotImplemented
    +
    +            if hasattr(a, 'convert_to'):
    +                try:
    +                    a = a.convert_to(self.unit)
    +                except Exception:
    +                    pass
    +                arg_units.append(a.get_unit())
    +                converted_args.append(a.get_value())
    +            else:
    +                converted_args.append(a)
    +                if hasattr(a, 'get_unit'):
    +                    arg_units.append(a.get_unit())
    +                else:
    +                    arg_units.append(None)
    +        converted_args = tuple(converted_args)
    +        ret = super().__call__(*converted_args)
    +        if ret is NotImplemented:
    +            return NotImplemented
    +        ret_unit = unit_resolver(self.fn_name, arg_units)
    +        if ret_unit is NotImplemented:
    +            return NotImplemented
    +        return TaggedValue(ret, ret_unit)
    +
    +
    +class TaggedValue(metaclass=TaggedValueMeta):
    +
    +    _proxies = {'__add__': ConvertAllProxy,
    +                '__sub__': ConvertAllProxy,
    +                '__mul__': ConvertAllProxy,
    +                '__rmul__': ConvertAllProxy,
    +                '__cmp__': ConvertAllProxy,
    +                '__lt__': ConvertAllProxy,
    +                '__gt__': ConvertAllProxy,
    +                '__len__': PassThroughProxy}
    +
    +    def __new__(cls, value, unit):
    +        # generate a new subclass for value
    +        value_class = type(value)
    +        try:
    +            subcls = type(f'TaggedValue_of_{value_class.__name__}',
    +                          (cls, value_class), {})
    +            return object.__new__(subcls)
    +        except TypeError:
    +            return object.__new__(cls)
    +
    +    def __init__(self, value, unit):
    +        self.value = value
    +        self.unit = unit
    +        self.proxy_target = self.value
    +
    +    def __copy__(self):
    +        return TaggedValue(self.value, self.unit)
    +
    +    def __getattribute__(self, name):
    +        if name.startswith('__'):
    +            return object.__getattribute__(self, name)
    +        variable = object.__getattribute__(self, 'value')
    +        if hasattr(variable, name) and name not in self.__class__.__dict__:
    +            return getattr(variable, name)
    +        return object.__getattribute__(self, name)
    +
    +    def __array__(self, dtype=object, copy=False):
    +        return np.asarray(self.value, dtype)
    +
    +    def __array_wrap__(self, array, context=None, return_scalar=False):
    +        return TaggedValue(array, self.unit)
    +
    +    def __repr__(self):
    +        return f'TaggedValue({self.value!r}, {self.unit!r})'
    +
    +    def __str__(self):
    +        return f"{self.value} in {self.unit}"
    +
    +    def __len__(self):
    +        return len(self.value)
    +
    +    if parse_version(np.__version__) >= parse_version('1.20'):
    +        def __getitem__(self, key):
    +            return TaggedValue(self.value[key], self.unit)
    +
    +    def __iter__(self):
    +        # Return a generator expression rather than use `yield`, so that
    +        # TypeError is raised by iter(self) if appropriate when checking for
    +        # iterability.
    +        return (TaggedValue(inner, self.unit) for inner in self.value)
    +
    +    def get_compressed_copy(self, mask):
    +        new_value = np.ma.masked_array(self.value, mask=mask).compressed()
    +        return TaggedValue(new_value, self.unit)
    +
    +    def convert_to(self, unit):
    +        if unit == self.unit or not unit:
    +            return self
    +        try:
    +            new_value = self.unit.convert_value_to(self.value, unit)
    +        except AttributeError:
    +            new_value = self
    +        return TaggedValue(new_value, unit)
    +
    +    def get_value(self):
    +        return self.value
    +
    +    def get_unit(self):
    +        return self.unit
    +
    +
    +class BasicUnit:
    +    # numpy scalars convert eager and np.float64(2) * BasicUnit('cm')
    +    # would thus return a numpy scalar. To avoid this, we increase the
    +    # priority of the BasicUnit.
    +    __array_priority__ = np.float64(0).__array_priority__ + 1
    +
    +    def __init__(self, name, fullname=None):
    +        self.name = name
    +        if fullname is None:
    +            fullname = name
    +        self.fullname = fullname
    +        self.conversions = dict()
    +
    +    def __repr__(self):
    +        return f'BasicUnit({self.name})'
    +
    +    def __str__(self):
    +        return self.fullname
    +
    +    def __call__(self, value):
    +        return TaggedValue(value, self)
    +
    +    def __mul__(self, rhs):
    +        value = rhs
    +        unit = self
    +        if hasattr(rhs, 'get_unit'):
    +            value = rhs.get_value()
    +            unit = rhs.get_unit()
    +            unit = unit_resolver('__mul__', (self, unit))
    +        if unit is NotImplemented:
    +            return NotImplemented
    +        return TaggedValue(value, unit)
    +
    +    def __rmul__(self, lhs):
    +        return self*lhs
    +
    +    def __array_wrap__(self, array, context=None, return_scalar=False):
    +        return TaggedValue(array, self)
    +
    +    def __array__(self, t=None, context=None, copy=False):
    +        ret = np.array(1)
    +        if t is not None:
    +            return ret.astype(t)
    +        else:
    +            return ret
    +
    +    def add_conversion_factor(self, unit, factor):
    +        def convert(x):
    +            return x*factor
    +        self.conversions[unit] = convert
    +
    +    def add_conversion_fn(self, unit, fn):
    +        self.conversions[unit] = fn
    +
    +    def get_conversion_fn(self, unit):
    +        return self.conversions[unit]
    +
    +    def convert_value_to(self, value, unit):
    +        conversion_fn = self.conversions[unit]
    +        ret = conversion_fn(value)
    +        return ret
    +
    +    def get_unit(self):
    +        return self
    +
    +
    +class UnitResolver:
    +    def addition_rule(self, units):
    +        for unit_1, unit_2 in itertools.pairwise(units):
    +            if unit_1 != unit_2:
    +                return NotImplemented
    +        return units[0]
    +
    +    def multiplication_rule(self, units):
    +        non_null = [u for u in units if u]
    +        if len(non_null) > 1:
    +            return NotImplemented
    +        return non_null[0]
    +
    +    op_dict = {
    +        '__mul__': multiplication_rule,
    +        '__rmul__': multiplication_rule,
    +        '__add__': addition_rule,
    +        '__radd__': addition_rule,
    +        '__sub__': addition_rule,
    +        '__rsub__': addition_rule}
    +
    +    def __call__(self, operation, units):
    +        if operation not in self.op_dict:
    +            return NotImplemented
    +
    +        return self.op_dict[operation](self, units)
    +
    +
    +unit_resolver = UnitResolver()
    +
    +cm = BasicUnit('cm', 'centimeters')
    +inch = BasicUnit('inch', 'inches')
    +inch.add_conversion_factor(cm, 2.54)
    +cm.add_conversion_factor(inch, 1/2.54)
    +
    +radians = BasicUnit('rad', 'radians')
    +degrees = BasicUnit('deg', 'degrees')
    +radians.add_conversion_factor(degrees, 180.0/np.pi)
    +degrees.add_conversion_factor(radians, np.pi/180.0)
    +
    +secs = BasicUnit('s', 'seconds')
    +hertz = BasicUnit('Hz', 'Hertz')
    +minutes = BasicUnit('min', 'minutes')
    +
    +secs.add_conversion_fn(hertz, lambda x: 1./x)
    +secs.add_conversion_factor(minutes, 1/60.0)
    +
    +
    +# radians formatting
    +def rad_fn(x, pos=None):
    +    if x >= 0:
    +        n = int((x / np.pi) * 2.0 + 0.25)
    +    else:
    +        n = int((x / np.pi) * 2.0 - 0.25)
    +
    +    if n == 0:
    +        return '0'
    +    elif n == 1:
    +        return r'$\pi/2$'
    +    elif n == 2:
    +        return r'$\pi$'
    +    elif n == -1:
    +        return r'$-\pi/2$'
    +    elif n == -2:
    +        return r'$-\pi$'
    +    elif n % 2 == 0:
    +        return fr'${n//2}\pi$'
    +    else:
    +        return fr'${n}\pi/2$'
    +
    +
    +class BasicUnitConverter(units.ConversionInterface):
    +    @staticmethod
    +    def axisinfo(unit, axis):
    +        """Return AxisInfo instance for x and unit."""
    +
    +        if unit == radians:
    +            return units.AxisInfo(
    +                majloc=ticker.MultipleLocator(base=np.pi/2),
    +                majfmt=ticker.FuncFormatter(rad_fn),
    +                label=unit.fullname,
    +            )
    +        elif unit == degrees:
    +            return units.AxisInfo(
    +                majloc=ticker.AutoLocator(),
    +                majfmt=ticker.FormatStrFormatter(r'$%i^\circ$'),
    +                label=unit.fullname,
    +            )
    +        elif unit is not None:
    +            if hasattr(unit, 'fullname'):
    +                return units.AxisInfo(label=unit.fullname)
    +            elif hasattr(unit, 'unit'):
    +                return units.AxisInfo(label=unit.unit.fullname)
    +        return None
    +
    +    @staticmethod
    +    def convert(val, unit, axis):
    +        if np.iterable(val):
    +            if isinstance(val, np.ma.MaskedArray):
    +                val = val.astype(float).filled(np.nan)
    +            out = np.empty(len(val))
    +            for i, thisval in enumerate(val):
    +                if np.ma.is_masked(thisval):
    +                    out[i] = np.nan
    +                else:
    +                    try:
    +                        out[i] = thisval.convert_to(unit).get_value()
    +                    except AttributeError:
    +                        out[i] = thisval
    +            return out
    +        if np.ma.is_masked(val):
    +            return np.nan
    +        else:
    +            return val.convert_to(unit).get_value()
    +
    +    @staticmethod
    +    def default_units(x, axis):
    +        """Return the default unit for x or None."""
    +        if np.iterable(x):
    +            for thisx in x:
    +                return thisx.unit
    +        return x.unit
    +
    +
    +def cos(x):
    +    if np.iterable(x):
    +        return [math.cos(val.convert_to(radians).get_value()) for val in x]
    +    else:
    +        return math.cos(x.convert_to(radians).get_value())
    +
    +
    +units.registry[BasicUnit] = units.registry[TaggedValue] = BasicUnitConverter()
    diff --git a/examples/units/ellipse_with_units.py b/galleries/examples/units/ellipse_with_units.py
    similarity index 77%
    rename from examples/units/ellipse_with_units.py
    rename to galleries/examples/units/ellipse_with_units.py
    index 2127d6c0323a..e4518c90eeb9 100644
    --- a/examples/units/ellipse_with_units.py
    +++ b/galleries/examples/units/ellipse_with_units.py
    @@ -1,18 +1,27 @@
     """
    -Compare the ellipse generated with arcs versus a polygonal approximation
    +==================
    +Ellipse with units
    +==================
    +
    +Compare the ellipse generated with arcs versus a polygonal approximation.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
     """
    +
     from basic_units import cm
    -import numpy as np
    -from matplotlib import patches
    +
     import matplotlib.pyplot as plt
    +import numpy as np
     
    +from matplotlib import patches
     
     xcenter, ycenter = 0.38*cm, 0.52*cm
    -#xcenter, ycenter = 0., 0.
     width, height = 1e-1*cm, 3e-1*cm
     angle = -30
     
    -theta = np.arange(0.0, 360.0, 1.0)*np.pi/180.0
    +theta = np.deg2rad(np.arange(0.0, 360.0, 1.0))
     x = 0.5 * width * np.cos(theta)
     y = 0.5 * height * np.sin(theta)
     
    @@ -23,13 +32,16 @@
         ])
     
     
    -x, y = np.dot(R, np.array([x, y]))
    +x, y = np.dot(R, [x, y])
     x += xcenter
     y += ycenter
     
    +# %%
    +
     fig = plt.figure()
     ax = fig.add_subplot(211, aspect='auto')
    -ax.fill(x, y, alpha=0.2, facecolor='yellow', edgecolor='yellow', linewidth=1, zorder=1)
    +ax.fill(x, y, alpha=0.2, facecolor='yellow',
    +        edgecolor='yellow', linewidth=1, zorder=1)
     
     e1 = patches.Ellipse((xcenter, ycenter), width, height,
                          angle=angle, linewidth=2, fill=False, zorder=2)
    @@ -43,13 +55,14 @@
     
     
     ax.add_patch(e2)
    -
    -#fig.savefig('ellipse_compare.png')
     fig.savefig('ellipse_compare')
     
    +# %%
    +
     fig = plt.figure()
     ax = fig.add_subplot(211, aspect='auto')
    -ax.fill(x, y, alpha=0.2, facecolor='yellow', edgecolor='yellow', linewidth=1, zorder=1)
    +ax.fill(x, y, alpha=0.2, facecolor='yellow',
    +        edgecolor='yellow', linewidth=1, zorder=1)
     
     e1 = patches.Arc((xcenter, ycenter), width, height,
                      angle=angle, linewidth=2, fill=False, zorder=2)
    @@ -63,8 +76,6 @@
     
     
     ax.add_patch(e2)
    -
    -#fig.savefig('arc_compare.png')
     fig.savefig('arc_compare')
     
     plt.show()
    diff --git a/galleries/examples/units/evans_test.py b/galleries/examples/units/evans_test.py
    new file mode 100644
    index 000000000000..b86b73bf5675
    --- /dev/null
    +++ b/galleries/examples/units/evans_test.py
    @@ -0,0 +1,87 @@
    +"""
    +==========
    +Evans test
    +==========
    +
    +A mockup "Foo" units class which supports conversion and different tick
    +formatting depending on the "unit".  Here the "unit" is just a scalar
    +conversion factor, but this example shows that Matplotlib is entirely agnostic
    +to what kind of units client packages use.
    +"""
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +import matplotlib.ticker as ticker
    +import matplotlib.units as units
    +
    +
    +class Foo:
    +    def __init__(self, val, unit=1.0):
    +        self.unit = unit
    +        self._val = val * unit
    +
    +    def value(self, unit):
    +        if unit is None:
    +            unit = self.unit
    +        return self._val / unit
    +
    +
    +class FooConverter(units.ConversionInterface):
    +    @staticmethod
    +    def axisinfo(unit, axis):
    +        """Return the Foo AxisInfo."""
    +        if unit == 1.0 or unit == 2.0:
    +            return units.AxisInfo(
    +                majloc=ticker.IndexLocator(8, 0),
    +                majfmt=ticker.FormatStrFormatter("VAL: %s"),
    +                label='foo',
    +                )
    +
    +        else:
    +            return None
    +
    +    @staticmethod
    +    def convert(obj, unit, axis):
    +        """
    +        Convert *obj* using *unit*.
    +
    +        If *obj* is a sequence, return the converted sequence.
    +        """
    +        if np.iterable(obj):
    +            return [o.value(unit) for o in obj]
    +        else:
    +            return obj.value(unit)
    +
    +    @staticmethod
    +    def default_units(x, axis):
    +        """Return the default unit for *x* or None."""
    +        if np.iterable(x):
    +            for thisx in x:
    +                return thisx.unit
    +        else:
    +            return x.unit
    +
    +
    +units.registry[Foo] = FooConverter()
    +
    +# create some Foos
    +x = [Foo(val, 1.0) for val in range(0, 50, 2)]
    +# and some arbitrary y data
    +y = [i for i in range(len(x))]
    +
    +fig, (ax1, ax2) = plt.subplots(1, 2)
    +fig.suptitle("Custom units")
    +fig.subplots_adjust(bottom=0.2)
    +
    +# plot specifying units
    +ax2.plot(x, y, 'o', xunits=2.0)
    +ax2.set_title("xunits = 2.0")
    +ax2.tick_params(axis='x', rotation=30, rotation_mode='xtick')
    +
    +# plot without specifying units; will use the None branch for axisinfo
    +ax1.plot(x, y)  # uses default units
    +ax1.set_title('default units')
    +ax1.tick_params(axis='x', rotation=30, rotation_mode='xtick')
    +
    +plt.show()
    diff --git a/galleries/examples/units/radian_demo.py b/galleries/examples/units/radian_demo.py
    new file mode 100644
    index 000000000000..492a1e971c55
    --- /dev/null
    +++ b/galleries/examples/units/radian_demo.py
    @@ -0,0 +1,30 @@
    +"""
    +============
    +Radian ticks
    +============
    +
    +Plot with radians from the basic_units mockup example package.
    +
    +
    +This example shows how the unit class can determine the tick locating,
    +formatting and axis labeling.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +
    +from basic_units import cos, degrees, radians
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +x = [val*radians for val in np.arange(0, 15, 0.01)]
    +
    +fig, axs = plt.subplots(2)
    +
    +axs[0].plot(x, cos(x), xunits=radians)
    +axs[1].plot(x, cos(x), xunits=degrees)
    +
    +fig.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/units/units_sample.py b/galleries/examples/units/units_sample.py
    new file mode 100644
    index 000000000000..2690ee7db727
    --- /dev/null
    +++ b/galleries/examples/units/units_sample.py
    @@ -0,0 +1,35 @@
    +"""
    +======================
    +Inches and centimeters
    +======================
    +
    +The example illustrates the ability to override default x and y units (ax1) to
    +inches and centimeters using the *xunits* and *yunits* parameters for the
    +`~.axes.Axes.plot` function. Note that conversions are applied to get numbers
    +to correct units.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +
    +"""
    +from basic_units import cm, inch
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +cms = cm * np.arange(0, 10, 2)
    +
    +fig, axs = plt.subplots(2, 2, layout='constrained')
    +
    +axs[0, 0].plot(cms, cms)
    +
    +axs[0, 1].plot(cms, cms, xunits=cm, yunits=inch)
    +
    +axs[1, 0].plot(cms, cms, xunits=inch, yunits=cm)
    +axs[1, 0].set_xlim(-1, 4)  # scalars are interpreted in current units
    +
    +axs[1, 1].plot(cms, cms, xunits=inch, yunits=inch)
    +axs[1, 1].set_xlim(3*cm, 6*cm)  # cm are converted to inches
    +
    +plt.show()
    diff --git a/galleries/examples/units/units_scatter.py b/galleries/examples/units/units_scatter.py
    new file mode 100644
    index 000000000000..bcaf2c6bee18
    --- /dev/null
    +++ b/galleries/examples/units/units_scatter.py
    @@ -0,0 +1,31 @@
    +"""
    +=============
    +Unit handling
    +=============
    +
    +The example below shows support for unit conversions over masked
    +arrays.
    +
    +.. only:: builder_html
    +
    +   This example requires :download:`basic_units.py `
    +"""
    +from basic_units import hertz, minutes, secs
    +
    +import matplotlib.pyplot as plt
    +import numpy as np
    +
    +# create masked array
    +data = (1, 2, 3, 4, 5, 6, 7, 8)
    +mask = (1, 0, 1, 0, 0, 0, 1, 0)
    +xsecs = secs * np.ma.MaskedArray(data, mask, float)
    +
    +fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, sharex=True)
    +
    +ax1.scatter(xsecs, xsecs)
    +ax1.yaxis.set_units(secs)
    +ax2.scatter(xsecs, xsecs, yunits=hertz)
    +ax3.scatter(xsecs, xsecs, yunits=minutes)
    +
    +fig.tight_layout()
    +plt.show()
    diff --git a/galleries/examples/user_interfaces/README.txt b/galleries/examples/user_interfaces/README.txt
    new file mode 100644
    index 000000000000..75b469da7cf6
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/README.txt
    @@ -0,0 +1,13 @@
    +.. _user_interfaces:
    +
    +Embedding Matplotlib in graphical user interfaces
    +=================================================
    +
    +You can embed Matplotlib directly into a user interface application by
    +following the embedding_in_SOMEGUI.py examples here. Currently
    +Matplotlib supports PyQt/PySide, PyGObject, Tkinter, and wxPython.
    +
    +When embedding Matplotlib in a GUI, you must use the Matplotlib API
    +directly rather than the pylab/pyplot procedural interface, so take a
    +look at the examples/api directory for some example code working with
    +the API.
    diff --git a/galleries/examples/user_interfaces/canvasagg.py b/galleries/examples/user_interfaces/canvasagg.py
    new file mode 100644
    index 000000000000..2786a2518dd3
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/canvasagg.py
    @@ -0,0 +1,71 @@
    +"""
    +==============
    +CanvasAgg demo
    +==============
    +
    +This example shows how to use the agg backend directly to create images, which
    +may be of use to web application developers who want full control over their
    +code without using the pyplot interface to manage figures, figure closing etc.
    +
    +.. note::
    +
    +    It is not necessary to avoid using the pyplot interface in order to
    +    create figures without a graphical front-end - simply setting
    +    the backend to "Agg" would be sufficient.
    +
    +In this example, we show how to save the contents of the agg canvas to a file,
    +and how to extract them to a numpy array, which can in turn be passed off
    +to Pillow_.  The latter functionality allows e.g. to use Matplotlib inside a
    +cgi-script *without* needing to write a figure to disk, and to write images in
    +any format supported by Pillow.
    +
    +.. _Pillow: https://pillow.readthedocs.io/
    +.. redirect-from:: /gallery/misc/agg_buffer
    +.. redirect-from:: /gallery/misc/agg_buffer_to_array
    +"""
    +
    +from PIL import Image
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_agg import FigureCanvasAgg
    +from matplotlib.figure import Figure
    +
    +fig = Figure(figsize=(5, 4), dpi=100)
    +
    +# Do some plotting.
    +ax = fig.add_subplot()
    +ax.plot([1, 2, 3])
    +
    +# Option 1: Save the figure to a file; can also be a file-like object (BytesIO,
    +# etc.).
    +fig.savefig("test.png")
    +
    +# Option 2 (low-level approach to directly save to a numpy array): Manually
    +# attach a canvas to the figure (pyplot or savefig would automatically do
    +# it), by instantiating the canvas with the figure as argument; then draw the
    +# figure, retrieve a memoryview on the renderer buffer, and convert it to a
    +# numpy array.
    +canvas = FigureCanvasAgg(fig)
    +canvas.draw()
    +rgba = np.asarray(canvas.buffer_rgba())
    +# ... and pass it to PIL.
    +im = Image.fromarray(rgba)
    +# This image can then be saved to any format supported by Pillow, e.g.:
    +im.save("test.bmp")
    +
    +# Uncomment this line to display the image using ImageMagick's `display` tool.
    +# im.show()
    +
    +# %%
    +#
    +# .. admonition:: References
    +#
    +#    The use of the following functions, methods, classes and modules is shown
    +#    in this example:
    +#
    +#    - `matplotlib.backends.backend_agg.FigureCanvasAgg`
    +#    - `matplotlib.figure.Figure`
    +#    - `matplotlib.figure.Figure.add_subplot`
    +#    - `matplotlib.figure.Figure.savefig` / `matplotlib.pyplot.savefig`
    +#    - `matplotlib.axes.Axes.plot` / `matplotlib.pyplot.plot`
    diff --git a/galleries/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py b/galleries/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py
    new file mode 100644
    index 000000000000..c5e3279b031d
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py
    @@ -0,0 +1,43 @@
    +"""
    +=======================================
    +Embed in GTK3 with a navigation toolbar
    +=======================================
    +
    +Demonstrate NavigationToolbar with GTK3 accessed via pygobject.
    +"""
    +
    +import gi
    +
    +gi.require_version('Gtk', '3.0')
    +from gi.repository import Gtk
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
    +from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
    +from matplotlib.figure import Figure
    +
    +win = Gtk.Window()
    +win.connect("delete-event", Gtk.main_quit)
    +win.set_default_size(400, 300)
    +win.set_title("Embedded in GTK3")
    +
    +fig = Figure(figsize=(5, 4), dpi=100)
    +ax = fig.add_subplot(1, 1, 1)
    +t = np.arange(0.0, 3.0, 0.01)
    +s = np.sin(2*np.pi*t)
    +ax.plot(t, s)
    +
    +vbox = Gtk.VBox()
    +win.add(vbox)
    +
    +# Add canvas to vbox
    +canvas = FigureCanvas(fig)  # a Gtk.DrawingArea
    +vbox.pack_start(canvas, True, True, 0)
    +
    +# Create toolbar
    +toolbar = NavigationToolbar(canvas)
    +vbox.pack_start(toolbar, False, False, 0)
    +
    +win.show_all()
    +Gtk.main()
    diff --git a/galleries/examples/user_interfaces/embedding_in_gtk3_sgskip.py b/galleries/examples/user_interfaces/embedding_in_gtk3_sgskip.py
    new file mode 100644
    index 000000000000..3ddff529b298
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_gtk3_sgskip.py
    @@ -0,0 +1,41 @@
    +"""
    +=============
    +Embed in GTK3
    +=============
    +
    +Demonstrate adding a FigureCanvasGTK3Agg widget to a Gtk.ScrolledWindow using
    +GTK3 accessed via pygobject.
    +"""
    +
    +import gi
    +
    +gi.require_version('Gtk', '3.0')
    +from gi.repository import Gtk
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
    +from matplotlib.figure import Figure
    +
    +win = Gtk.Window()
    +win.connect("delete-event", Gtk.main_quit)
    +win.set_default_size(400, 300)
    +win.set_title("Embedded in GTK3")
    +
    +fig = Figure(figsize=(5, 4), dpi=100)
    +ax = fig.add_subplot()
    +t = np.arange(0.0, 3.0, 0.01)
    +s = np.sin(2*np.pi*t)
    +ax.plot(t, s)
    +
    +sw = Gtk.ScrolledWindow()
    +win.add(sw)
    +# A scrolled window border goes outside the scrollbars and viewport
    +sw.set_border_width(10)
    +
    +canvas = FigureCanvas(fig)  # a Gtk.DrawingArea
    +canvas.set_size_request(800, 600)
    +sw.add(canvas)
    +
    +win.show_all()
    +Gtk.main()
    diff --git a/galleries/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py b/galleries/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py
    new file mode 100644
    index 000000000000..4dec7a219d4e
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py
    @@ -0,0 +1,50 @@
    +"""
    +=======================================
    +Embed in GTK4 with a navigation toolbar
    +=======================================
    +
    +Demonstrate NavigationToolbar with GTK4 accessed via pygobject.
    +"""
    +
    +import gi
    +
    +gi.require_version('Gtk', '4.0')
    +from gi.repository import Gtk
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_gtk4 import NavigationToolbar2GTK4 as NavigationToolbar
    +from matplotlib.backends.backend_gtk4agg import FigureCanvasGTK4Agg as FigureCanvas
    +from matplotlib.figure import Figure
    +
    +
    +def on_activate(app):
    +    win = Gtk.ApplicationWindow(application=app)
    +    win.set_default_size(400, 300)
    +    win.set_title("Embedded in GTK4")
    +
    +    fig = Figure(figsize=(5, 4), dpi=100)
    +    ax = fig.add_subplot(1, 1, 1)
    +    t = np.arange(0.0, 3.0, 0.01)
    +    s = np.sin(2*np.pi*t)
    +    ax.plot(t, s)
    +
    +    vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
    +    win.set_child(vbox)
    +
    +    # Add canvas to vbox
    +    canvas = FigureCanvas(fig)  # a Gtk.DrawingArea
    +    canvas.set_hexpand(True)
    +    canvas.set_vexpand(True)
    +    vbox.append(canvas)
    +
    +    # Create toolbar
    +    toolbar = NavigationToolbar(canvas)
    +    vbox.append(toolbar)
    +
    +    win.present()
    +
    +
    +app = Gtk.Application(application_id='org.matplotlib.examples.EmbeddingInGTK4PanZoom')
    +app.connect('activate', on_activate)
    +app.run(None)
    diff --git a/galleries/examples/user_interfaces/embedding_in_gtk4_sgskip.py b/galleries/examples/user_interfaces/embedding_in_gtk4_sgskip.py
    new file mode 100644
    index 000000000000..bc90700e48d3
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_gtk4_sgskip.py
    @@ -0,0 +1,46 @@
    +"""
    +=============
    +Embed in GTK4
    +=============
    +
    +Demonstrate adding a FigureCanvasGTK4Agg widget to a Gtk.ScrolledWindow using
    +GTK4 accessed via pygobject.
    +"""
    +
    +import gi
    +
    +gi.require_version('Gtk', '4.0')
    +from gi.repository import Gtk
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_gtk4agg import FigureCanvasGTK4Agg as FigureCanvas
    +from matplotlib.figure import Figure
    +
    +
    +def on_activate(app):
    +    win = Gtk.ApplicationWindow(application=app)
    +    win.set_default_size(400, 300)
    +    win.set_title("Embedded in GTK4")
    +
    +    fig = Figure(figsize=(5, 4), dpi=100)
    +    ax = fig.add_subplot()
    +    t = np.arange(0.0, 3.0, 0.01)
    +    s = np.sin(2*np.pi*t)
    +    ax.plot(t, s)
    +
    +    # A scrolled margin goes outside the scrollbars and viewport.
    +    sw = Gtk.ScrolledWindow(margin_top=10, margin_bottom=10,
    +                            margin_start=10, margin_end=10)
    +    win.set_child(sw)
    +
    +    canvas = FigureCanvas(fig)  # a Gtk.DrawingArea
    +    canvas.set_size_request(800, 600)
    +    sw.set_child(canvas)
    +
    +    win.present()
    +
    +
    +app = Gtk.Application(application_id='org.matplotlib.examples.EmbeddingInGTK4')
    +app.connect('activate', on_activate)
    +app.run(None)
    diff --git a/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py
    new file mode 100644
    index 000000000000..35a22efd67ec
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_qt_sgskip.py
    @@ -0,0 +1,87 @@
    +"""
    +===========
    +Embed in Qt
    +===========
    +
    +Simple Qt application embedding Matplotlib canvases.  This program will work
    +equally well using any Qt binding (PyQt6, PySide6, PyQt5, PySide2).  The
    +binding can be selected by setting the :envvar:`QT_API` environment variable to
    +the binding name, or by first importing it.
    +"""
    +
    +import sys
    +import time
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_qtagg import FigureCanvas
    +from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
    +from matplotlib.backends.qt_compat import QtWidgets
    +from matplotlib.figure import Figure
    +
    +
    +class ApplicationWindow(QtWidgets.QMainWindow):
    +    def __init__(self):
    +        super().__init__()
    +        self._main = QtWidgets.QWidget()
    +        self.setCentralWidget(self._main)
    +        layout = QtWidgets.QVBoxLayout(self._main)
    +
    +        static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
    +        # Ideally one would use self.addToolBar here, but it is slightly
    +        # incompatible between PyQt6 and other bindings, so we just add the
    +        # toolbar as a plain widget instead.
    +        layout.addWidget(NavigationToolbar(static_canvas, self))
    +        layout.addWidget(static_canvas)
    +
    +        dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
    +        layout.addWidget(dynamic_canvas)
    +        layout.addWidget(NavigationToolbar(dynamic_canvas, self))
    +
    +        self._static_ax = static_canvas.figure.subplots()
    +        t = np.linspace(0, 10, 501)
    +        self._static_ax.plot(t, np.tan(t), ".")
    +
    +        self._dynamic_ax = dynamic_canvas.figure.subplots()
    +        # Set up a Line2D.
    +        self.xdata = np.linspace(0, 10, 101)
    +        self._update_ydata()
    +        self._line, = self._dynamic_ax.plot(self.xdata, self.ydata)
    +        # The below two timers must be attributes of self, so that the garbage
    +        # collector won't clean them after we finish with __init__...
    +
    +        # The data retrieval may be fast as possible (Using QRunnable could be
    +        # even faster).
    +        self.data_timer = dynamic_canvas.new_timer(1)
    +        self.data_timer.add_callback(self._update_ydata)
    +        self.data_timer.start()
    +        # Drawing at 50Hz should be fast enough for the GUI to feel smooth, and
    +        # not too fast for the GUI to be overloaded with events that need to be
    +        # processed while the GUI element is changed.
    +        self.drawing_timer = dynamic_canvas.new_timer(20)
    +        self.drawing_timer.add_callback(self._update_canvas)
    +        self.drawing_timer.start()
    +
    +    def _update_ydata(self):
    +        # Shift the sinusoid as a function of time.
    +        self.ydata = np.sin(self.xdata + time.time())
    +
    +    def _update_canvas(self):
    +        self._line.set_data(self.xdata, self.ydata)
    +        # It should be safe to use the synchronous draw() method for most drawing
    +        # frequencies, but it is safer to use draw_idle().
    +        self._line.figure.canvas.draw_idle()
    +
    +
    +if __name__ == "__main__":
    +    # Check whether there is already a running QApplication (e.g., if running
    +    # from an IDE).
    +    qapp = QtWidgets.QApplication.instance()
    +    if not qapp:
    +        qapp = QtWidgets.QApplication(sys.argv)
    +
    +    app = ApplicationWindow()
    +    app.show()
    +    app.activateWindow()
    +    app.raise_()
    +    qapp.exec()
    diff --git a/galleries/examples/user_interfaces/embedding_in_tk_sgskip.py b/galleries/examples/user_interfaces/embedding_in_tk_sgskip.py
    new file mode 100644
    index 000000000000..2fa132a80227
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_tk_sgskip.py
    @@ -0,0 +1,65 @@
    +"""
    +===========
    +Embed in Tk
    +===========
    +
    +"""
    +
    +import tkinter
    +
    +import numpy as np
    +
    +# Implement the default Matplotlib key bindings.
    +from matplotlib.backend_bases import key_press_handler
    +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
    +from matplotlib.figure import Figure
    +
    +root = tkinter.Tk()
    +root.wm_title("Embedded in Tk")
    +
    +fig = Figure(figsize=(5, 4), dpi=100)
    +t = np.arange(0, 3, .01)
    +ax = fig.add_subplot()
    +line, = ax.plot(t, 2 * np.sin(2 * np.pi * t))
    +ax.set_xlabel("time [s]")
    +ax.set_ylabel("f(t)")
    +
    +canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
    +canvas.draw()
    +
    +# pack_toolbar=False will make it easier to use a layout manager later on.
    +toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
    +toolbar.update()
    +
    +canvas.mpl_connect(
    +    "key_press_event", lambda event: print(f"you pressed {event.key}"))
    +canvas.mpl_connect("key_press_event", key_press_handler)
    +
    +button_quit = tkinter.Button(master=root, text="Quit", command=root.destroy)
    +
    +
    +def update_frequency(new_val):
    +    # retrieve frequency
    +    f = float(new_val)
    +
    +    # update data
    +    y = 2 * np.sin(2 * np.pi * f * t)
    +    line.set_data(t, y)
    +
    +    # required to update canvas and attached toolbar!
    +    canvas.draw()
    +
    +
    +slider_update = tkinter.Scale(root, from_=1, to=5, orient=tkinter.HORIZONTAL,
    +                              command=update_frequency, label="Frequency [Hz]")
    +
    +# Packing order is important. Widgets are processed sequentially and if there
    +# is no space left, because the window is too small, they are not displayed.
    +# The canvas is rather flexible in its size, so we pack it last which makes
    +# sure the UI controls are displayed as long as possible.
    +button_quit.pack(side=tkinter.BOTTOM)
    +slider_update.pack(side=tkinter.BOTTOM)
    +toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
    +canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
    +
    +tkinter.mainloop()
    diff --git a/galleries/examples/user_interfaces/embedding_in_wx2_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx2_sgskip.py
    new file mode 100644
    index 000000000000..634d8c511aa7
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_wx2_sgskip.py
    @@ -0,0 +1,64 @@
    +"""
    +==============
    +Embed in wx #2
    +==============
    +
    +An example of how to use wxagg in an application with the new
    +toolbar - comment out the add_toolbar line for no toolbar.
    +"""
    +
    +import wx
    +import wx.lib.mixins.inspection as WIT
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    +from matplotlib.backends.backend_wxagg import \
    +    NavigationToolbar2WxAgg as NavigationToolbar
    +from matplotlib.figure import Figure
    +
    +
    +class CanvasFrame(wx.Frame):
    +    def __init__(self):
    +        super().__init__(None, -1, 'CanvasFrame', size=(550, 350))
    +
    +        self.figure = Figure()
    +        self.axes = self.figure.add_subplot()
    +        t = np.arange(0.0, 3.0, 0.01)
    +        s = np.sin(2 * np.pi * t)
    +
    +        self.axes.plot(t, s)
    +        self.canvas = FigureCanvas(self, -1, self.figure)
    +
    +        self.sizer = wx.BoxSizer(wx.VERTICAL)
    +        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)
    +        self.SetSizer(self.sizer)
    +        self.Fit()
    +
    +        self.add_toolbar()  # comment this out for no toolbar
    +
    +    def add_toolbar(self):
    +        self.toolbar = NavigationToolbar(self.canvas)
    +        self.toolbar.Realize()
    +        # By adding toolbar in sizer, we are able to put it at the bottom
    +        # of the frame - so appearance is closer to GTK version.
    +        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
    +        # update the axes menu on the toolbar
    +        self.toolbar.update()
    +
    +
    +# Alternatively you could use:
    +# class App(wx.App):
    +class App(WIT.InspectableApp):
    +    def OnInit(self):
    +        """Create the main window and insert the custom frame."""
    +        self.Init()
    +        frame = CanvasFrame()
    +        frame.Show(True)
    +
    +        return True
    +
    +
    +if __name__ == "__main__":
    +    app = App()
    +    app.MainLoop()
    diff --git a/galleries/examples/user_interfaces/embedding_in_wx3_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx3_sgskip.py
    new file mode 100644
    index 000000000000..4917180582fe
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_wx3_sgskip.py
    @@ -0,0 +1,145 @@
    +"""
    +==============
    +Embed in wx #3
    +==============
    +
    +Copyright (C) 2003-2004 Andrew Straw, Jeremy O'Donoghue and others
    +
    +License: This work is licensed under the PSF. A copy should be included
    +with this source code, and is also available at
    +https://docs.python.org/3/license.html
    +
    +This is yet another example of using matplotlib with wx.  Hopefully
    +this is pretty full-featured:
    +
    +- both matplotlib toolbar and WX buttons manipulate plot
    +- full wxApp framework, including widget interaction
    +- XRC (XML wxWidgets resource) file to create GUI (made with XRCed)
    +
    +This was derived from embedding_in_wx and dynamic_image_wxagg.
    +
    +Thanks to matplotlib and wx teams for creating such great software!
    +"""
    +
    +import wx
    +import wx.xrc as xrc
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    +from matplotlib.backends.backend_wxagg import \
    +    NavigationToolbar2WxAgg as NavigationToolbar
    +import matplotlib.cbook as cbook
    +from matplotlib.figure import Figure
    +
    +ERR_TOL = 1e-5  # floating point slop for peak-detection
    +
    +
    +class PlotPanel(wx.Panel):
    +    def __init__(self, parent):
    +        super().__init__(parent, -1)
    +
    +        self.fig = Figure((5, 4), 75)
    +        self.canvas = FigureCanvas(self, -1, self.fig)
    +        self.toolbar = NavigationToolbar(self.canvas)  # matplotlib toolbar
    +        self.toolbar.Realize()
    +
    +        # Now put all into a sizer
    +        sizer = wx.BoxSizer(wx.VERTICAL)
    +        # This way of adding to sizer allows resizing
    +        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
    +        # Best to allow the toolbar to resize!
    +        sizer.Add(self.toolbar, 0, wx.GROW)
    +        self.SetSizer(sizer)
    +        self.Fit()
    +
    +    def init_plot_data(self):
    +        ax = self.fig.add_subplot()
    +
    +        x = np.arange(120.0) * 2 * np.pi / 60.0
    +        y = np.arange(100.0) * 2 * np.pi / 50.0
    +        self.x, self.y = np.meshgrid(x, y)
    +        z = np.sin(self.x) + np.cos(self.y)
    +        self.im = ax.imshow(z, cmap="RdBu", origin='lower')
    +
    +        zmax = np.max(z) - ERR_TOL
    +        ymax_i, xmax_i = np.nonzero(z >= zmax)
    +        if self.im.origin == 'upper':
    +            ymax_i = z.shape[0] - ymax_i
    +        self.lines = ax.plot(xmax_i, ymax_i, 'ko')
    +
    +        self.toolbar.update()  # Not sure why this is needed - ADS
    +
    +    def GetToolBar(self):
    +        # You will need to override GetToolBar if you are using an
    +        # unmanaged toolbar in your frame
    +        return self.toolbar
    +
    +    def OnWhiz(self, event):
    +        self.x += np.pi / 15
    +        self.y += np.pi / 20
    +        z = np.sin(self.x) + np.cos(self.y)
    +        self.im.set_array(z)
    +
    +        zmax = np.max(z) - ERR_TOL
    +        ymax_i, xmax_i = np.nonzero(z >= zmax)
    +        if self.im.origin == 'upper':
    +            ymax_i = z.shape[0] - ymax_i
    +        self.lines[0].set_data(xmax_i, ymax_i)
    +
    +        self.canvas.draw()
    +
    +
    +class MyApp(wx.App):
    +    def OnInit(self):
    +        xrcfile = cbook.get_sample_data('embedding_in_wx3.xrc',
    +                                        asfileobj=False)
    +        print('loading', xrcfile)
    +
    +        self.res = xrc.XmlResource(xrcfile)
    +
    +        # main frame and panel ---------
    +
    +        self.frame = self.res.LoadFrame(None, "MainFrame")
    +        self.panel = xrc.XRCCTRL(self.frame, "MainPanel")
    +
    +        # matplotlib panel -------------
    +
    +        # container for matplotlib panel (I like to make a container
    +        # panel for our panel so I know where it'll go when in XRCed.)
    +        plot_container = xrc.XRCCTRL(self.frame, "plot_container_panel")
    +        sizer = wx.BoxSizer(wx.VERTICAL)
    +
    +        # matplotlib panel itself
    +        self.plotpanel = PlotPanel(plot_container)
    +        self.plotpanel.init_plot_data()
    +
    +        # wx boilerplate
    +        sizer.Add(self.plotpanel, 1, wx.EXPAND)
    +        plot_container.SetSizer(sizer)
    +
    +        # whiz button ------------------
    +        whiz_button = xrc.XRCCTRL(self.frame, "whiz_button")
    +        whiz_button.Bind(wx.EVT_BUTTON, self.plotpanel.OnWhiz)
    +
    +        # bang button ------------------
    +        bang_button = xrc.XRCCTRL(self.frame, "bang_button")
    +        bang_button.Bind(wx.EVT_BUTTON, self.OnBang)
    +
    +        # final setup ------------------
    +        self.frame.Show()
    +
    +        self.SetTopWindow(self.frame)
    +
    +        return True
    +
    +    def OnBang(self, event):
    +        bang_count = xrc.XRCCTRL(self.frame, "bang_count")
    +        bangs = bang_count.GetValue()
    +        bangs = int(bangs) + 1
    +        bang_count.SetValue(str(bangs))
    +
    +
    +if __name__ == '__main__':
    +    app = MyApp()
    +    app.MainLoop()
    diff --git a/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py
    new file mode 100644
    index 000000000000..062f1219adb5
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_wx4_sgskip.py
    @@ -0,0 +1,79 @@
    +"""
    +==============
    +Embed in wx #4
    +==============
    +
    +An example of how to use wxagg in a wx application with a custom toolbar.
    +"""
    +
    +import wx
    +
    +import numpy as np
    +
    +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    +from matplotlib.backends.backend_wxagg import \
    +    NavigationToolbar2WxAgg as NavigationToolbar
    +from matplotlib.figure import Figure
    +
    +
    +class MyNavigationToolbar(NavigationToolbar):
    +    """Extend the default wx toolbar with your own event handlers."""
    +
    +    def __init__(self, canvas):
    +        super().__init__(canvas)
    +        # We use a stock wx bitmap, but you could also use your own image file.
    +        bmp = wx.ArtProvider.GetBitmap(wx.ART_CROSS_MARK, wx.ART_TOOLBAR)
    +        tool = self.AddTool(wx.ID_ANY, 'Click me', bmp,
    +                            'Activate custom control')
    +        self.Bind(wx.EVT_TOOL, self._on_custom, id=tool.GetId())
    +
    +    def _on_custom(self, event):
    +        # add some text to the Axes in a random location in axes coords with a
    +        # random color
    +        ax = self.canvas.figure.axes[0]
    +        x, y = np.random.rand(2)  # generate a random location
    +        rgb = np.random.rand(3)  # generate a random color
    +        ax.text(x, y, 'You clicked me', transform=ax.transAxes, color=rgb)
    +        self.canvas.draw()
    +        event.Skip()
    +
    +
    +class CanvasFrame(wx.Frame):
    +    def __init__(self):
    +        super().__init__(None, -1, 'CanvasFrame', size=(550, 350))
    +
    +        self.figure = Figure(figsize=(5, 4), dpi=100)
    +        self.axes = self.figure.add_subplot()
    +        t = np.arange(0.0, 3.0, 0.01)
    +        s = np.sin(2 * np.pi * t)
    +
    +        self.axes.plot(t, s)
    +
    +        self.canvas = FigureCanvas(self, -1, self.figure)
    +
    +        self.sizer = wx.BoxSizer(wx.VERTICAL)
    +        self.sizer.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND)
    +
    +        self.toolbar = MyNavigationToolbar(self.canvas)
    +        self.toolbar.Realize()
    +        # By adding toolbar in sizer, we are able to put it at the bottom
    +        # of the frame - so appearance is closer to GTK version.
    +        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
    +
    +        # update the axes menu on the toolbar
    +        self.toolbar.update()
    +        self.SetSizer(self.sizer)
    +        self.Fit()
    +
    +
    +class App(wx.App):
    +    def OnInit(self):
    +        """Create the main window and insert the custom frame."""
    +        frame = CanvasFrame()
    +        frame.Show(True)
    +        return True
    +
    +
    +if __name__ == "__main__":
    +    app = App()
    +    app.MainLoop()
    diff --git a/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py b/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py
    new file mode 100644
    index 000000000000..f150e2106ead
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_in_wx5_sgskip.py
    @@ -0,0 +1,62 @@
    +"""
    +==============
    +Embed in wx #5
    +==============
    +
    +"""
    +
    +import wx
    +import wx.lib.agw.aui as aui
    +import wx.lib.mixins.inspection as wit
    +
    +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    +from matplotlib.backends.backend_wxagg import \
    +    NavigationToolbar2WxAgg as NavigationToolbar
    +from matplotlib.figure import Figure
    +
    +
    +class Plot(wx.Panel):
    +    def __init__(self, parent, id=-1, dpi=None, **kwargs):
    +        super().__init__(parent, id=id, **kwargs)
    +        self.figure = Figure(dpi=dpi, figsize=(2, 2))
    +        self.canvas = FigureCanvas(self, -1, self.figure)
    +        self.toolbar = NavigationToolbar(self.canvas)
    +        self.toolbar.Realize()
    +
    +        sizer = wx.BoxSizer(wx.VERTICAL)
    +        sizer.Add(self.canvas, 1, wx.EXPAND)
    +        sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
    +        self.SetSizer(sizer)
    +
    +
    +class PlotNotebook(wx.Panel):
    +    def __init__(self, parent, id=-1):
    +        super().__init__(parent, id=id)
    +        self.nb = aui.AuiNotebook(self)
    +        sizer = wx.BoxSizer()
    +        sizer.Add(self.nb, 1, wx.EXPAND)
    +        self.SetSizer(sizer)
    +
    +    def add(self, name="plot"):
    +        page = Plot(self.nb)
    +        self.nb.AddPage(page, name)
    +        return page.figure
    +
    +
    +def demo():
    +    # Alternatively you could use:
    +    # app = wx.App()
    +    # InspectableApp is a great debug tool, see:
    +    # http://wiki.wxpython.org/Widget%20Inspection%20Tool
    +    app = wit.InspectableApp()
    +    frame = wx.Frame(None, -1, 'Plotter')
    +    plotter = PlotNotebook(frame)
    +    axes1 = plotter.add('figure 1').add_subplot()
    +    axes1.plot([1, 2, 3], [2, 1, 4])
    +    axes2 = plotter.add('figure 2').add_subplot()
    +    axes2.plot([1, 2, 3, 4, 5], [2, 1, 4, 2, 3])
    +    frame.Show()
    +    app.MainLoop()
    +
    +if __name__ == "__main__":
    +    demo()
    diff --git a/galleries/examples/user_interfaces/embedding_webagg_sgskip.py b/galleries/examples/user_interfaces/embedding_webagg_sgskip.py
    new file mode 100644
    index 000000000000..40d8a718facc
    --- /dev/null
    +++ b/galleries/examples/user_interfaces/embedding_webagg_sgskip.py
    @@ -0,0 +1,274 @@
    +"""
    +================
    +Embedding WebAgg
    +================
    +
    +This example demonstrates how to embed Matplotlib WebAgg interactive plotting
    +in your own web application and framework.  It is not necessary to do all this
    +if you merely want to display a plot in a browser or use Matplotlib's built-in
    +Tornado-based server "on the side".
    +
    +The framework being used must support web sockets.
    +"""
    +
    +import argparse
    +import io
    +import json
    +import mimetypes
    +from pathlib import Path
    +import signal
    +import socket
    +
    +try:
    +    import tornado
    +except ImportError as err:
    +    raise RuntimeError("This example requires tornado.") from err
    +import tornado.httpserver
    +import tornado.ioloop
    +import tornado.web
    +import tornado.websocket
    +
    +import numpy as np
    +
    +import matplotlib as mpl
    +from matplotlib.backends.backend_webagg import (FigureManagerWebAgg,
    +                                                new_figure_manager_given_figure)
    +from matplotlib.figure import Figure
    +
    +
    +def create_figure():
    +    """
    +    Creates a simple example figure.
    +    """
    +    fig = Figure()
    +    ax = fig.add_subplot()
    +    t = np.arange(0.0, 3.0, 0.01)
    +    s = np.sin(2 * np.pi * t)
    +    ax.plot(t, s)
    +    return fig
    +
    +
    +# The following is the content of the web page.  You would normally
    +# generate this using some sort of template facility in your web
    +# framework, but here we just use Python string formatting.
    +html_content = """
    +
    +  
    +    
    +    
    +    
    +    
    +    
    +    
    +
    +    
    +
    +    matplotlib
    +  
    +
    +  
    +    
    +
    + + +""" + + +class MyApplication(tornado.web.Application): + class MainPage(tornado.web.RequestHandler): + """ + Serves the main HTML page. + """ + + def get(self): + manager = self.application.manager + ws_uri = f"ws://{self.request.host}/" + content = html_content % { + "ws_uri": ws_uri, "fig_id": manager.num} + self.write(content) + + class MplJs(tornado.web.RequestHandler): + """ + Serves the generated matplotlib javascript file. The content + is dynamically generated based on which toolbar functions the + user has defined. Call `FigureManagerWebAgg` to get its + content. + """ + + def get(self): + self.set_header('Content-Type', 'application/javascript') + js_content = FigureManagerWebAgg.get_javascript() + + self.write(js_content) + + class Download(tornado.web.RequestHandler): + """ + Handles downloading of the figure in various file formats. + """ + + def get(self, fmt): + manager = self.application.manager + self.set_header( + 'Content-Type', mimetypes.types_map.get(fmt, 'binary')) + buff = io.BytesIO() + manager.canvas.figure.savefig(buff, format=fmt) + self.write(buff.getvalue()) + + class WebSocket(tornado.websocket.WebSocketHandler): + """ + A websocket for interactive communication between the plot in + the browser and the server. + + In addition to the methods required by tornado, it is required to + have two callback methods: + + - ``send_json(json_content)`` is called by matplotlib when + it needs to send json to the browser. `json_content` is + a JSON tree (Python dictionary), and it is the responsibility + of this implementation to encode it as a string to send over + the socket. + + - ``send_binary(blob)`` is called to send binary image data + to the browser. + """ + supports_binary = True + + def open(self): + # Register the websocket with the FigureManager. + manager = self.application.manager + manager.add_web_socket(self) + if hasattr(self, 'set_nodelay'): + self.set_nodelay(True) + + def on_close(self): + # When the socket is closed, deregister the websocket with + # the FigureManager. + manager = self.application.manager + manager.remove_web_socket(self) + + def on_message(self, message): + # The 'supports_binary' message is relevant to the + # websocket itself. The other messages get passed along + # to matplotlib as-is. + + # Every message has a "type" and a "figure_id". + message = json.loads(message) + if message['type'] == 'supports_binary': + self.supports_binary = message['value'] + else: + manager = self.application.manager + manager.handle_json(message) + + def send_json(self, content): + self.write_message(json.dumps(content)) + + def send_binary(self, blob): + if self.supports_binary: + self.write_message(blob, binary=True) + else: + data_uri = ("data:image/png;base64," + + blob.encode('base64').replace('\n', '')) + self.write_message(data_uri) + + def __init__(self, figure): + self.figure = figure + self.manager = new_figure_manager_given_figure(id(figure), figure) + + super().__init__([ + # Static files for the CSS and JS + (r'/_static/(.*)', + tornado.web.StaticFileHandler, + {'path': FigureManagerWebAgg.get_static_file_path()}), + + # Static images for the toolbar + (r'/_images/(.*)', + tornado.web.StaticFileHandler, + {'path': Path(mpl.get_data_path(), 'images')}), + + # The page that contains all of the pieces + ('/', self.MainPage), + + ('/mpl.js', self.MplJs), + + # Sends images and events to the browser, and receives + # events from the browser + ('/ws', self.WebSocket), + + # Handles the downloading (i.e., saving) of static images + (r'/download.([a-z0-9.]+)', self.Download), + ]) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--port', type=int, default=8080, + help='Port to listen on (0 for a random port).') + args = parser.parse_args() + + figure = create_figure() + application = MyApplication(figure) + + http_server = tornado.httpserver.HTTPServer(application) + sockets = tornado.netutil.bind_sockets(args.port, '') + http_server.add_sockets(sockets) + + for s in sockets: + addr, port = s.getsockname()[:2] + if s.family is socket.AF_INET6: + addr = f'[{addr}]' + print(f"Listening on http://{addr}:{port}/") + print("Press Ctrl+C to quit") + + ioloop = tornado.ioloop.IOLoop.instance() + + def shutdown(): + ioloop.stop() + print("Server stopped") + + old_handler = signal.signal( + signal.SIGINT, + lambda sig, frame: ioloop.add_callback_from_signal(shutdown)) + + try: + ioloop.start() + finally: + signal.signal(signal.SIGINT, old_handler) diff --git a/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py b/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py new file mode 100644 index 000000000000..9e72b3745a40 --- /dev/null +++ b/galleries/examples/user_interfaces/fourier_demo_wx_sgskip.py @@ -0,0 +1,238 @@ +""" +=============== +Fourier Demo WX +=============== + +""" + +import wx + +import numpy as np + +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas +from matplotlib.figure import Figure + + +class Knob: + """ + Knob - simple class with a "setKnob" method. + A Knob instance is attached to a Param instance, e.g., param.attach(knob) + Base class is for documentation purposes. + """ + + def setKnob(self, value): + pass + + +class Param: + """ + The idea of the "Param" class is that some parameter in the GUI may have + several knobs that both control it and reflect the parameter's state, e.g. + a slider, text, and dragging can all change the value of the frequency in + the waveform of this example. + The class allows a cleaner way to update/"feedback" to the other knobs when + one is being changed. Also, this class handles min/max constraints for all + the knobs. + Idea - knob list - in "set" method, knob object is passed as well + - the other knobs in the knob list have a "set" method which gets + called for the others. + """ + + def __init__(self, initialValue=None, minimum=0., maximum=1.): + self.minimum = minimum + self.maximum = maximum + if initialValue != self.constrain(initialValue): + raise ValueError('illegal initial value') + self.value = initialValue + self.knobs = [] + + def attach(self, knob): + self.knobs += [knob] + + def set(self, value, knob=None): + self.value = value + self.value = self.constrain(value) + for feedbackKnob in self.knobs: + if feedbackKnob != knob: + feedbackKnob.setKnob(self.value) + return self.value + + def constrain(self, value): + if value <= self.minimum: + value = self.minimum + if value >= self.maximum: + value = self.maximum + return value + + +class SliderGroup(Knob): + def __init__(self, parent, label, param): + self.sliderLabel = wx.StaticText(parent, label=label) + self.sliderText = wx.TextCtrl(parent, -1, style=wx.TE_PROCESS_ENTER) + self.slider = wx.Slider(parent, -1) + # self.slider.SetMax(param.maximum*1000) + self.slider.SetRange(0, int(param.maximum * 1000)) + self.setKnob(param.value) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(self.sliderLabel, 0, + wx.EXPAND | wx.ALL, + border=2) + sizer.Add(self.sliderText, 0, + wx.EXPAND | wx.ALL, + border=2) + sizer.Add(self.slider, 1, wx.EXPAND) + self.sizer = sizer + + self.slider.Bind(wx.EVT_SLIDER, self.sliderHandler) + self.sliderText.Bind(wx.EVT_TEXT_ENTER, self.sliderTextHandler) + + self.param = param + self.param.attach(self) + + def sliderHandler(self, event): + value = event.GetInt() / 1000. + self.param.set(value) + + def sliderTextHandler(self, event): + value = float(self.sliderText.GetValue()) + self.param.set(value) + + def setKnob(self, value): + self.sliderText.SetValue(f'{value:g}') + self.slider.SetValue(int(value * 1000)) + + +class FourierDemoFrame(wx.Frame): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + panel = wx.Panel(self) + + # create the GUI elements + self.createCanvas(panel) + self.createSliders(panel) + + # place them in a sizer for the Layout + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.canvas, 1, wx.EXPAND) + sizer.Add(self.frequencySliderGroup.sizer, 0, + wx.EXPAND | wx.ALL, border=5) + sizer.Add(self.amplitudeSliderGroup.sizer, 0, + wx.EXPAND | wx.ALL, border=5) + panel.SetSizer(sizer) + + def createCanvas(self, parent): + self.lines = [] + self.figure = Figure() + self.canvas = FigureCanvas(parent, -1, self.figure) + self.canvas.callbacks.connect('button_press_event', self.mouseDown) + self.canvas.callbacks.connect('motion_notify_event', self.mouseMotion) + self.canvas.callbacks.connect('button_release_event', self.mouseUp) + self.state = '' + self.mouseInfo = (None, None, None, None) + self.f0 = Param(2., minimum=0., maximum=6.) + self.A = Param(1., minimum=0.01, maximum=2.) + self.createPlots() + + # Not sure I like having two params attached to the same Knob, + # but that is what we have here... it works but feels kludgy - + # although maybe it's not too bad since the knob changes both params + # at the same time (both f0 and A are affected during a drag) + self.f0.attach(self) + self.A.attach(self) + + def createSliders(self, panel): + self.frequencySliderGroup = SliderGroup( + panel, + label='Frequency f0:', + param=self.f0) + self.amplitudeSliderGroup = SliderGroup(panel, label=' Amplitude a:', + param=self.A) + + def mouseDown(self, event): + if self.lines[0].contains(event)[0]: + self.state = 'frequency' + elif self.lines[1].contains(event)[0]: + self.state = 'time' + else: + self.state = '' + self.mouseInfo = (event.xdata, event.ydata, + max(self.f0.value, .1), + self.A.value) + + def mouseMotion(self, event): + if self.state == '': + return + x, y = event.xdata, event.ydata + if x is None: # outside the Axes + return + x0, y0, f0Init, AInit = self.mouseInfo + self.A.set(AInit + (AInit * (y - y0) / y0), self) + if self.state == 'frequency': + self.f0.set(f0Init + (f0Init * (x - x0) / x0)) + elif self.state == 'time': + if (x - x0) / x0 != -1.: + self.f0.set(1. / (1. / f0Init + (1. / f0Init * (x - x0) / x0))) + + def mouseUp(self, event): + self.state = '' + + def createPlots(self): + # This method creates the subplots, waveforms and labels. + # Later, when the waveforms or sliders are dragged, only the + # waveform data will be updated (not here, but below in setKnob). + self.subplot1, self.subplot2 = self.figure.subplots(2) + x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value) + color = (1., 0., 0.) + self.lines += self.subplot1.plot(x1, y1, color=color, linewidth=2) + self.lines += self.subplot2.plot(x2, y2, color=color, linewidth=2) + # Set some plot attributes + self.subplot1.set_title( + "Click and drag waveforms to change frequency and amplitude", + fontsize=12) + self.subplot1.set_ylabel("Frequency Domain Waveform X(f)", fontsize=8) + self.subplot1.set_xlabel("frequency f", fontsize=8) + self.subplot2.set_ylabel("Time Domain Waveform x(t)", fontsize=8) + self.subplot2.set_xlabel("time t", fontsize=8) + self.subplot1.set_xlim(-6, 6) + self.subplot1.set_ylim(0, 1) + self.subplot2.set_xlim(-2, 2) + self.subplot2.set_ylim(-2, 2) + self.subplot1.text(0.05, .95, + r'$X(f) = \mathcal{F}\{x(t)\}$', + verticalalignment='top', + transform=self.subplot1.transAxes) + self.subplot2.text(0.05, .95, + r'$x(t) = a \cdot \cos(2\pi f_0 t) e^{-\pi t^2}$', + verticalalignment='top', + transform=self.subplot2.transAxes) + + def compute(self, f0, A): + f = np.arange(-6., 6., 0.02) + t = np.arange(-2., 2., 0.01) + x = A * np.cos(2 * np.pi * f0 * t) * np.exp(-np.pi * t ** 2) + X = A / 2 * \ + (np.exp(-np.pi * (f - f0) ** 2) + np.exp(-np.pi * (f + f0) ** 2)) + return f, X, t, x + + def setKnob(self, value): + # Note, we ignore value arg here and just go by state of the params + x1, y1, x2, y2 = self.compute(self.f0.value, self.A.value) + # update the data of the two waveforms + self.lines[0].set(xdata=x1, ydata=y1) + self.lines[1].set(xdata=x2, ydata=y2) + # make the canvas draw its contents again with the new data + self.canvas.draw() + + +class App(wx.App): + def OnInit(self): + self.frame1 = FourierDemoFrame(parent=None, title="Fourier Demo", + size=(640, 480)) + self.frame1.Show() + return True + + +if __name__ == "__main__": + app = App() + app.MainLoop() diff --git a/galleries/examples/user_interfaces/gtk3_spreadsheet_sgskip.py b/galleries/examples/user_interfaces/gtk3_spreadsheet_sgskip.py new file mode 100644 index 000000000000..bd95deaabba3 --- /dev/null +++ b/galleries/examples/user_interfaces/gtk3_spreadsheet_sgskip.py @@ -0,0 +1,89 @@ +""" +================ +GTK3 spreadsheet +================ + +Example of embedding Matplotlib in an application and interacting with a +treeview to store data. Double-click on an entry to update plot data. +""" + +import gi + +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +from gi.repository import Gdk, Gtk + +from numpy.random import random + +from matplotlib.backends.backend_gtk3agg import FigureCanvas # or gtk3cairo. +from matplotlib.figure import Figure + + +class DataManager(Gtk.Window): + num_rows, num_cols = 20, 10 + + data = random((num_rows, num_cols)) + + def __init__(self): + super().__init__() + self.set_default_size(600, 600) + self.connect('destroy', lambda win: Gtk.main_quit()) + + self.set_title('GtkListStore demo') + self.set_border_width(8) + + vbox = Gtk.VBox(homogeneous=False, spacing=8) + self.add(vbox) + + label = Gtk.Label(label='Double click a row to plot the data') + + vbox.pack_start(label, False, False, 0) + + sw = Gtk.ScrolledWindow() + sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) + sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + vbox.pack_start(sw, True, True, 0) + + model = self.create_model() + + self.treeview = Gtk.TreeView(model=model) + + # Matplotlib stuff + fig = Figure(figsize=(6, 4)) + + self.canvas = FigureCanvas(fig) # a Gtk.DrawingArea + vbox.pack_start(self.canvas, True, True, 0) + ax = fig.add_subplot() + self.line, = ax.plot(self.data[0, :], 'go') # plot the first row + + self.treeview.connect('row-activated', self.plot_row) + sw.add(self.treeview) + + self.add_columns() + + self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.KEY_PRESS_MASK | + Gdk.EventMask.KEY_RELEASE_MASK) + + def plot_row(self, treeview, path, view_column): + ind, = path # get the index into data + points = self.data[ind, :] + self.line.set_ydata(points) + self.canvas.draw() + + def add_columns(self): + for i in range(self.num_cols): + column = Gtk.TreeViewColumn(str(i), Gtk.CellRendererText(), text=i) + self.treeview.append_column(column) + + def create_model(self): + types = [float] * self.num_cols + store = Gtk.ListStore(*types) + for row in self.data: + store.append(tuple(row)) + return store + + +manager = DataManager() +manager.show_all() +Gtk.main() diff --git a/galleries/examples/user_interfaces/gtk4_spreadsheet_sgskip.py b/galleries/examples/user_interfaces/gtk4_spreadsheet_sgskip.py new file mode 100644 index 000000000000..bfd7f0274883 --- /dev/null +++ b/galleries/examples/user_interfaces/gtk4_spreadsheet_sgskip.py @@ -0,0 +1,92 @@ +""" +================ +GTK4 spreadsheet +================ + +Example of embedding Matplotlib in an application and interacting with a +treeview to store data. Double-click on an entry to update plot data. +""" + +import gi + +gi.require_version('Gtk', '4.0') +gi.require_version('Gdk', '4.0') +from gi.repository import Gtk + +from numpy.random import random + +from matplotlib.backends.backend_gtk4agg import FigureCanvas # or gtk4cairo. +from matplotlib.figure import Figure + + +class DataManager(Gtk.ApplicationWindow): + num_rows, num_cols = 20, 10 + + data = random((num_rows, num_cols)) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_default_size(600, 600) + + self.set_title('GtkListStore demo') + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False, + spacing=8) + self.set_child(vbox) + + label = Gtk.Label(label='Double click a row to plot the data') + vbox.append(label) + + sw = Gtk.ScrolledWindow() + sw.set_has_frame(True) + sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + sw.set_hexpand(True) + sw.set_vexpand(True) + vbox.append(sw) + + model = self.create_model() + self.treeview = Gtk.TreeView(model=model) + self.treeview.connect('row-activated', self.plot_row) + sw.set_child(self.treeview) + + # Matplotlib stuff + fig = Figure(figsize=(6, 4), layout='constrained') + + self.canvas = FigureCanvas(fig) # a Gtk.DrawingArea + self.canvas.set_hexpand(True) + self.canvas.set_vexpand(True) + vbox.append(self.canvas) + ax = fig.add_subplot() + self.line, = ax.plot(self.data[0, :], 'go') # plot the first row + + self.add_columns() + + def plot_row(self, treeview, path, view_column): + ind, = path # get the index into data + points = self.data[ind, :] + self.line.set_ydata(points) + self.canvas.draw() + + def add_columns(self): + for i in range(self.num_cols): + column = Gtk.TreeViewColumn(str(i), Gtk.CellRendererText(), text=i) + self.treeview.append_column(column) + + def create_model(self): + types = [float] * self.num_cols + store = Gtk.ListStore(*types) + for row in self.data: + # Gtk.ListStore.append is broken in PyGObject, so insert manually. + it = store.insert(-1) + store.set(it, {i: val for i, val in enumerate(row)}) + return store + + +def on_activate(app): + manager = DataManager(application=app) + manager.show() + + +app = Gtk.Application(application_id='org.matplotlib.examples.GTK4Spreadsheet') +app.connect('activate', on_activate) +app.run() diff --git a/galleries/examples/user_interfaces/images/eye-symbolic.svg b/galleries/examples/user_interfaces/images/eye-symbolic.svg new file mode 100644 index 000000000000..20d5db230405 --- /dev/null +++ b/galleries/examples/user_interfaces/images/eye-symbolic.svg @@ -0,0 +1,70 @@ + + + + + + + + 2021-07-14T19:48:07.525592 + image/svg+xml + + + Matplotlib v3.4.2.post1357+gf1afce77c6.d20210714, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/galleries/examples/user_interfaces/images/eye.pdf b/galleries/examples/user_interfaces/images/eye.pdf new file mode 100644 index 000000000000..52f18e8342f8 Binary files /dev/null and b/galleries/examples/user_interfaces/images/eye.pdf differ diff --git a/galleries/examples/user_interfaces/images/eye.png b/galleries/examples/user_interfaces/images/eye.png new file mode 100644 index 000000000000..365f6fbcde5d Binary files /dev/null and b/galleries/examples/user_interfaces/images/eye.png differ diff --git a/galleries/examples/user_interfaces/images/eye.svg b/galleries/examples/user_interfaces/images/eye.svg new file mode 100644 index 000000000000..20d5db230405 --- /dev/null +++ b/galleries/examples/user_interfaces/images/eye.svg @@ -0,0 +1,70 @@ + + + + + + + + 2021-07-14T19:48:07.525592 + image/svg+xml + + + Matplotlib v3.4.2.post1357+gf1afce77c6.d20210714, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/galleries/examples/user_interfaces/images/eye_large.png b/galleries/examples/user_interfaces/images/eye_large.png new file mode 100644 index 000000000000..f8a2911032a4 Binary files /dev/null and b/galleries/examples/user_interfaces/images/eye_large.png differ diff --git a/galleries/examples/user_interfaces/mathtext_wx_sgskip.py b/galleries/examples/user_interfaces/mathtext_wx_sgskip.py new file mode 100644 index 000000000000..5d3c5c6bc46d --- /dev/null +++ b/galleries/examples/user_interfaces/mathtext_wx_sgskip.py @@ -0,0 +1,133 @@ +""" +====================== +Display mathtext in WX +====================== + +Demonstrates how to convert (math)text to a wx.Bitmap for display in various +controls on wxPython. +""" + +from io import BytesIO + +import wx + +import numpy as np + +from matplotlib.backends.backend_wx import NavigationToolbar2Wx +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas +from matplotlib.figure import Figure + +IS_WIN = 'wxMSW' in wx.PlatformInfo + + +def mathtext_to_wxbitmap(s): + # We draw the text at position (0, 0) but then rely on + # ``facecolor="none"`` and ``bbox_inches="tight", pad_inches=0`` to get a + # transparent mask that is then loaded into a wx.Bitmap. + fig = Figure(facecolor="none") + text_color = ( + np.array(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) / 255) + fig.text(0, 0, s, fontsize=10, color=text_color) + buf = BytesIO() + fig.savefig(buf, format="png", dpi=150, bbox_inches="tight", pad_inches=0) + s = buf.getvalue() + return wx.Bitmap.NewFromPNGData(s, len(s)) + + +functions = [ + (r'$\sin(2 \pi x)$', lambda x: np.sin(2*np.pi*x)), + (r'$\frac{4}{3}\pi x^3$', lambda x: (4/3)*np.pi*x**3), + (r'$\cos(2 \pi x)$', lambda x: np.cos(2*np.pi*x)), + (r'$\log(x)$', lambda x: np.log(x)) +] + + +class CanvasFrame(wx.Frame): + def __init__(self, parent, title): + super().__init__(parent, -1, title, size=(550, 350)) + + self.figure = Figure() + self.axes = self.figure.add_subplot() + + self.canvas = FigureCanvas(self, -1, self.figure) + + self.change_plot(0) + + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.add_buttonbar() + self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) + self.add_toolbar() # comment this out for no toolbar + + menuBar = wx.MenuBar() + + # File Menu + menu = wx.Menu() + m_exit = menu.Append( + wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") + menuBar.Append(menu, "&File") + self.Bind(wx.EVT_MENU, self.OnClose, m_exit) + + if IS_WIN: + # Equation Menu + menu = wx.Menu() + for i, (mt, func) in enumerate(functions): + bm = mathtext_to_wxbitmap(mt) + item = wx.MenuItem(menu, 1000 + i, " ") + item.SetBitmap(bm) + menu.Append(item) + self.Bind(wx.EVT_MENU, self.OnChangePlot, item) + menuBar.Append(menu, "&Functions") + + self.SetMenuBar(menuBar) + + self.SetSizer(self.sizer) + self.Fit() + + def add_buttonbar(self): + self.button_bar = wx.Panel(self) + self.button_bar_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.sizer.Add(self.button_bar, 0, wx.LEFT | wx.TOP | wx.GROW) + + for i, (mt, func) in enumerate(functions): + bm = mathtext_to_wxbitmap(mt) + button = wx.BitmapButton(self.button_bar, 1000 + i, bm) + self.button_bar_sizer.Add(button, 1, wx.GROW) + self.Bind(wx.EVT_BUTTON, self.OnChangePlot, button) + + self.button_bar.SetSizer(self.button_bar_sizer) + + def add_toolbar(self): + """Copied verbatim from embedding_wx2.py""" + self.toolbar = NavigationToolbar2Wx(self.canvas) + self.toolbar.Realize() + # By adding toolbar in sizer, we are able to put it at the bottom + # of the frame - so appearance is closer to GTK version. + self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) + # update the axes menu on the toolbar + self.toolbar.update() + + def OnChangePlot(self, event): + self.change_plot(event.GetId() - 1000) + + def change_plot(self, plot_number): + t = np.arange(1.0, 3.0, 0.01) + s = functions[plot_number][1](t) + self.axes.clear() + self.axes.plot(t, s) + self.canvas.draw() + + def OnClose(self, event): + self.Destroy() + + +class MyApp(wx.App): + def OnInit(self): + frame = CanvasFrame(None, "wxPython mathtext demo app") + self.SetTopWindow(frame) + frame.Show(True) + return True + + +if __name__ == "__main__": + app = MyApp() + app.MainLoop() diff --git a/examples/user_interfaces/mpl_with_glade_316.glade b/galleries/examples/user_interfaces/mpl_with_glade3.glade similarity index 100% rename from examples/user_interfaces/mpl_with_glade_316.glade rename to galleries/examples/user_interfaces/mpl_with_glade3.glade diff --git a/galleries/examples/user_interfaces/mpl_with_glade3_sgskip.py b/galleries/examples/user_interfaces/mpl_with_glade3_sgskip.py new file mode 100644 index 000000000000..8321405aa011 --- /dev/null +++ b/galleries/examples/user_interfaces/mpl_with_glade3_sgskip.py @@ -0,0 +1,53 @@ +""" +======================= +Matplotlib with Glade 3 +======================= +""" + +from pathlib import Path + +import gi + +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +import numpy as np + +from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas +from matplotlib.figure import Figure + + +class Window1Signals: + def on_window1_destroy(self, widget): + Gtk.main_quit() + + +def main(): + builder = Gtk.Builder() + builder.add_objects_from_file( + str(Path(__file__).parent / "mpl_with_glade3.glade"), + ("window1", "")) + builder.connect_signals(Window1Signals()) + window = builder.get_object("window1") + sw = builder.get_object("scrolledwindow1") + + # Start of Matplotlib specific code + figure = Figure(figsize=(8, 6), dpi=71) + axis = figure.add_subplot() + t = np.arange(0.0, 3.0, 0.01) + s = np.sin(2*np.pi*t) + axis.plot(t, s) + + axis.set_xlabel('time [s]') + axis.set_ylabel('voltage [V]') + + canvas = FigureCanvas(figure) # a Gtk.DrawingArea + canvas.set_size_request(800, 600) + sw.add(canvas) + # End of Matplotlib specific code + + window.show_all() + Gtk.main() + +if __name__ == "__main__": + main() diff --git a/galleries/examples/user_interfaces/mplcvd.py b/galleries/examples/user_interfaces/mplcvd.py new file mode 100644 index 000000000000..78decd8a4927 --- /dev/null +++ b/galleries/examples/user_interfaces/mplcvd.py @@ -0,0 +1,300 @@ +""" +mplcvd -- an example of figure hook +=================================== + +To use this hook, ensure that this module is in your ``PYTHONPATH``, and set +``rcParams["figure.hooks"] = ["mplcvd:setup"]``. This hook depends on +the ``colorspacious`` third-party module. +""" + +import functools +from pathlib import Path + +import colorspacious + +import numpy as np + +_BUTTON_NAME = "Filter" +_BUTTON_HELP = "Simulate color vision deficiencies" +_MENU_ENTRIES = { + "None": None, + "Greyscale": "greyscale", + "Deuteranopia": "deuteranomaly", + "Protanopia": "protanomaly", + "Tritanopia": "tritanomaly", +} + + +def _get_color_filter(name): + """ + Given a color filter name, create a color filter function. + + Parameters + ---------- + name : str + The color filter name, one of the following: + + - ``"none"``: ... + - ``"greyscale"``: Convert the input to luminosity. + - ``"deuteranopia"``: Simulate the most common form of red-green + colorblindness. + - ``"protanopia"``: Simulate a rarer form of red-green colorblindness. + - ``"tritanopia"``: Simulate the rare form of blue-yellow + colorblindness. + + Color conversions use `colorspacious`_. + + Returns + ------- + callable + A color filter function that has the form: + + def filter(input: np.ndarray[M, N, D])-> np.ndarray[M, N, D] + + where (M, N) are the image dimensions, and D is the color depth (3 for + RGB, 4 for RGBA). Alpha is passed through unchanged and otherwise + ignored. + """ + if name not in _MENU_ENTRIES: + raise ValueError(f"Unsupported filter name: {name!r}") + name = _MENU_ENTRIES[name] + + if name is None: + return None + + elif name == "greyscale": + rgb_to_jch = colorspacious.cspace_converter("sRGB1", "JCh") + jch_to_rgb = colorspacious.cspace_converter("JCh", "sRGB1") + + def convert(im): + greyscale_JCh = rgb_to_jch(im) + greyscale_JCh[..., 1] = 0 + im = jch_to_rgb(greyscale_JCh) + return im + + else: + cvd_space = {"name": "sRGB1+CVD", "cvd_type": name, "severity": 100} + convert = colorspacious.cspace_converter(cvd_space, "sRGB1") + + def filter_func(im, dpi): + alpha = None + if im.shape[-1] == 4: + im, alpha = im[..., :3], im[..., 3] + im = convert(im) + if alpha is not None: + im = np.dstack((im, alpha)) + return np.clip(im, 0, 1), 0, 0 + + return filter_func + + +def _set_menu_entry(tb, name): + tb.canvas.figure.set_agg_filter(_get_color_filter(name)) + tb.canvas.draw_idle() + + +def setup(figure): + tb = figure.canvas.toolbar + if tb is None: + return + for cls in type(tb).__mro__: + pkg = cls.__module__.split(".")[0] + if pkg != "matplotlib": + break + if pkg == "gi": + _setup_gtk(tb) + elif pkg in ("PyQt5", "PySide2", "PyQt6", "PySide6"): + _setup_qt(tb) + elif pkg == "tkinter": + _setup_tk(tb) + elif pkg == "wx": + _setup_wx(tb) + else: + raise NotImplementedError("The current backend is not supported") + + +def _setup_gtk(tb): + from gi.repository import Gio, GLib, Gtk + + for idx in range(tb.get_n_items()): + children = tb.get_nth_item(idx).get_children() + if children and isinstance(children[0], Gtk.Label): + break + + toolitem = Gtk.SeparatorToolItem() + tb.insert(toolitem, idx) + + image = Gtk.Image.new_from_gicon( + Gio.Icon.new_for_string( + str(Path(__file__).parent / "images/eye-symbolic.svg")), + Gtk.IconSize.LARGE_TOOLBAR) + + # The type of menu is progressively downgraded depending on GTK version. + if Gtk.check_version(3, 6, 0) is None: + + group = Gio.SimpleActionGroup.new() + action = Gio.SimpleAction.new_stateful("cvdsim", + GLib.VariantType("s"), + GLib.Variant("s", "none")) + group.add_action(action) + + @functools.partial(action.connect, "activate") + def set_filter(action, parameter): + _set_menu_entry(tb, parameter.get_string()) + action.set_state(parameter) + + menu = Gio.Menu() + for name in _MENU_ENTRIES: + menu.append(name, f"local.cvdsim::{name}") + + button = Gtk.MenuButton.new() + button.remove(button.get_children()[0]) + button.add(image) + button.insert_action_group("local", group) + button.set_menu_model(menu) + button.get_style_context().add_class("flat") + + item = Gtk.ToolItem() + item.add(button) + tb.insert(item, idx + 1) + + else: + + menu = Gtk.Menu() + group = [] + for name in _MENU_ENTRIES: + item = Gtk.RadioMenuItem.new_with_label(group, name) + item.set_active(name == "None") + item.connect( + "activate", lambda item: _set_menu_entry(tb, item.get_label())) + group.append(item) + menu.append(item) + menu.show_all() + + tbutton = Gtk.MenuToolButton.new(image, _BUTTON_NAME) + tbutton.set_menu(menu) + tb.insert(tbutton, idx + 1) + + tb.show_all() + + +def _setup_qt(tb): + from matplotlib.backends.qt_compat import QtGui, QtWidgets + + menu = QtWidgets.QMenu() + try: + QActionGroup = QtGui.QActionGroup # Qt6 + except AttributeError: + QActionGroup = QtWidgets.QActionGroup # Qt5 + group = QActionGroup(menu) + group.triggered.connect(lambda action: _set_menu_entry(tb, action.text())) + + for name in _MENU_ENTRIES: + action = menu.addAction(name) + action.setCheckable(True) + action.setActionGroup(group) + action.setChecked(name == "None") + + actions = tb.actions() + before = next( + (action for action in actions + if isinstance(tb.widgetForAction(action), QtWidgets.QLabel)), None) + + tb.insertSeparator(before) + button = QtWidgets.QToolButton() + # FIXME: _icon needs public API. + button.setIcon(tb._icon(str(Path(__file__).parent / "images/eye.png"))) + button.setText(_BUTTON_NAME) + button.setToolTip(_BUTTON_HELP) + button.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup) + button.setMenu(menu) + tb.insertWidget(before, button) + + +def _setup_tk(tb): + import tkinter as tk + + tb._Spacer() # FIXME: _Spacer needs public API. + + button = tk.Menubutton(master=tb, relief="raised") + button._image_file = str(Path(__file__).parent / "images/eye.png") + # FIXME: _set_image_for_button needs public API (perhaps like _icon). + tb._set_image_for_button(button) + button.pack(side=tk.LEFT) + + menu = tk.Menu(master=button, tearoff=False) + for name in _MENU_ENTRIES: + menu.add("radiobutton", label=name, + command=lambda _name=name: _set_menu_entry(tb, _name)) + menu.invoke(0) + button.config(menu=menu) + + +def _setup_wx(tb): + import wx + + idx = next(idx for idx in range(tb.ToolsCount) + if tb.GetToolByPos(idx).IsStretchableSpace()) + tb.InsertSeparator(idx) + tool = tb.InsertTool( + idx + 1, -1, _BUTTON_NAME, + # FIXME: _icon needs public API. + tb._icon(str(Path(__file__).parent / "images/eye.png")), + # FIXME: ITEM_DROPDOWN is not supported on macOS. + kind=wx.ITEM_DROPDOWN, shortHelp=_BUTTON_HELP) + + menu = wx.Menu() + for name in _MENU_ENTRIES: + item = menu.AppendRadioItem(-1, name) + menu.Bind( + wx.EVT_MENU, + lambda event, _name=name: _set_menu_entry(tb, _name), + id=item.Id, + ) + tb.SetDropdownMenu(tool.Id, menu) + + +if __name__ == '__main__': + import matplotlib.pyplot as plt + + from matplotlib import cbook + + plt.rcParams['figure.hooks'].append('mplcvd:setup') + + fig, axd = plt.subplot_mosaic( + [ + ['viridis', 'turbo'], + ['photo', 'lines'] + ] + ) + + delta = 0.025 + x = y = np.arange(-3.0, 3.0, delta) + X, Y = np.meshgrid(x, y) + Z1 = np.exp(-X**2 - Y**2) + Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2) + Z = (Z1 - Z2) * 2 + + imv = axd['viridis'].imshow( + Z, interpolation='bilinear', + origin='lower', extent=[-3, 3, -3, 3], + vmax=abs(Z).max(), vmin=-abs(Z).max() + ) + fig.colorbar(imv) + imt = axd['turbo'].imshow( + Z, interpolation='bilinear', cmap='turbo', + origin='lower', extent=[-3, 3, -3, 3], + vmax=abs(Z).max(), vmin=-abs(Z).max() + ) + fig.colorbar(imt) + + # A sample image + with cbook.get_sample_data('grace_hopper.jpg') as image_file: + photo = plt.imread(image_file) + axd['photo'].imshow(photo) + + th = np.linspace(0, 2*np.pi, 1024) + for j in [1, 2, 4, 6]: + axd['lines'].plot(th, np.sin(th * j), label=f'$\\omega={j}$') + axd['lines'].legend(ncols=2, loc='upper right') + plt.show() diff --git a/galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py b/galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py new file mode 100644 index 000000000000..e86e2a75d629 --- /dev/null +++ b/galleries/examples/user_interfaces/pylab_with_gtk3_sgskip.py @@ -0,0 +1,61 @@ +""" +================ +pyplot with GTK3 +================ + +An example of how to use pyplot to manage your figure windows, but modify the +GUI by accessing the underlying GTK widgets. +""" + +import matplotlib + +matplotlib.use('GTK3Agg') # or 'GTK3Cairo' +import gi + +import matplotlib.pyplot as plt + +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +fig, ax = plt.subplots() +ax.plot([1, 2, 3], 'ro-', label='easy as 1 2 3') +ax.plot([1, 4, 9], 'gs--', label='easy as 1 2 3 squared') +ax.legend() + +manager = fig.canvas.manager +# you can access the window or vbox attributes this way +toolbar = manager.toolbar +vbox = manager.vbox + +# now let's add a button to the toolbar +button = Gtk.Button(label='Click me') +button.show() +button.connect('clicked', lambda button: print('hi mom')) + +toolitem = Gtk.ToolItem() +toolitem.show() +toolitem.set_tooltip_text('Click me for fun and profit') +toolitem.add(button) + +pos = 8 # where to insert this in the toolbar +toolbar.insert(toolitem, pos) + +# now let's add a widget to the vbox +label = Gtk.Label() +label.set_markup('Drag mouse over axes for position') +label.show() +vbox.pack_start(label, False, False, 0) +vbox.reorder_child(toolbar, -1) + + +def update(event): + if event.xdata is None: + label.set_markup('Drag mouse over axes for position') + else: + label.set_markup( + f'x,y=({event.xdata}, {event.ydata})') + + +fig.canvas.mpl_connect('motion_notify_event', update) + +plt.show() diff --git a/galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py b/galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py new file mode 100644 index 000000000000..0d449530934e --- /dev/null +++ b/galleries/examples/user_interfaces/pylab_with_gtk4_sgskip.py @@ -0,0 +1,52 @@ +""" +================ +pyplot with GTK4 +================ + +An example of how to use pyplot to manage your figure windows, but modify the +GUI by accessing the underlying GTK widgets. +""" + +import matplotlib + +matplotlib.use('GTK4Agg') # or 'GTK4Cairo' +import gi + +import matplotlib.pyplot as plt + +gi.require_version('Gtk', '4.0') +from gi.repository import Gtk + +fig, ax = plt.subplots() +ax.plot([1, 2, 3], 'ro-', label='easy as 1 2 3') +ax.plot([1, 4, 9], 'gs--', label='easy as 1 2 3 squared') +ax.legend() + +manager = fig.canvas.manager +# you can access the window or vbox attributes this way +toolbar = manager.toolbar +vbox = manager.vbox + +# now let's add a button to the toolbar +button = Gtk.Button(label='Click me') +button.connect('clicked', lambda button: print('hi mom')) +button.set_tooltip_text('Click me for fun and profit') +toolbar.append(button) + +# now let's add a widget to the vbox +label = Gtk.Label() +label.set_markup('Drag mouse over axes for position') +vbox.insert_child_after(label, fig.canvas) + + +def update(event): + if event.xdata is None: + label.set_markup('Drag mouse over axes for position') + else: + label.set_markup( + f'x,y=({event.xdata}, {event.ydata})') + + +fig.canvas.mpl_connect('motion_notify_event', update) + +plt.show() diff --git a/galleries/examples/user_interfaces/svg_histogram_sgskip.py b/galleries/examples/user_interfaces/svg_histogram_sgskip.py new file mode 100644 index 000000000000..7a484d998e69 --- /dev/null +++ b/galleries/examples/user_interfaces/svg_histogram_sgskip.py @@ -0,0 +1,159 @@ +""" +============= +SVG Histogram +============= + +Demonstrate how to create an interactive histogram, in which bars +are hidden or shown by clicking on legend markers. + +The interactivity is encoded in ecmascript (javascript) and inserted in +the SVG code in a post-processing step. To render the image, open it in +a web browser. SVG is supported in most web browsers used by Linux and +macOS users. Windows IE9 supports SVG, but earlier versions do not. + +Notes +----- +The matplotlib backend lets us assign ids to each object. This is the +mechanism used here to relate matplotlib objects created in python and +the corresponding SVG constructs that are parsed in the second step. +While flexible, ids are cumbersome to use for large collection of +objects. Two mechanisms could be used to simplify things: + +* systematic grouping of objects into SVG tags, +* assigning classes to each SVG object according to its origin. + +For example, instead of modifying the properties of each individual bar, +the bars from the `~.pyplot.hist` function could either be grouped in +a PatchCollection, or be assigned a class="hist_##" attribute. + +CSS could also be used more extensively to replace repetitive markup +throughout the generated SVG. + +Author: david.huard@gmail.com + +""" + + +from io import BytesIO +import json +import xml.etree.ElementTree as ET + +import matplotlib.pyplot as plt +import numpy as np + +plt.rcParams['svg.fonttype'] = 'none' + +# Apparently, this `register_namespace` method is necessary to avoid garbling +# the XML namespace with ns0. +ET.register_namespace("", "http://www.w3.org/2000/svg") + +# Fixing random state for reproducibility +np.random.seed(19680801) + +# --- Create histogram, legend and title --- +plt.figure() +r = np.random.randn(100) +r1 = r + 1 +labels = ['Rabbits', 'Frogs'] +H = plt.hist([r, r1], label=labels) +containers = H[-1] +leg = plt.legend(frameon=False) +plt.title("From a web browser, click on the legend\n" + "marker to toggle the corresponding histogram.") + + +# --- Add ids to the svg objects we'll modify + +hist_patches = {} +for ic, c in enumerate(containers): + hist_patches[f'hist_{ic}'] = [] + for il, element in enumerate(c): + element.set_gid(f'hist_{ic}_patch_{il}') + hist_patches[f'hist_{ic}'].append(f'hist_{ic}_patch_{il}') + +# Set ids for the legend patches +for i, t in enumerate(leg.get_patches()): + t.set_gid(f'leg_patch_{i}') + +# Set ids for the text patches +for i, t in enumerate(leg.get_texts()): + t.set_gid(f'leg_text_{i}') + +# Save SVG in a fake file object. +f = BytesIO() +plt.savefig(f, format="svg") + +# Create XML tree from the SVG file. +tree, xmlid = ET.XMLID(f.getvalue()) + + +# --- Add interactivity --- + +# Add attributes to the patch objects. +for i, t in enumerate(leg.get_patches()): + el = xmlid[f'leg_patch_{i}'] + el.set('cursor', 'pointer') + el.set('onclick', "toggle_hist(this)") + +# Add attributes to the text objects. +for i, t in enumerate(leg.get_texts()): + el = xmlid[f'leg_text_{i}'] + el.set('cursor', 'pointer') + el.set('onclick', "toggle_hist(this)") + +# Create script defining the function `toggle_hist`. +# We create a global variable `container` that stores the patches id +# belonging to each histogram. Then a function "toggle_element" sets the +# visibility attribute of all patches of each histogram and the opacity +# of the marker itself. + +script = """ + +""" % json.dumps(hist_patches) + +# Add a transition effect +css = tree.find('.//{http://www.w3.org/2000/svg}style') +css.text = css.text + "g {-webkit-transition:opacity 0.4s ease-out;" + \ + "-moz-transition:opacity 0.4s ease-out;}" + +# Insert the script and save to file. +tree.insert(0, ET.XML(script)) + +ET.ElementTree(tree).write("svg_histogram.svg") diff --git a/galleries/examples/user_interfaces/svg_tooltip_sgskip.py b/galleries/examples/user_interfaces/svg_tooltip_sgskip.py new file mode 100644 index 000000000000..7068431b45e8 --- /dev/null +++ b/galleries/examples/user_interfaces/svg_tooltip_sgskip.py @@ -0,0 +1,109 @@ +""" +=========== +SVG Tooltip +=========== + +This example shows how to create a tooltip that will show up when +hovering over a matplotlib patch. + +Although it is possible to create the tooltip from CSS or javascript, +here we create it in matplotlib and simply toggle its visibility on +when hovering over the patch. This approach provides total control over +the tooltip placement and appearance, at the expense of more code up +front. + +The alternative approach would be to put the tooltip content in ``title`` +attributes of SVG objects. Then, using an existing js/CSS library, it +would be relatively straightforward to create the tooltip in the +browser. The content would be dictated by the ``title`` attribute, and +the appearance by the CSS. + + +:author: David Huard +""" + + +from io import BytesIO +import xml.etree.ElementTree as ET + +import matplotlib.pyplot as plt + +ET.register_namespace("", "http://www.w3.org/2000/svg") + +fig, ax = plt.subplots() + +# Create patches to which tooltips will be assigned. +rect1 = plt.Rectangle((10, -20), 10, 5, fc='blue') +rect2 = plt.Rectangle((-20, 15), 10, 5, fc='green') + +shapes = [rect1, rect2] +labels = ['This is a blue rectangle.', 'This is a green rectangle'] + +for i, (item, label) in enumerate(zip(shapes, labels)): + patch = ax.add_patch(item) + annotate = ax.annotate(labels[i], xy=item.get_xy(), xytext=(0, 0), + textcoords='offset points', color='w', ha='center', + fontsize=8, bbox=dict(boxstyle='round, pad=.5', + fc=(.1, .1, .1, .92), + ec=(1., 1., 1.), lw=1, + zorder=1)) + + ax.add_patch(patch) + patch.set_gid(f'mypatch_{i:03d}') + annotate.set_gid(f'mytooltip_{i:03d}') + +# Save the figure in a fake file object +ax.set_xlim(-30, 30) +ax.set_ylim(-30, 30) +ax.set_aspect('equal') + +f = BytesIO() +plt.savefig(f, format="svg") + +# --- Add interactivity --- + +# Create XML tree from the SVG file. +tree, xmlid = ET.XMLID(f.getvalue()) +tree.set('onload', 'init(event)') + +for i in shapes: + # Get the index of the shape + index = shapes.index(i) + # Hide the tooltips + tooltip = xmlid[f'mytooltip_{index:03d}'] + tooltip.set('visibility', 'hidden') + # Assign onmouseover and onmouseout callbacks to patches. + mypatch = xmlid[f'mypatch_{index:03d}'] + mypatch.set('onmouseover', "ShowTooltip(this)") + mypatch.set('onmouseout', "HideTooltip(this)") + +# This is the script defining the ShowTooltip and HideTooltip functions. +script = """ + + """ + +# Insert the script at the top of the file and save it. +tree.insert(0, ET.XML(script)) +ET.ElementTree(tree).write('svg_tooltip.svg') diff --git a/galleries/examples/user_interfaces/toolmanager_sgskip.py b/galleries/examples/user_interfaces/toolmanager_sgskip.py new file mode 100644 index 000000000000..14fc671a5301 --- /dev/null +++ b/galleries/examples/user_interfaces/toolmanager_sgskip.py @@ -0,0 +1,92 @@ +""" +============ +Tool Manager +============ + +This example demonstrates how to + +* modify the Toolbar +* create tools +* add tools +* remove tools + +using `matplotlib.backend_managers.ToolManager`. +""" + +import matplotlib.pyplot as plt + +from matplotlib.backend_tools import ToolBase, ToolToggleBase + +plt.rcParams['toolbar'] = 'toolmanager' + + +class ListTools(ToolBase): + """List all the tools controlled by the `ToolManager`.""" + default_keymap = 'm' # keyboard shortcut + description = 'List Tools' + + def trigger(self, *args, **kwargs): + print('_' * 80) + fmt_tool = "{:12} {:45} {}".format + print(fmt_tool('Name (id)', 'Tool description', 'Keymap')) + print('-' * 80) + tools = self.toolmanager.tools + for name in sorted(tools): + if not tools[name].description: + continue + keys = ', '.join(sorted(self.toolmanager.get_tool_keymap(name))) + print(fmt_tool(name, tools[name].description, keys)) + print('_' * 80) + fmt_active_toggle = "{!s:12} {!s:45}".format + print("Active Toggle tools") + print(fmt_active_toggle("Group", "Active")) + print('-' * 80) + for group, active in self.toolmanager.active_toggle.items(): + print(fmt_active_toggle(group, active)) + + +class GroupHideTool(ToolToggleBase): + """Show lines with a given gid.""" + default_keymap = 'S' + description = 'Show by gid' + default_toggled = True + + def __init__(self, *args, gid, **kwargs): + self.gid = gid + super().__init__(*args, **kwargs) + + def enable(self, *args): + self.set_lines_visibility(True) + + def disable(self, *args): + self.set_lines_visibility(False) + + def set_lines_visibility(self, state): + for ax in self.figure.get_axes(): + for line in ax.get_lines(): + if line.get_gid() == self.gid: + line.set_visible(state) + self.figure.canvas.draw() + + +fig = plt.figure() +plt.plot([1, 2, 3], gid='mygroup') +plt.plot([2, 3, 4], gid='unknown') +plt.plot([3, 2, 1], gid='mygroup') + +# Add the custom tools that we created +fig.canvas.manager.toolmanager.add_tool('List', ListTools) +fig.canvas.manager.toolmanager.add_tool('Show', GroupHideTool, gid='mygroup') + +# Add an existing tool to new group `foo`. +# It can be added as many times as we want +fig.canvas.manager.toolbar.add_tool('zoom', 'foo') + +# Remove the forward button +fig.canvas.manager.toolmanager.remove_tool('forward') + +# To add a custom tool to the toolbar at specific location inside +# the navigation group +fig.canvas.manager.toolbar.add_tool('Show', 'navigation', 1) + +plt.show() diff --git a/galleries/examples/user_interfaces/web_application_server_sgskip.py b/galleries/examples/user_interfaces/web_application_server_sgskip.py new file mode 100644 index 000000000000..f125916db54b --- /dev/null +++ b/galleries/examples/user_interfaces/web_application_server_sgskip.py @@ -0,0 +1,66 @@ +""" +========================================= +Embed in a web application server (Flask) +========================================= + +When using Matplotlib in a web server it is strongly recommended to not use +pyplot (pyplot maintains references to the opened figures to make +`~.pyplot.show` work, but this will cause memory leaks unless the +figures are properly closed). + +Since Matplotlib 3.1, one can directly create figures using the `.Figure` +constructor and save them to in-memory buffers. In older versions, it was +necessary to explicitly instantiate an Agg canvas (see e.g. +:doc:`/gallery/user_interfaces/canvasagg`). + +The following example uses Flask_, but other frameworks work similarly: + +.. _Flask: https://flask.palletsprojects.com + +""" + +import base64 +from io import BytesIO + +from flask import Flask + +from matplotlib.figure import Figure + +app = Flask(__name__) + + +@app.route("/") +def hello(): + # Generate the figure **without using pyplot**. + fig = Figure() + ax = fig.subplots() + ax.plot([1, 2]) + # Save it to a temporary buffer. + buf = BytesIO() + fig.savefig(buf, format="png") + # Embed the result in the html output. + data = base64.b64encode(buf.getbuffer()).decode("ascii") + return f"" + +# %% +# +# Since the above code is a Flask application, it should be run using the +# `flask command-line tool `_: +# run +# +# .. code-block:: console +# +# flask --app web_application_server_sgskip run +# +# from the directory containing this script. +# +# +# Clickable images for HTML +# ------------------------- +# +# Andrew Dalke of `Dalke Scientific `_ +# has written a nice `article +# `_ +# on how to make html click maps with Matplotlib agg PNGs. We would +# also like to add this functionality to SVG. If you are interested in +# contributing to these efforts that would be great. diff --git a/galleries/examples/user_interfaces/wxcursor_demo_sgskip.py b/galleries/examples/user_interfaces/wxcursor_demo_sgskip.py new file mode 100644 index 000000000000..e2e7348f1c3c --- /dev/null +++ b/galleries/examples/user_interfaces/wxcursor_demo_sgskip.py @@ -0,0 +1,68 @@ +""" +================== +Add a cursor in WX +================== + +Example to draw a cursor and report the data coords in wx. +""" + +import wx + +import numpy as np + +from matplotlib.backends.backend_wx import NavigationToolbar2Wx +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas +from matplotlib.figure import Figure + + +class CanvasFrame(wx.Frame): + def __init__(self, ): + super().__init__(None, -1, 'CanvasFrame', size=(550, 350)) + + self.figure = Figure() + self.axes = self.figure.add_subplot() + t = np.arange(0.0, 3.0, 0.01) + s = np.sin(2*np.pi*t) + + self.axes.plot(t, s) + self.axes.set_xlabel('t') + self.axes.set_ylabel('sin(t)') + self.figure_canvas = FigureCanvas(self, -1, self.figure) + + # Note that event is a MplEvent + self.figure_canvas.mpl_connect( + 'motion_notify_event', self.UpdateStatusBar) + self.figure_canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor) + + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.sizer.Add(self.figure_canvas, 1, wx.LEFT | wx.TOP | wx.GROW) + self.SetSizer(self.sizer) + self.Fit() + + self.statusBar = wx.StatusBar(self, -1) + self.SetStatusBar(self.statusBar) + + self.toolbar = NavigationToolbar2Wx(self.figure_canvas) + self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) + self.toolbar.Show() + + def ChangeCursor(self, event): + self.figure_canvas.SetCursor(wx.Cursor(wx.CURSOR_BULLSEYE)) + + def UpdateStatusBar(self, event): + if event.inaxes: + self.statusBar.SetStatusText(f"x={event.xdata} y={event.ydata}") + + +class App(wx.App): + def OnInit(self): + """Create the main window and insert the custom frame.""" + frame = CanvasFrame() + self.SetTopWindow(frame) + frame.Show(True) + return True + + +if __name__ == '__main__': + app = App() + app.MainLoop() diff --git a/galleries/examples/widgets/README.txt b/galleries/examples/widgets/README.txt new file mode 100644 index 000000000000..aaa2eca9f53c --- /dev/null +++ b/galleries/examples/widgets/README.txt @@ -0,0 +1,5 @@ +Widgets +======= + +Examples of how to write primitive, but GUI agnostic, widgets in +matplotlib diff --git a/galleries/examples/widgets/annotated_cursor.py b/galleries/examples/widgets/annotated_cursor.py new file mode 100644 index 000000000000..9fec9b6568bc --- /dev/null +++ b/galleries/examples/widgets/annotated_cursor.py @@ -0,0 +1,356 @@ +""" +================ +Annotated cursor +================ + +Display a data cursor including a text box, which shows the plot point close +to the mouse pointer. + +The new cursor inherits from `~matplotlib.widgets.Cursor` and demonstrates the +creation of new widgets and their event callbacks. + +See also the :doc:`cross hair cursor +`, which implements a cursor tracking the +plotted data, but without using inheritance and without displaying the +currently tracked coordinates. + +.. note:: + The figure related to this example does not show the cursor, because that + figure is automatically created in a build queue, where the first mouse + movement, which triggers the cursor creation, is missing. + +""" +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.backend_bases import MouseEvent +from matplotlib.widgets import Cursor + + +class AnnotatedCursor(Cursor): + """ + A crosshair cursor like `~matplotlib.widgets.Cursor` with a text showing \ + the current coordinates. + + For the cursor to remain responsive you must keep a reference to it. + The data of the axis specified as *dataaxis* must be in ascending + order. Otherwise, the `numpy.searchsorted` call might fail and the text + disappears. You can satisfy the requirement by sorting the data you plot. + Usually the data is already sorted (if it was created e.g. using + `numpy.linspace`), but e.g. scatter plots might cause this problem. + The cursor sticks to the plotted line. + + Parameters + ---------- + line : `matplotlib.lines.Line2D` + The plot line from which the data coordinates are displayed. + + numberformat : `python format string `_, optional, default: "{0:.4g};{1:.4g}" + The displayed text is created by calling *format()* on this string + with the two coordinates. + + offset : (float, float) default: (5, 5) + The offset in display (pixel) coordinates of the text position + relative to the cross-hair. + + dataaxis : {"x", "y"}, optional, default: "x" + If "x" is specified, the vertical cursor line sticks to the mouse + pointer. The horizontal cursor line sticks to *line* + at that x value. The text shows the data coordinates of *line* + at the pointed x value. If you specify "y", it works in the opposite + manner. But: For the "y" value, where the mouse points to, there might + be multiple matching x values, if the plotted function is not biunique. + Cursor and text coordinate will always refer to only one x value. + So if you use the parameter value "y", ensure that your function is + biunique. + + Other Parameters + ---------------- + textprops : `matplotlib.text` properties as dictionary + Specifies the appearance of the rendered text object. + + **cursorargs : `matplotlib.widgets.Cursor` properties + Arguments passed to the internal `~matplotlib.widgets.Cursor` instance. + The `matplotlib.axes.Axes` argument is mandatory! The parameter + *useblit* can be set to *True* in order to achieve faster rendering. + + """ + + def __init__(self, line, numberformat="{0:.4g};{1:.4g}", offset=(5, 5), + dataaxis='x', textprops=None, **cursorargs): + if textprops is None: + textprops = {} + # The line object, for which the coordinates are displayed + self.line = line + # The format string, on which .format() is called for creating the text + self.numberformat = numberformat + # Text position offset + self.offset = np.array(offset) + # The axis in which the cursor position is looked up + self.dataaxis = dataaxis + + # First call baseclass constructor. + # Draws cursor and remembers background for blitting. + # Saves ax as class attribute. + super().__init__(**cursorargs) + + # Default value for position of text. + self.set_position(self.line.get_xdata()[0], self.line.get_ydata()[0]) + # Create invisible animated text + self.text = self.ax.text( + self.ax.get_xbound()[0], + self.ax.get_ybound()[0], + "0, 0", + animated=bool(self.useblit), + visible=False, **textprops) + # The position at which the cursor was last drawn + self.lastdrawnplotpoint = None + + def onmove(self, event): + """ + Overridden draw callback for cursor. Called when moving the mouse. + """ + + # Leave method under the same conditions as in overridden method + if self.ignore(event): + self.lastdrawnplotpoint = None + return + if not self.canvas.widgetlock.available(self): + self.lastdrawnplotpoint = None + return + + # If the mouse left drawable area, we now make the text invisible. + # Baseclass will redraw complete canvas after, which makes both text + # and cursor disappear. + if event.inaxes != self.ax: + self.lastdrawnplotpoint = None + self.text.set_visible(False) + super().onmove(event) + return + + # Get the coordinates, which should be displayed as text, + # if the event coordinates are valid. + plotpoint = None + if event.xdata is not None and event.ydata is not None: + # Get plot point related to current x position. + # These coordinates are displayed in text. + plotpoint = self.set_position(event.xdata, event.ydata) + # Modify event, such that the cursor is displayed on the + # plotted line, not at the mouse pointer, + # if the returned plot point is valid + if plotpoint is not None: + event.xdata = plotpoint[0] + event.ydata = plotpoint[1] + + # If the plotpoint is given, compare to last drawn plotpoint and + # return if they are the same. + # Skip even the call of the base class, because this would restore the + # background, draw the cursor lines and would leave us the job to + # re-draw the text. + if plotpoint is not None and plotpoint == self.lastdrawnplotpoint: + return + + # Baseclass redraws canvas and cursor. Due to blitting, + # the added text is removed in this call, because the + # background is redrawn. + super().onmove(event) + + # Check if the display of text is still necessary. + # If not, just return. + # This behaviour is also cloned from the base class. + if not self.get_active() or not self.visible: + return + + # Draw the widget, if event coordinates are valid. + if plotpoint is not None: + # Update position and displayed text. + # Position: Where the event occurred. + # Text: Determined by set_position() method earlier + # Position is transformed to pixel coordinates, + # an offset is added there and this is transformed back. + temp = [event.xdata, event.ydata] + temp = self.ax.transData.transform(temp) + temp = temp + self.offset + temp = self.ax.transData.inverted().transform(temp) + self.text.set_position(temp) + self.text.set_text(self.numberformat.format(*plotpoint)) + self.text.set_visible(self.visible) + + # Tell base class, that we have drawn something. + # Baseclass needs to know, that it needs to restore a clean + # background, if the cursor leaves our figure context. + self.needclear = True + + # Remember the recently drawn cursor position, so events for the + # same position (mouse moves slightly between two plot points) + # can be skipped + self.lastdrawnplotpoint = plotpoint + # otherwise, make text invisible + else: + self.text.set_visible(False) + + # Draw changes. Cannot use _update method of baseclass, + # because it would first restore the background, which + # is done already and is not necessary. + if self.useblit: + self.ax.draw_artist(self.text) + self.canvas.blit(self.ax.bbox) + else: + # If blitting is deactivated, the overridden _update call made + # by the base class immediately returned. + # We still have to draw the changes. + self.canvas.draw_idle() + + def set_position(self, xpos, ypos): + """ + Finds the coordinates, which have to be shown in text. + + The behaviour depends on the *dataaxis* attribute. Function looks + up the matching plot coordinate for the given mouse position. + + Parameters + ---------- + xpos : float + The current x position of the cursor in data coordinates. + Important if *dataaxis* is set to 'x'. + ypos : float + The current y position of the cursor in data coordinates. + Important if *dataaxis* is set to 'y'. + + Returns + ------- + ret : {2D array-like, None} + The coordinates which should be displayed. + *None* is the fallback value. + """ + + # Get plot line data + xdata = self.line.get_xdata() + ydata = self.line.get_ydata() + + # The dataaxis attribute decides, in which axis we look up which cursor + # coordinate. + if self.dataaxis == 'x': + pos = xpos + data = xdata + lim = self.ax.get_xlim() + elif self.dataaxis == 'y': + pos = ypos + data = ydata + lim = self.ax.get_ylim() + else: + raise ValueError(f"The data axis specifier {self.dataaxis} should " + f"be 'x' or 'y'") + + # If position is valid and in valid plot data range. + if pos is not None and lim[0] <= pos <= lim[-1]: + # Find closest x value in sorted x vector. + # This requires the plotted data to be sorted. + index = np.searchsorted(data, pos) + # Return none, if this index is out of range. + if index < 0 or index >= len(data): + return None + # Return plot point as tuple. + return (xdata[index], ydata[index]) + + # Return none if there is no good related point for this x position. + return None + + def clear(self, event): + """ + Overridden clear callback for cursor, called before drawing the figure. + """ + + # The base class saves the clean background for blitting. + # Text and cursor are invisible, + # until the first mouse move event occurs. + super().clear(event) + if self.ignore(event): + return + self.text.set_visible(False) + + def _update(self): + """ + Overridden method for either blitting or drawing the widget canvas. + + Passes call to base class if blitting is activated, only. + In other cases, one draw_idle call is enough, which is placed + explicitly in this class (see *onmove()*). + In that case, `~matplotlib.widgets.Cursor` is not supposed to draw + something using this method. + """ + + if self.useblit: + super()._update() + + +fig, ax = plt.subplots(figsize=(8, 6)) +ax.set_title("Cursor Tracking x Position") + +x = np.linspace(-5, 5, 1000) +y = x**2 + +line, = ax.plot(x, y) +ax.set_xlim(-5, 5) +ax.set_ylim(0, 25) + +# A minimum call +# Set useblit=True on most backends for enhanced performance +# and pass the ax parameter to the Cursor base class. +# cursor = AnnotatedCursor(line=lin[0], ax=ax, useblit=True) + +# A more advanced call. Properties for text and lines are passed. +# Watch the passed color names and the color of cursor line and text, to +# relate the passed options to graphical elements. +# The dataaxis parameter is still the default. +cursor = AnnotatedCursor( + line=line, + numberformat="{0:.2f}\n{1:.2f}", + dataaxis='x', offset=[10, 10], + textprops={'color': 'blue', 'fontweight': 'bold'}, + ax=ax, + useblit=True, + color='red', + linewidth=2) + +# Simulate a mouse move to (-2, 10), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((-2, 10)) +)._process() + +plt.show() + +# %% +# Trouble with non-biunique functions +# ----------------------------------- +# A call demonstrating problems with the *dataaxis=y* parameter. +# The text now looks up the matching x value for the current cursor y position +# instead of vice versa. Hover your cursor to y=4. There are two x values +# producing this y value: -2 and 2. The function is only unique, +# but not biunique. Only one value is shown in the text. + +fig, ax = plt.subplots(figsize=(8, 6)) +ax.set_title("Cursor Tracking y Position") + +line, = ax.plot(x, y) +ax.set_xlim(-5, 5) +ax.set_ylim(0, 25) + +cursor = AnnotatedCursor( + line=line, + numberformat="{0:.2f}\n{1:.2f}", + dataaxis='y', offset=[10, 10], + textprops={'color': 'blue', 'fontweight': 'bold'}, + ax=ax, + useblit=True, + color='red', linewidth=2) + +# Simulate a mouse move to (-2, 10), needed for online docs +t = ax.transData +MouseEvent( + "motion_notify_event", ax.figure.canvas, *t.transform((-2, 10)) +)._process() + +plt.show() diff --git a/galleries/examples/widgets/buttons.py b/galleries/examples/widgets/buttons.py new file mode 100644 index 000000000000..2aef798399f4 --- /dev/null +++ b/galleries/examples/widgets/buttons.py @@ -0,0 +1,60 @@ +""" +======= +Buttons +======= + +Constructing a simple button GUI to modify a sine wave. + +The ``next`` and ``previous`` button widget helps visualize the wave with +new frequencies. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import Button + +freqs = np.arange(2, 20, 3) + +fig, ax = plt.subplots() +fig.subplots_adjust(bottom=0.2) +t = np.arange(0.0, 1.0, 0.001) +s = np.sin(2*np.pi*freqs[0]*t) +l, = ax.plot(t, s, lw=2) + + +class Index: + ind = 0 + + def next(self, event): + self.ind += 1 + i = self.ind % len(freqs) + ydata = np.sin(2*np.pi*freqs[i]*t) + l.set_ydata(ydata) + plt.draw() + + def prev(self, event): + self.ind -= 1 + i = self.ind % len(freqs) + ydata = np.sin(2*np.pi*freqs[i]*t) + l.set_ydata(ydata) + plt.draw() + +callback = Index() +axprev = fig.add_axes((0.7, 0.05, 0.1, 0.075)) +axnext = fig.add_axes((0.81, 0.05, 0.1, 0.075)) +bnext = Button(axnext, 'Next') +bnext.on_clicked(callback.next) +bprev = Button(axprev, 'Previous') +bprev.on_clicked(callback.prev) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.Button` diff --git a/galleries/examples/widgets/check_buttons.py b/galleries/examples/widgets/check_buttons.py new file mode 100644 index 000000000000..2fe1eafe29db --- /dev/null +++ b/galleries/examples/widgets/check_buttons.py @@ -0,0 +1,63 @@ +""" +============= +Check buttons +============= + +Turning visual elements on and off with check buttons. + +This program shows the use of `.CheckButtons` which is similar to +check boxes. There are 3 different sine waves shown, and we can choose which +waves are displayed with the check buttons. + +Check buttons may be styled using the *check_props*, *frame_props*, and *label_props* +parameters. The parameters each take a dictionary with keys of artist property names and +values of lists of settings with length matching the number of buttons. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import CheckButtons + +t = np.arange(0.0, 2.0, 0.01) +s0 = np.sin(2*np.pi*t) +s1 = np.sin(4*np.pi*t) +s2 = np.sin(6*np.pi*t) + +fig, ax = plt.subplots() +l0, = ax.plot(t, s0, visible=False, lw=2, color='black', label='1 Hz') +l1, = ax.plot(t, s1, lw=2, color='red', label='2 Hz') +l2, = ax.plot(t, s2, lw=2, color='blue', label='3 Hz') + +lines_by_label = {l.get_label(): l for l in [l0, l1, l2]} +line_colors = [l.get_color() for l in lines_by_label.values()] + +# Make checkbuttons with all plotted lines with correct visibility +rax = ax.inset_axes([0.0, 0.0, 0.12, 0.2]) +check = CheckButtons( + ax=rax, + labels=lines_by_label.keys(), + actives=[l.get_visible() for l in lines_by_label.values()], + label_props={'color': line_colors}, + frame_props={'edgecolor': line_colors}, + check_props={'facecolor': line_colors}, +) + + +def callback(label): + ln = lines_by_label[label] + ln.set_visible(not ln.get_visible()) + ln.figure.canvas.draw_idle() + +check.on_clicked(callback) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.CheckButtons` diff --git a/galleries/examples/widgets/cursor.py b/galleries/examples/widgets/cursor.py new file mode 100644 index 000000000000..af7d821fbf10 --- /dev/null +++ b/galleries/examples/widgets/cursor.py @@ -0,0 +1,34 @@ +""" +====== +Cursor +====== + +""" +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import Cursor + +# Fixing random state for reproducibility +np.random.seed(19680801) + +fig, ax = plt.subplots(figsize=(8, 6)) + +x, y = 4*(np.random.rand(2, 100) - .5) +ax.plot(x, y, 'o') +ax.set_xlim(-2, 2) +ax.set_ylim(-2, 2) + +# Set useblit=True on most backends for enhanced performance. +cursor = Cursor(ax, useblit=True, color='red', linewidth=2) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.Cursor` diff --git a/galleries/examples/widgets/lasso_selector_demo_sgskip.py b/galleries/examples/widgets/lasso_selector_demo_sgskip.py new file mode 100644 index 000000000000..fd2459be4f4f --- /dev/null +++ b/galleries/examples/widgets/lasso_selector_demo_sgskip.py @@ -0,0 +1,111 @@ +""" +============== +Lasso Selector +============== + +Interactively selecting data points with the lasso tool. + +This examples plots a scatter plot. You can then select a few points by drawing +a lasso loop around the points on the graph. To draw, just click +on the graph, hold, and drag it around the points you need to select. +""" + + +import numpy as np + +from matplotlib.path import Path +from matplotlib.widgets import LassoSelector + + +class SelectFromCollection: + """ + Select indices from a matplotlib collection using `LassoSelector`. + + Selected indices are saved in the `ind` attribute. This tool fades out the + points that are not part of the selection (i.e., reduces their alpha + values). If your collection has alpha < 1, this tool will permanently + alter the alpha values. + + Note that this tool selects collection objects based on their *origins* + (i.e., `offsets`). + + Parameters + ---------- + ax : `~matplotlib.axes.Axes` + Axes to interact with. + collection : `matplotlib.collections.Collection` subclass + Collection you want to select from. + alpha_other : 0 <= float <= 1 + To highlight a selection, this tool sets all selected points to an + alpha value of 1 and non-selected points to *alpha_other*. + """ + + def __init__(self, ax, collection, alpha_other=0.3): + self.canvas = ax.figure.canvas + self.collection = collection + self.alpha_other = alpha_other + + self.xys = collection.get_offsets() + self.Npts = len(self.xys) + + # Ensure that we have separate colors for each object + self.fc = collection.get_facecolors() + if len(self.fc) == 0: + raise ValueError('Collection must have a facecolor') + elif len(self.fc) == 1: + self.fc = np.tile(self.fc, (self.Npts, 1)) + + self.lasso = LassoSelector(ax, onselect=self.onselect) + self.ind = [] + + def onselect(self, verts): + path = Path(verts) + self.ind = np.nonzero(path.contains_points(self.xys))[0] + self.fc[:, -1] = self.alpha_other + self.fc[self.ind, -1] = 1 + self.collection.set_facecolors(self.fc) + self.canvas.draw_idle() + + def disconnect(self): + self.lasso.disconnect_events() + self.fc[:, -1] = 1 + self.collection.set_facecolors(self.fc) + self.canvas.draw_idle() + + +if __name__ == '__main__': + import matplotlib.pyplot as plt + + # Fixing random state for reproducibility + np.random.seed(19680801) + + data = np.random.rand(100, 2) + + subplot_kw = dict(xlim=(0, 1), ylim=(0, 1), autoscale_on=False) + fig, ax = plt.subplots(subplot_kw=subplot_kw) + + pts = ax.scatter(data[:, 0], data[:, 1], s=80) + selector = SelectFromCollection(ax, pts) + + def accept(event): + if event.key == "enter": + print("Selected points:") + print(selector.xys[selector.ind]) + selector.disconnect() + ax.set_title("") + fig.canvas.draw() + + fig.canvas.mpl_connect("key_press_event", accept) + ax.set_title("Press enter to accept selected points.") + + plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.LassoSelector` +# - `matplotlib.path.Path` diff --git a/galleries/examples/widgets/menu.py b/galleries/examples/widgets/menu.py new file mode 100644 index 000000000000..e948d5e00863 --- /dev/null +++ b/galleries/examples/widgets/menu.py @@ -0,0 +1,142 @@ +""" +==== +Menu +==== + +Using texts to construct a simple menu. +""" + +from dataclasses import dataclass + +import matplotlib.pyplot as plt + +import matplotlib.artist as artist +import matplotlib.patches as patches +from matplotlib.typing import ColorType + + +@dataclass +class ItemProperties: + fontsize: float = 14 + labelcolor: ColorType = 'black' + bgcolor: ColorType = 'yellow' + alpha: float = 1.0 + + +class MenuItem(artist.Artist): + padx = 0.05 # inches + pady = 0.05 + + def __init__(self, fig, labelstr, props=None, hoverprops=None, + on_select=None): + super().__init__() + + self.set_figure(fig) + self.labelstr = labelstr + + self.props = props if props is not None else ItemProperties() + self.hoverprops = ( + hoverprops if hoverprops is not None else ItemProperties()) + if self.props.fontsize != self.hoverprops.fontsize: + raise NotImplementedError( + 'support for different font sizes not implemented') + + self.on_select = on_select + + # specify coordinates in inches. + self.label = fig.text(0, 0, labelstr, transform=fig.dpi_scale_trans, + size=props.fontsize) + self.text_bbox = self.label.get_window_extent( + fig.canvas.get_renderer()) + self.text_bbox = fig.dpi_scale_trans.inverted().transform_bbox(self.text_bbox) + + self.rect = patches.Rectangle( + (0, 0), 1, 1, transform=fig.dpi_scale_trans + ) # Will be updated later. + + self.set_hover_props(False) + + fig.canvas.mpl_connect('button_release_event', self.check_select) + + def check_select(self, event): + over, _ = self.rect.contains(event) + if not over: + return + if self.on_select is not None: + self.on_select(self) + + def set_extent(self, x, y, w, h, depth): + self.rect.set(x=x, y=y, width=w, height=h) + self.label.set(position=(x + self.padx, y + depth + self.pady / 2)) + self.hover = False + + def draw(self, renderer): + self.rect.draw(renderer) + self.label.draw(renderer) + + def set_hover_props(self, b): + props = self.hoverprops if b else self.props + self.label.set(color=props.labelcolor) + self.rect.set(facecolor=props.bgcolor, alpha=props.alpha) + + def set_hover(self, event): + """ + Update the hover status of event and return whether it was changed. + """ + b, _ = self.rect.contains(event) + changed = (b != self.hover) + if changed: + self.set_hover_props(b) + self.hover = b + return changed + + +class Menu: + def __init__(self, fig, menuitems): + self.figure = fig + + self.menuitems = menuitems + + maxw = max(item.text_bbox.width for item in menuitems) + maxh = max(item.text_bbox.height for item in menuitems) + depth = max(-item.text_bbox.y0 for item in menuitems) + + x0 = 1 + y0 = 4 + + width = maxw + 2 * MenuItem.padx + height = maxh + MenuItem.pady + + for item in menuitems: + left = x0 + bottom = y0 - maxh - MenuItem.pady + + item.set_extent(left, bottom, width, height, depth) + + fig.artists.append(item) + y0 -= maxh + MenuItem.pady + + fig.canvas.mpl_connect('motion_notify_event', self.on_move) + + def on_move(self, event): + if any(item.set_hover(event) for item in self.menuitems): + self.figure.canvas.draw() + + +fig = plt.figure() +fig.subplots_adjust(left=0.3) +props = ItemProperties(labelcolor='black', bgcolor='yellow', + fontsize=15, alpha=0.2) +hoverprops = ItemProperties(labelcolor='white', bgcolor='blue', + fontsize=15, alpha=0.2) + +menuitems = [] +for label in ('open', 'close', 'save', 'save as', 'quit'): + def on_select(item): + print(f'you selected {item.labelstr}') + item = MenuItem(fig, label, props=props, hoverprops=hoverprops, + on_select=on_select) + menuitems.append(item) + +menu = Menu(fig, menuitems) +plt.show() diff --git a/galleries/examples/widgets/mouse_cursor.py b/galleries/examples/widgets/mouse_cursor.py new file mode 100644 index 000000000000..2ac1b10dac58 --- /dev/null +++ b/galleries/examples/widgets/mouse_cursor.py @@ -0,0 +1,46 @@ +""" +============ +Mouse Cursor +============ + +This example sets an alternative cursor on a figure canvas. + +Note, this is an interactive example, and must be run to see the effect. +""" + +import matplotlib.pyplot as plt + +from matplotlib.backend_tools import Cursors + +fig, axs = plt.subplots(len(Cursors), figsize=(6, len(Cursors) + 0.5), + gridspec_kw={'hspace': 0}) +fig.suptitle('Hover over an Axes to see alternate Cursors') + +for cursor, ax in zip(Cursors, axs): + ax.cursor_to_use = cursor + ax.text(0.5, 0.5, cursor.name, + horizontalalignment='center', verticalalignment='center') + ax.set(xticks=[], yticks=[]) + + +def hover(event): + if fig.canvas.widgetlock.locked(): + # Don't do anything if the zoom/pan tools have been enabled. + return + + fig.canvas.set_cursor( + event.inaxes.cursor_to_use if event.inaxes else Cursors.POINTER) + + +fig.canvas.mpl_connect('motion_notify_event', hover) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.backend_bases.FigureCanvasBase.set_cursor` diff --git a/galleries/examples/widgets/multicursor.py b/galleries/examples/widgets/multicursor.py new file mode 100644 index 000000000000..9123c31f63f4 --- /dev/null +++ b/galleries/examples/widgets/multicursor.py @@ -0,0 +1,39 @@ +""" +=========== +Multicursor +=========== + +Showing a cursor on multiple plots simultaneously. + +This example generates three Axes split over two different figures. On +hovering the cursor over data in one subplot, the values of that datapoint are +shown in all Axes. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import MultiCursor + +t = np.arange(0.0, 2.0, 0.01) +s1 = np.sin(2*np.pi*t) +s2 = np.sin(3*np.pi*t) +s3 = np.sin(4*np.pi*t) + +fig, (ax1, ax2) = plt.subplots(2, sharex=True) +ax1.plot(t, s1) +ax2.plot(t, s2) +fig, ax3 = plt.subplots() +ax3.plot(t, s3) + +multi = MultiCursor((ax1, ax2, ax3), color='r', lw=1) +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.MultiCursor` diff --git a/galleries/examples/widgets/polygon_selector_demo.py b/galleries/examples/widgets/polygon_selector_demo.py new file mode 100644 index 000000000000..7efd1429d094 --- /dev/null +++ b/galleries/examples/widgets/polygon_selector_demo.py @@ -0,0 +1,103 @@ +""" +======================================================= +Select indices from a collection using polygon selector +======================================================= + +Shows how one can select indices of a polygon interactively. +""" + +import numpy as np + +from matplotlib.path import Path +from matplotlib.widgets import PolygonSelector + + +class SelectFromCollection: + """ + Select indices from a matplotlib collection using `PolygonSelector`. + + Selected indices are saved in the `ind` attribute. This tool fades out the + points that are not part of the selection (i.e., reduces their alpha + values). If your collection has alpha < 1, this tool will permanently + alter the alpha values. + + Note that this tool selects collection objects based on their *origins* + (i.e., `offsets`). + + Parameters + ---------- + ax : `~matplotlib.axes.Axes` + Axes to interact with. + collection : `matplotlib.collections.Collection` subclass + Collection you want to select from. + alpha_other : 0 <= float <= 1 + To highlight a selection, this tool sets all selected points to an + alpha value of 1 and non-selected points to *alpha_other*. + """ + + def __init__(self, ax, collection, alpha_other=0.3): + self.canvas = ax.figure.canvas + self.collection = collection + self.alpha_other = alpha_other + + self.xys = collection.get_offsets() + self.Npts = len(self.xys) + + # Ensure that we have separate colors for each object + self.fc = collection.get_facecolors() + if len(self.fc) == 0: + raise ValueError('Collection must have a facecolor') + elif len(self.fc) == 1: + self.fc = np.tile(self.fc, (self.Npts, 1)) + + self.poly = PolygonSelector(ax, self.onselect, draw_bounding_box=True) + self.ind = [] + + def onselect(self, verts): + path = Path(verts) + self.ind = np.nonzero(path.contains_points(self.xys))[0] + self.fc[:, -1] = self.alpha_other + self.fc[self.ind, -1] = 1 + self.collection.set_facecolors(self.fc) + self.canvas.draw_idle() + + def disconnect(self): + self.poly.disconnect_events() + self.fc[:, -1] = 1 + self.collection.set_facecolors(self.fc) + self.canvas.draw_idle() + + +if __name__ == '__main__': + import matplotlib.pyplot as plt + + fig, ax = plt.subplots() + grid_size = 5 + grid_x = np.tile(np.arange(grid_size), grid_size) + grid_y = np.repeat(np.arange(grid_size), grid_size) + pts = ax.scatter(grid_x, grid_y) + + selector = SelectFromCollection(ax, pts) + + print("Select points in the figure by enclosing them within a polygon.") + print("Press the 'esc' key to start a new polygon.") + print("Try holding the 'shift' key to move all of the vertices.") + print("Try holding the 'ctrl' key to move a single vertex.") + + plt.show() + + selector.disconnect() + + # After figure is closed print the coordinates of the selected points + print('\nSelected points:') + print(selector.xys[selector.ind]) + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.PolygonSelector` +# - `matplotlib.path.Path` diff --git a/galleries/examples/widgets/polygon_selector_simple.py b/galleries/examples/widgets/polygon_selector_simple.py new file mode 100644 index 000000000000..e344da7e0645 --- /dev/null +++ b/galleries/examples/widgets/polygon_selector_simple.py @@ -0,0 +1,56 @@ +""" +================ +Polygon Selector +================ + +Shows how to create a polygon programmatically or interactively +""" + +import matplotlib.pyplot as plt + +from matplotlib.widgets import PolygonSelector + +# %% +# +# To create the polygon programmatically +fig, ax = plt.subplots() +fig.show() + +selector = PolygonSelector(ax, lambda *args: None) + +# Add three vertices +selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)] + + +# %% +# +# To create the polygon interactively + +fig2, ax2 = plt.subplots() +fig2.show() + +selector2 = PolygonSelector(ax2, lambda *args: None) + +print("Click on the figure to create a polygon.") +print("Press the 'esc' key to start a new polygon.") +print("Try holding the 'shift' key to move all of the vertices.") +print("Try holding the 'ctrl' key to move a single vertex.") + + +# %% +# .. tags:: +# +# component: axes, +# styling: position, +# plot-type: line, +# level: intermediate, +# domain: cartography, +# domain: geometry, +# domain: statistics, +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.PolygonSelector` diff --git a/galleries/examples/widgets/radio_buttons.py b/galleries/examples/widgets/radio_buttons.py new file mode 100644 index 000000000000..b2d7f8396576 --- /dev/null +++ b/galleries/examples/widgets/radio_buttons.py @@ -0,0 +1,86 @@ +""" +============= +Radio Buttons +============= + +Using radio buttons to choose properties of your plot. + +Radio buttons let you choose between multiple options in a visualization. +In this case, the buttons let the user choose one of the three different sine +waves to be shown in the plot. + +Radio buttons may be styled using the *label_props* and *radio_props* parameters, which +each take a dictionary with keys of artist property names and values of lists of +settings with length matching the number of buttons. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import RadioButtons + +t = np.arange(0.0, 2.0, 0.01) +s0 = np.sin(2*np.pi*t) +s1 = np.sin(4*np.pi*t) +s2 = np.sin(8*np.pi*t) + +fig, ax = plt.subplot_mosaic( + [ + ['main', 'freq'], + ['main', 'color'], + ['main', 'linestyle'], + ], + width_ratios=[5, 1], + layout='constrained', +) +l, = ax['main'].plot(t, s0, lw=2, color='red') + +radio_background = 'lightgoldenrodyellow' + +ax['freq'].set_facecolor(radio_background) +radio = RadioButtons(ax['freq'], ('1 Hz', '2 Hz', '4 Hz'), + label_props={'color': 'cmy', 'fontsize': [12, 14, 16]}, + radio_props={'s': [16, 32, 64]}) + + +def hzfunc(label): + hzdict = {'1 Hz': s0, '2 Hz': s1, '4 Hz': s2} + ydata = hzdict[label] + l.set_ydata(ydata) + fig.canvas.draw() +radio.on_clicked(hzfunc) + +ax['color'].set_facecolor(radio_background) +radio2 = RadioButtons( + ax['color'], ('red', 'blue', 'green'), + label_props={'color': ['red', 'blue', 'green']}, + radio_props={ + 'facecolor': ['red', 'blue', 'green'], + 'edgecolor': ['darkred', 'darkblue', 'darkgreen'], + }) + + +def colorfunc(label): + l.set_color(label) + fig.canvas.draw() +radio2.on_clicked(colorfunc) + +ax['linestyle'].set_facecolor(radio_background) +radio3 = RadioButtons(ax['linestyle'], ('-', '--', '-.', ':')) + + +def stylefunc(label): + l.set_linestyle(label) + fig.canvas.draw() +radio3.on_clicked(stylefunc) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.RadioButtons` diff --git a/galleries/examples/widgets/range_slider.py b/galleries/examples/widgets/range_slider.py new file mode 100644 index 000000000000..d2f2d1554246 --- /dev/null +++ b/galleries/examples/widgets/range_slider.py @@ -0,0 +1,71 @@ +""" +================================= +Image scaling using a RangeSlider +================================= + +Using the RangeSlider widget to control the thresholding of an image. + +The RangeSlider widget can be used similarly to the `.widgets.Slider` +widget. The major difference is that RangeSlider's ``val`` attribute +is a tuple of floats ``(lower val, upper val)`` rather than a single float. + +See :doc:`/gallery/widgets/slider_demo` for an example of using +a ``Slider`` to control a single float. + +See :doc:`/gallery/widgets/slider_snap_demo` for an example of having +the ``Slider`` snap to discrete values. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import RangeSlider + +# generate a fake image +np.random.seed(19680801) +N = 128 +img = np.random.randn(N, N) + +fig, axs = plt.subplots(1, 2, figsize=(10, 5)) +fig.subplots_adjust(bottom=0.25) + +im = axs[0].imshow(img) +axs[1].hist(img.flatten(), bins='auto') +axs[1].set_title('Histogram of pixel intensities') + +# Create the RangeSlider +slider_ax = fig.add_axes((0.20, 0.1, 0.60, 0.03)) +slider = RangeSlider(slider_ax, "Threshold", img.min(), img.max()) + +# Create the Vertical lines on the histogram +lower_limit_line = axs[1].axvline(slider.val[0], color='k') +upper_limit_line = axs[1].axvline(slider.val[1], color='k') + + +def update(val): + # The val passed to a callback by the RangeSlider will + # be a tuple of (min, max) + + # Update the image's colormap + im.norm.vmin = val[0] + im.norm.vmax = val[1] + + # Update the position of the vertical lines + lower_limit_line.set_xdata([val[0], val[0]]) + upper_limit_line.set_xdata([val[1], val[1]]) + + # Redraw the figure to ensure it updates + fig.canvas.draw_idle() + + +slider.on_changed(update) +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.RangeSlider` diff --git a/galleries/examples/widgets/rectangle_selector.py b/galleries/examples/widgets/rectangle_selector.py new file mode 100644 index 000000000000..012d52d89a9e --- /dev/null +++ b/galleries/examples/widgets/rectangle_selector.py @@ -0,0 +1,74 @@ +""" +=============================== +Rectangle and ellipse selectors +=============================== + +Click somewhere, move the mouse, and release the mouse button. +`.RectangleSelector` and `.EllipseSelector` draw a rectangle or an ellipse +from the initial click position to the current mouse position (within the same +axes) until the button is released. A connected callback receives the click- +and release-events. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import EllipseSelector, RectangleSelector + + +def select_callback(eclick, erelease): + """ + Callback for line selection. + + *eclick* and *erelease* are the press and release events. + """ + x1, y1 = eclick.xdata, eclick.ydata + x2, y2 = erelease.xdata, erelease.ydata + print(f"({x1:3.2f}, {y1:3.2f}) --> ({x2:3.2f}, {y2:3.2f})") + print(f"The buttons you used were: {eclick.button} {erelease.button}") + + +def toggle_selector(event): + print('Key pressed.') + if event.key == 't': + for selector in selectors: + name = type(selector).__name__ + if selector.active: + print(f'{name} deactivated.') + selector.set_active(False) + else: + print(f'{name} activated.') + selector.set_active(True) + + +fig = plt.figure(layout='constrained') +axs = fig.subplots(2) + +N = 100000 # If N is large one can see improvement by using blitting. +x = np.linspace(0, 10, N) + +selectors = [] +for ax, selector_class in zip(axs, [RectangleSelector, EllipseSelector]): + ax.plot(x, np.sin(2*np.pi*x)) # plot something + ax.set_title(f"Click and drag to draw a {selector_class.__name__}.") + selectors.append(selector_class( + ax, select_callback, + useblit=True, + button=[1, 3], # disable middle button + minspanx=5, minspany=5, + spancoords='pixels', + interactive=True)) + fig.canvas.mpl_connect('key_press_event', toggle_selector) +axs[0].set_title("Press 't' to toggle the selectors on and off.\n" + + axs[0].get_title()) +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.RectangleSelector` +# - `matplotlib.widgets.EllipseSelector` diff --git a/galleries/examples/widgets/slider_demo.py b/galleries/examples/widgets/slider_demo.py new file mode 100644 index 000000000000..e56390c182a0 --- /dev/null +++ b/galleries/examples/widgets/slider_demo.py @@ -0,0 +1,92 @@ +""" +====== +Slider +====== + +In this example, sliders are used to control the frequency and amplitude of +a sine wave. + +See :doc:`/gallery/widgets/slider_snap_demo` for an example of having +the ``Slider`` snap to discrete values. + +See :doc:`/gallery/widgets/range_slider` for an example of using +a ``RangeSlider`` to define a range of values. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import Button, Slider + + +# The parametrized function to be plotted +def f(t, amplitude, frequency): + return amplitude * np.sin(2 * np.pi * frequency * t) + +t = np.linspace(0, 1, 1000) + +# Define initial parameters +init_amplitude = 5 +init_frequency = 3 + +# Create the figure and the line that we will manipulate +fig, ax = plt.subplots() +line, = ax.plot(t, f(t, init_amplitude, init_frequency), lw=2) +ax.set_xlabel('Time [s]') + +# adjust the main plot to make room for the sliders +fig.subplots_adjust(left=0.25, bottom=0.25) + +# Make a horizontal slider to control the frequency. +axfreq = fig.add_axes((0.25, 0.1, 0.65, 0.03)) +freq_slider = Slider( + ax=axfreq, + label='Frequency [Hz]', + valmin=0.1, + valmax=30, + valinit=init_frequency, +) + +# Make a vertically oriented slider to control the amplitude +axamp = fig.add_axes((0.1, 0.25, 0.0225, 0.63)) +amp_slider = Slider( + ax=axamp, + label="Amplitude", + valmin=0, + valmax=10, + valinit=init_amplitude, + orientation="vertical" +) + + +# The function to be called anytime a slider's value changes +def update(val): + line.set_ydata(f(t, amp_slider.val, freq_slider.val)) + fig.canvas.draw_idle() + + +# register the update function with each slider +freq_slider.on_changed(update) +amp_slider.on_changed(update) + +# Create a `matplotlib.widgets.Button` to reset the sliders to initial values. +resetax = fig.add_axes((0.8, 0.025, 0.1, 0.04)) +button = Button(resetax, 'Reset', hovercolor='0.975') + + +def reset(event): + freq_slider.reset() + amp_slider.reset() +button.on_clicked(reset) + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.Button` +# - `matplotlib.widgets.Slider` diff --git a/galleries/examples/widgets/slider_snap_demo.py b/galleries/examples/widgets/slider_snap_demo.py new file mode 100644 index 000000000000..5826be32fa07 --- /dev/null +++ b/galleries/examples/widgets/slider_snap_demo.py @@ -0,0 +1,83 @@ +""" +=============================== +Snap sliders to discrete values +=============================== + +You can snap slider values to discrete values using the ``valstep`` argument. + +In this example the Freq slider is constrained to be multiples of pi, and the +Amp slider uses an array as the ``valstep`` argument to more densely sample +the first part of its range. + +See :doc:`/gallery/widgets/slider_demo` for an example of using +a ``Slider`` to control a single float. + +See :doc:`/gallery/widgets/range_slider` for an example of using +a ``RangeSlider`` to define a range of values. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import Button, Slider + +t = np.arange(0.0, 1.0, 0.001) +a0 = 5 +f0 = 3 +s = a0 * np.sin(2 * np.pi * f0 * t) + +fig, ax = plt.subplots() +fig.subplots_adjust(bottom=0.25) +l, = ax.plot(t, s, lw=2) + +ax_freq = fig.add_axes((0.25, 0.1, 0.65, 0.03)) +ax_amp = fig.add_axes((0.25, 0.15, 0.65, 0.03)) + +# define the values to use for snapping +allowed_amplitudes = np.concatenate([np.linspace(.1, 5, 100), [6, 7, 8, 9]]) + +# create the sliders +samp = Slider( + ax_amp, "Amp", 0.1, 9.0, + valinit=a0, valstep=allowed_amplitudes, + color="green" +) + +sfreq = Slider( + ax_freq, "Freq", 0, 10*np.pi, + valinit=2*np.pi, valstep=np.pi, + initcolor='none' # Remove the line marking the valinit position. +) + + +def update(val): + amp = samp.val + freq = sfreq.val + l.set_ydata(amp*np.sin(2*np.pi*freq*t)) + fig.canvas.draw_idle() + + +sfreq.on_changed(update) +samp.on_changed(update) + +ax_reset = fig.add_axes((0.8, 0.025, 0.1, 0.04)) +button = Button(ax_reset, 'Reset', hovercolor='0.975') + + +def reset(event): + sfreq.reset() + samp.reset() +button.on_clicked(reset) + + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.Slider` +# - `matplotlib.widgets.Button` diff --git a/galleries/examples/widgets/span_selector.py b/galleries/examples/widgets/span_selector.py new file mode 100644 index 000000000000..2fe1646948f8 --- /dev/null +++ b/galleries/examples/widgets/span_selector.py @@ -0,0 +1,74 @@ +""" +============= +Span Selector +============= + +The `.SpanSelector` is a mouse widget that enables selecting a range on an +axis. + +Here, an x-range can be selected on the upper axis; a detailed view of the +selected range is then plotted on the lower axis. + +.. note:: + + If the SpanSelector object is garbage collected you will lose the + interactivity. You must keep a hard reference to it to prevent this. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import SpanSelector + +# Fixing random state for reproducibility +np.random.seed(19680801) + +fig, (ax1, ax2) = plt.subplots(2, figsize=(8, 6)) + +x = np.arange(0.0, 5.0, 0.01) +y = np.sin(2 * np.pi * x) + 0.5 * np.random.randn(len(x)) + +ax1.plot(x, y) +ax1.set_ylim(-2, 2) +ax1.set_title('Press left mouse button and drag ' + 'to select a region in the top graph') + +line2, = ax2.plot([], []) + + +def onselect(xmin, xmax): + indmin, indmax = np.searchsorted(x, (xmin, xmax)) + indmax = min(len(x) - 1, indmax) + + region_x = x[indmin:indmax] + region_y = y[indmin:indmax] + + if len(region_x) >= 2: + line2.set_data(region_x, region_y) + ax2.set_xlim(region_x[0], region_x[-1]) + ax2.set_ylim(region_y.min(), region_y.max()) + fig.canvas.draw_idle() + + +span = SpanSelector( + ax1, + onselect, + "horizontal", + useblit=True, + props=dict(alpha=0.5, facecolor="tab:blue"), + interactive=True, + drag_from_anywhere=True +) +# Set useblit=True on most backends for enhanced performance. + + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.SpanSelector` diff --git a/galleries/examples/widgets/textbox.py b/galleries/examples/widgets/textbox.py new file mode 100644 index 000000000000..2121ce8594ce --- /dev/null +++ b/galleries/examples/widgets/textbox.py @@ -0,0 +1,56 @@ +""" +======= +Textbox +======= + +The Textbox widget lets users interactively provide text input, including +formulas. In this example, the plot is updated using the `.on_submit` method. +This method triggers the execution of the *submit* function when the +user presses enter in the textbox or leaves the textbox. + +Note: The `matplotlib.widgets.TextBox` widget is different from the following +static elements: :ref:`annotations` and +:doc:`/gallery/text_labels_and_annotations/placing_text_boxes`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +from matplotlib.widgets import TextBox + +fig, ax = plt.subplots() +fig.subplots_adjust(bottom=0.2) + +t = np.arange(-2.0, 2.0, 0.001) +l, = ax.plot(t, np.zeros_like(t), lw=2) + + +def submit(expression): + """ + Update the plotted function to the new math *expression*. + + *expression* is a string using "t" as its independent variable, e.g. + "t ** 3". + """ + ydata = eval(expression, {'np': np}, {'t': t}) + l.set_ydata(ydata) + ax.relim() + ax.autoscale_view() + plt.draw() + + +axbox = fig.add_axes((0.1, 0.05, 0.8, 0.075)) +text_box = TextBox(axbox, "Evaluate", textalignment="center") +text_box.on_submit(submit) +text_box.set_val("t ** 2") # Trigger `submit` with the initial string. + +plt.show() + +# %% +# +# .. admonition:: References +# +# The use of the following functions, methods, classes and modules is shown +# in this example: +# +# - `matplotlib.widgets.TextBox` diff --git a/galleries/plot_types/3D/README.rst b/galleries/plot_types/3D/README.rst new file mode 100644 index 000000000000..61b2729dfb8f --- /dev/null +++ b/galleries/plot_types/3D/README.rst @@ -0,0 +1,7 @@ +.. _3D_plots: + +3D and volumetric data +---------------------- + +Plots of three-dimensional :math:`(x,y,z)`, surface :math:`f(x,y)=z`, and +volumetric :math:`V_{x, y, z}` data using the `mpl_toolkits.mplot3d` library. diff --git a/galleries/plot_types/3D/bar3d_simple.py b/galleries/plot_types/3D/bar3d_simple.py new file mode 100644 index 000000000000..aa75560de8f2 --- /dev/null +++ b/galleries/plot_types/3D/bar3d_simple.py @@ -0,0 +1,29 @@ +""" +========================== +bar3d(x, y, z, dx, dy, dz) +========================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +x = [1, 1, 2, 2] +y = [1, 2, 1, 2] +z = [0, 0, 0, 0] +dx = np.ones_like(x)*0.5 +dy = np.ones_like(x)*0.5 +dz = [2, 3, 1, 4] + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.bar3d(x, y, z, dx, dy, dz) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/fill_between3d_simple.py b/galleries/plot_types/3D/fill_between3d_simple.py new file mode 100644 index 000000000000..f12fbbb5e958 --- /dev/null +++ b/galleries/plot_types/3D/fill_between3d_simple.py @@ -0,0 +1,33 @@ +""" +==================================== +fill_between(x1, y1, z1, x2, y2, z2) +==================================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.fill_between`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data for a double helix +n = 50 +theta = np.linspace(0, 2*np.pi, n) +x1 = np.cos(theta) +y1 = np.sin(theta) +z1 = np.linspace(0, 1, n) +x2 = np.cos(theta + np.pi) +y2 = np.sin(theta + np.pi) +z2 = z1 + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.fill_between(x1, y1, z1, x2, y2, z2, alpha=0.5) +ax.plot(x1, y1, z1, linewidth=2, color='C0') +ax.plot(x2, y2, z2, linewidth=2, color='C0') + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/plot3d_simple.py b/galleries/plot_types/3D/plot3d_simple.py new file mode 100644 index 000000000000..108dbecfbd87 --- /dev/null +++ b/galleries/plot_types/3D/plot3d_simple.py @@ -0,0 +1,27 @@ +""" +================ +plot(xs, ys, zs) +================ + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 100 +xs = np.linspace(0, 1, n) +ys = np.sin(xs * 6 * np.pi) +zs = np.cos(xs * 6 * np.pi) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.plot(xs, ys, zs) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/quiver3d_simple.py b/galleries/plot_types/3D/quiver3d_simple.py new file mode 100644 index 000000000000..6f4aaa9cad90 --- /dev/null +++ b/galleries/plot_types/3D/quiver3d_simple.py @@ -0,0 +1,32 @@ +""" +======================== +quiver(X, Y, Z, U, V, W) +======================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.quiver`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 4 +x = np.linspace(-1, 1, n) +y = np.linspace(-1, 1, n) +z = np.linspace(-1, 1, n) +X, Y, Z = np.meshgrid(x, y, z) +U = (X + Y)/5 +V = (Y - X)/5 +W = Z*0 + + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.quiver(X, Y, Z, U, V, W) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/scatter3d_simple.py b/galleries/plot_types/3D/scatter3d_simple.py new file mode 100644 index 000000000000..27ffb6abf748 --- /dev/null +++ b/galleries/plot_types/3D/scatter3d_simple.py @@ -0,0 +1,29 @@ +""" +=================== +scatter(xs, ys, zs) +=================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.scatter`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +np.random.seed(19680801) +n = 100 +rng = np.random.default_rng() +xs = rng.uniform(23, 32, n) +ys = rng.uniform(0, 100, n) +zs = rng.uniform(-50, -25, n) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.scatter(xs, ys, zs) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/stem3d.py b/galleries/plot_types/3D/stem3d.py new file mode 100644 index 000000000000..50aa80146bdc --- /dev/null +++ b/galleries/plot_types/3D/stem3d.py @@ -0,0 +1,27 @@ +""" +============= +stem(x, y, z) +============= + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.stem`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +n = 20 +x = np.sin(np.linspace(0, 2*np.pi, n)) +y = np.cos(np.linspace(0, 2*np.pi, n)) +z = np.linspace(0, 1, n) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.stem(x, y, z) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/surface3d_simple.py b/galleries/plot_types/3D/surface3d_simple.py new file mode 100644 index 000000000000..c887b042da94 --- /dev/null +++ b/galleries/plot_types/3D/surface3d_simple.py @@ -0,0 +1,28 @@ +""" +===================== +plot_surface(X, Y, Z) +===================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot_surface`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Make data +X = np.arange(-5, 5, 0.25) +Y = np.arange(-5, 5, 0.25) +X, Y = np.meshgrid(X, Y) +R = np.sqrt(X**2 + Y**2) +Z = np.sin(R) + +# Plot the surface +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.plot_surface(X, Y, Z, vmin=Z.min() * 2, cmap="Blues") + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/trisurf3d_simple.py b/galleries/plot_types/3D/trisurf3d_simple.py new file mode 100644 index 000000000000..f5252699ac23 --- /dev/null +++ b/galleries/plot_types/3D/trisurf3d_simple.py @@ -0,0 +1,33 @@ +""" +===================== +plot_trisurf(x, y, z) +===================== + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot_trisurf`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +n_radii = 8 +n_angles = 36 + +# Make radii and angles spaces +radii = np.linspace(0.125, 1.0, n_radii) +angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis] + +# Convert polar (radii, angles) coords to cartesian (x, y) coords. +x = np.append(0, (radii*np.cos(angles)).flatten()) +y = np.append(0, (radii*np.sin(angles)).flatten()) +z = np.sin(-x*y) + +# Plot +fig, ax = plt.subplots(subplot_kw={'projection': '3d'}) +ax.plot_trisurf(x, y, z, vmin=z.min() * 2, cmap="Blues") + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/voxels_simple.py b/galleries/plot_types/3D/voxels_simple.py new file mode 100644 index 000000000000..05ce238b0935 --- /dev/null +++ b/galleries/plot_types/3D/voxels_simple.py @@ -0,0 +1,31 @@ +""" +========================= +voxels([x, y, z], filled) +========================= + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.voxels`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# Prepare some coordinates +x, y, z = np.indices((8, 8, 8)) + +# Draw cuboids in the top left and bottom right corners +cube1 = (x < 3) & (y < 3) & (z < 3) +cube2 = (x >= 5) & (y >= 5) & (z >= 5) + +# Combine the objects into a single boolean array +voxelarray = cube1 | cube2 + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.voxels(voxelarray, edgecolor='k') + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/3D/wire3d_simple.py b/galleries/plot_types/3D/wire3d_simple.py new file mode 100644 index 000000000000..1ab847f3ecf4 --- /dev/null +++ b/galleries/plot_types/3D/wire3d_simple.py @@ -0,0 +1,25 @@ +""" +======================= +plot_wireframe(X, Y, Z) +======================= + +See `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot_wireframe`. +""" +import matplotlib.pyplot as plt + +from mpl_toolkits.mplot3d import axes3d + +plt.style.use('_mpl-gallery') + +# Make data +X, Y, Z = axes3d.get_test_data(0.05) + +# Plot +fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) +ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) + +ax.set(xticklabels=[], + yticklabels=[], + zticklabels=[]) + +plt.show() diff --git a/galleries/plot_types/README.rst b/galleries/plot_types/README.rst new file mode 100644 index 000000000000..0bcbb3b804d7 --- /dev/null +++ b/galleries/plot_types/README.rst @@ -0,0 +1,11 @@ +.. _plot_types: + +.. redirect-from:: /tutorials/basic/sample_plots + +Plot types +========== + +Overview of many common plotting commands provided by Matplotlib. + +See the `gallery <../gallery/index.html>`_ for more examples and +the `tutorials page <../tutorials/index.html>`_ for longer examples. diff --git a/galleries/plot_types/arrays/README.rst b/galleries/plot_types/arrays/README.rst new file mode 100644 index 000000000000..aba457a69940 --- /dev/null +++ b/galleries/plot_types/arrays/README.rst @@ -0,0 +1,8 @@ +.. _array_plots: + +Gridded data +------------ + +Plots of arrays and images :math:`Z_{i, j}` and fields :math:`U_{i, j}, V_{i, j}` +on `regular grids `_ and +corresponding coordinate grids :math:`X_{i,j}, Y_{i,j}`. diff --git a/galleries/plot_types/arrays/barbs.py b/galleries/plot_types/arrays/barbs.py new file mode 100644 index 000000000000..b007d9b875b8 --- /dev/null +++ b/galleries/plot_types/arrays/barbs.py @@ -0,0 +1,34 @@ +""" +================= +barbs(X, Y, U, V) +================= +Plot a 2D field of wind barbs. + +See `~matplotlib.axes.Axes.barbs`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +X, Y = np.meshgrid([1, 2, 3, 4], [1, 2, 3, 4]) +angle = np.pi / 180 * np.array([[15., 30, 35, 45], + [25., 40, 55, 60], + [35., 50, 65, 75], + [45., 60, 75, 90]]) +amplitude = np.array([[5, 10, 25, 50], + [10, 15, 30, 60], + [15, 26, 50, 70], + [20, 45, 80, 100]]) +U = amplitude * np.sin(angle) +V = amplitude * np.cos(angle) + +# plot: +fig, ax = plt.subplots() + +ax.barbs(X, Y, U, V, barbcolor='C0', flagcolor='C0', length=7, linewidth=1.5) + +ax.set(xlim=(0, 4.5), ylim=(0, 4.5)) + +plt.show() diff --git a/galleries/plot_types/arrays/contour.py b/galleries/plot_types/arrays/contour.py new file mode 100644 index 000000000000..1bf8d71d482b --- /dev/null +++ b/galleries/plot_types/arrays/contour.py @@ -0,0 +1,24 @@ +""" +================ +contour(X, Y, Z) +================ +Plot contour lines. + +See `~matplotlib.axes.Axes.contour`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data +X, Y = np.meshgrid(np.linspace(-3, 3, 256), np.linspace(-3, 3, 256)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) +levels = np.linspace(np.min(Z), np.max(Z), 7) + +# plot +fig, ax = plt.subplots() + +ax.contour(X, Y, Z, levels=levels) + +plt.show() diff --git a/galleries/plot_types/arrays/contourf.py b/galleries/plot_types/arrays/contourf.py new file mode 100644 index 000000000000..c25afe0bfa77 --- /dev/null +++ b/galleries/plot_types/arrays/contourf.py @@ -0,0 +1,24 @@ +""" +================= +contourf(X, Y, Z) +================= +Plot filled contours. + +See `~matplotlib.axes.Axes.contourf`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data +X, Y = np.meshgrid(np.linspace(-3, 3, 256), np.linspace(-3, 3, 256)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) +levels = np.linspace(Z.min(), Z.max(), 7) + +# plot +fig, ax = plt.subplots() + +ax.contourf(X, Y, Z, levels=levels) + +plt.show() diff --git a/galleries/plot_types/arrays/imshow.py b/galleries/plot_types/arrays/imshow.py new file mode 100644 index 000000000000..b2920e7fd80c --- /dev/null +++ b/galleries/plot_types/arrays/imshow.py @@ -0,0 +1,24 @@ +""" +========= +imshow(Z) +========= +Display data as an image, i.e., on a 2D regular raster. + +See `~matplotlib.axes.Axes.imshow`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data +X, Y = np.meshgrid(np.linspace(-3, 3, 16), np.linspace(-3, 3, 16)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) + +# plot +fig, ax = plt.subplots() + +ax.imshow(Z, origin='lower') + +plt.show() diff --git a/galleries/plot_types/arrays/pcolormesh.py b/galleries/plot_types/arrays/pcolormesh.py new file mode 100644 index 000000000000..4f0913f62521 --- /dev/null +++ b/galleries/plot_types/arrays/pcolormesh.py @@ -0,0 +1,26 @@ +""" +=================== +pcolormesh(X, Y, Z) +=================== +Create a pseudocolor plot with a non-regular rectangular grid. + +`~.axes.Axes.pcolormesh` is more flexible than `~.axes.Axes.imshow` in that +the x and y vectors need not be equally spaced (indeed they can be skewed). + +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data with uneven sampling in x +x = [-3, -2, -1.6, -1.2, -.8, -.5, -.2, .1, .3, .5, .8, 1.1, 1.5, 1.9, 2.3, 3] +X, Y = np.meshgrid(x, np.linspace(-3, 3, 128)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) + +# plot +fig, ax = plt.subplots() + +ax.pcolormesh(X, Y, Z, vmin=-0.5, vmax=1.0) + +plt.show() diff --git a/galleries/plot_types/arrays/quiver.py b/galleries/plot_types/arrays/quiver.py new file mode 100644 index 000000000000..4b1cbd03759c --- /dev/null +++ b/galleries/plot_types/arrays/quiver.py @@ -0,0 +1,29 @@ +""" +================== +quiver(X, Y, U, V) +================== +Plot a 2D field of arrows. + +See `~matplotlib.axes.Axes.quiver`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data +x = np.linspace(-4, 4, 6) +y = np.linspace(-4, 4, 6) +X, Y = np.meshgrid(x, y) +U = X + Y +V = Y - X + +# plot +fig, ax = plt.subplots() + +ax.quiver(X, Y, U, V, color="C0", angles='xy', + scale_units='xy', scale=5, width=.015) + +ax.set(xlim=(-5, 5), ylim=(-5, 5)) + +plt.show() diff --git a/galleries/plot_types/arrays/streamplot.py b/galleries/plot_types/arrays/streamplot.py new file mode 100644 index 000000000000..670773d2cfd3 --- /dev/null +++ b/galleries/plot_types/arrays/streamplot.py @@ -0,0 +1,26 @@ +""" +====================== +streamplot(X, Y, U, V) +====================== +Draw streamlines of a vector flow. + +See `~matplotlib.axes.Axes.streamplot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make a stream function: +X, Y = np.meshgrid(np.linspace(-3, 3, 256), np.linspace(-3, 3, 256)) +Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2) +# make U and V out of the streamfunction: +V = np.diff(Z[1:, :], axis=1) +U = -np.diff(Z[:, 1:], axis=0) + +# plot: +fig, ax = plt.subplots() + +ax.streamplot(X[1:, 1:], Y[1:, 1:], U, V) + +plt.show() diff --git a/galleries/plot_types/basic/README.rst b/galleries/plot_types/basic/README.rst new file mode 100644 index 000000000000..937c7484c8db --- /dev/null +++ b/galleries/plot_types/basic/README.rst @@ -0,0 +1,7 @@ +.. _basic_plots: + +Pairwise data +------------- + +Plots of pairwise :math:`(x, y)`, tabular :math:`(var\_0, \cdots, var\_n)`, +and functional :math:`f(x)=y` data. diff --git a/galleries/plot_types/basic/bar.py b/galleries/plot_types/basic/bar.py new file mode 100644 index 000000000000..005e85c5e2ba --- /dev/null +++ b/galleries/plot_types/basic/bar.py @@ -0,0 +1,25 @@ +""" +============== +bar(x, height) +============== + +See `~matplotlib.axes.Axes.bar`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data: +x = 0.5 + np.arange(8) +y = [4.8, 5.5, 3.5, 4.6, 6.5, 6.6, 2.6, 3.0] + +# plot +fig, ax = plt.subplots() + +ax.bar(x, y, width=1, edgecolor="white", linewidth=0.7) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/fill_between.py b/galleries/plot_types/basic/fill_between.py new file mode 100644 index 000000000000..feca3c658d3e --- /dev/null +++ b/galleries/plot_types/basic/fill_between.py @@ -0,0 +1,30 @@ +""" +======================= +fill_between(x, y1, y2) +======================= +Fill the area between two horizontal curves. + +See `~matplotlib.axes.Axes.fill_between`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +np.random.seed(1) +x = np.linspace(0, 8, 16) +y1 = 3 + 4*x/8 + np.random.uniform(0.0, 0.5, len(x)) +y2 = 1 + 2*x/8 + np.random.uniform(0.0, 0.5, len(x)) + +# plot +fig, ax = plt.subplots() + +ax.fill_between(x, y1, y2, alpha=.5, linewidth=0) +ax.plot(x, (y1 + y2)/2, linewidth=2) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/plot.py b/galleries/plot_types/basic/plot.py new file mode 100644 index 000000000000..34cf500599bb --- /dev/null +++ b/galleries/plot_types/basic/plot.py @@ -0,0 +1,31 @@ +""" +========== +plot(x, y) +========== +Plot y versus x as lines and/or markers. + +See `~matplotlib.axes.Axes.plot`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +x = np.linspace(0, 10, 100) +y = 4 + 1 * np.sin(2 * x) +x2 = np.linspace(0, 10, 25) +y2 = 4 + 1 * np.sin(2 * x2) + +# plot +fig, ax = plt.subplots() + +ax.plot(x2, y2 + 2.5, 'x', markeredgewidth=2) +ax.plot(x, y, linewidth=2.0) +ax.plot(x2, y2 - 2.5, 'o-', linewidth=2) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/scatter_plot.py b/galleries/plot_types/basic/scatter_plot.py new file mode 100644 index 000000000000..738af15440db --- /dev/null +++ b/galleries/plot_types/basic/scatter_plot.py @@ -0,0 +1,30 @@ +""" +============= +scatter(x, y) +============= +A scatter plot of y versus x with varying marker size and/or color. + +See `~matplotlib.axes.Axes.scatter`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make the data +np.random.seed(3) +x = 4 + np.random.normal(0, 2, 24) +y = 4 + np.random.normal(0, 2, len(x)) +# size and color: +sizes = np.random.uniform(15, 80, len(x)) +colors = np.random.uniform(15, 80, len(x)) + +# plot +fig, ax = plt.subplots() + +ax.scatter(x, y, s=sizes, c=colors, vmin=0, vmax=100) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/stackplot.py b/galleries/plot_types/basic/stackplot.py new file mode 100644 index 000000000000..275fa5cb67ba --- /dev/null +++ b/galleries/plot_types/basic/stackplot.py @@ -0,0 +1,29 @@ +""" +=============== +stackplot(x, y) +=============== +Draw a stacked area plot or a streamgraph. + +See `~matplotlib.axes.Axes.stackplot` +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +x = np.arange(0, 10, 2) +ay = [1, 1.25, 2, 2.75, 3] +by = [1, 1, 1, 1, 1] +cy = [2, 1, 2, 1, 2] +y = np.vstack([ay, by, cy]) + +# plot +fig, ax = plt.subplots() + +ax.stackplot(x, y) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/stairs.py b/galleries/plot_types/basic/stairs.py new file mode 100644 index 000000000000..732ded998241 --- /dev/null +++ b/galleries/plot_types/basic/stairs.py @@ -0,0 +1,29 @@ +""" +============== +stairs(values) +============== +Draw a stepwise constant function as a line or a filled plot. + +See `~matplotlib.axes.Axes.stairs` when plotting :math:`y` between +:math:`(x_i, x_{i+1})`. For plotting :math:`y` at :math:`x`, see +`~matplotlib.axes.Axes.step`. + +.. redirect-from:: /plot_types/basic/step +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +y = [4.8, 5.5, 3.5, 4.6, 6.5, 6.6, 2.6, 3.0] + +# plot +fig, ax = plt.subplots() + +ax.stairs(y, linewidth=2.5) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/basic/stem.py b/galleries/plot_types/basic/stem.py new file mode 100644 index 000000000000..afd10ca1c9df --- /dev/null +++ b/galleries/plot_types/basic/stem.py @@ -0,0 +1,26 @@ +""" +========== +stem(x, y) +========== +Create a stem plot. + +See `~matplotlib.axes.Axes.stem`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +x = 0.5 + np.arange(8) +y = [4.8, 5.5, 3.5, 4.6, 6.5, 6.6, 2.6, 3.0] + +# plot +fig, ax = plt.subplots() + +ax.stem(x, y) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/stats/README.rst b/galleries/plot_types/stats/README.rst new file mode 100644 index 000000000000..56a56cb2db04 --- /dev/null +++ b/galleries/plot_types/stats/README.rst @@ -0,0 +1,7 @@ +.. _stats_plots: + +Statistical distributions +------------------------- + +Plots of the distribution of at least one variable in a dataset. Some of these +methods also compute the distributions. diff --git a/galleries/plot_types/stats/boxplot_plot.py b/galleries/plot_types/stats/boxplot_plot.py new file mode 100644 index 000000000000..996b97a2aef4 --- /dev/null +++ b/galleries/plot_types/stats/boxplot_plot.py @@ -0,0 +1,31 @@ +""" +========== +boxplot(X) +========== +Draw a box and whisker plot. + +See `~matplotlib.axes.Axes.boxplot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data: +np.random.seed(10) +D = np.random.normal((3, 5, 4), (1.25, 1.00, 1.25), (100, 3)) + +# plot +fig, ax = plt.subplots() +VP = ax.boxplot(D, positions=[2, 4, 6], widths=1.5, patch_artist=True, + showmeans=False, showfliers=False, + medianprops={"color": "white", "linewidth": 0.5}, + boxprops={"facecolor": "C0", "edgecolor": "white", + "linewidth": 0.5}, + whiskerprops={"color": "C0", "linewidth": 1.5}, + capprops={"color": "C0", "linewidth": 1.5}) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/stats/ecdf.py b/galleries/plot_types/stats/ecdf.py new file mode 100644 index 000000000000..1a8566b3d2eb --- /dev/null +++ b/galleries/plot_types/stats/ecdf.py @@ -0,0 +1,22 @@ +""" +======= +ecdf(x) +======= +Compute and plot the empirical cumulative distribution function of x. + +See `~matplotlib.axes.Axes.ecdf`. +""" + +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +np.random.seed(1) +x = 4 + np.random.normal(0, 1.5, 200) + +# plot: +fig, ax = plt.subplots() +ax.ecdf(x) +plt.show() diff --git a/galleries/plot_types/stats/errorbar_plot.py b/galleries/plot_types/stats/errorbar_plot.py new file mode 100644 index 000000000000..c96a08e111c1 --- /dev/null +++ b/galleries/plot_types/stats/errorbar_plot.py @@ -0,0 +1,28 @@ +""" +========================== +errorbar(x, y, yerr, xerr) +========================== +Plot y versus x as lines and/or markers with attached errorbars. + +See `~matplotlib.axes.Axes.errorbar`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data: +np.random.seed(1) +x = [2, 4, 6] +y = [3.6, 5, 4.2] +yerr = [0.9, 1.2, 0.5] + +# plot: +fig, ax = plt.subplots() + +ax.errorbar(x, y, yerr, fmt='o', linewidth=2, capsize=6) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/stats/eventplot.py b/galleries/plot_types/stats/eventplot.py new file mode 100644 index 000000000000..babdbe6d1ca1 --- /dev/null +++ b/galleries/plot_types/stats/eventplot.py @@ -0,0 +1,27 @@ +""" +============ +eventplot(D) +============ +Plot identical parallel lines at the given positions. + +See `~matplotlib.axes.Axes.eventplot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data: +np.random.seed(1) +x = [2, 4, 6] +D = np.random.gamma(4, size=(3, 50)) + +# plot: +fig, ax = plt.subplots() + +ax.eventplot(D, orientation="vertical", lineoffsets=x, linewidth=0.75) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/stats/hexbin.py b/galleries/plot_types/stats/hexbin.py new file mode 100644 index 000000000000..9d3a2a071346 --- /dev/null +++ b/galleries/plot_types/stats/hexbin.py @@ -0,0 +1,26 @@ +""" +=============== +hexbin(x, y, C) +=============== +Make a 2D hexagonal binning plot of points x, y. + +See `~matplotlib.axes.Axes.hexbin`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: correlated + noise +np.random.seed(1) +x = np.random.randn(5000) +y = 1.2 * x + np.random.randn(5000) / 3 + +# plot: +fig, ax = plt.subplots() + +ax.hexbin(x, y, gridsize=20) + +ax.set(xlim=(-2, 2), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/plot_types/stats/hist2d.py b/galleries/plot_types/stats/hist2d.py new file mode 100644 index 000000000000..d95b67539b33 --- /dev/null +++ b/galleries/plot_types/stats/hist2d.py @@ -0,0 +1,26 @@ +""" +============ +hist2d(x, y) +============ +Make a 2D histogram plot. + +See `~matplotlib.axes.Axes.hist2d`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: correlated + noise +np.random.seed(1) +x = np.random.randn(5000) +y = 1.2 * x + np.random.randn(5000) / 3 + +# plot: +fig, ax = plt.subplots() + +ax.hist2d(x, y, bins=(np.arange(-3, 3, 0.1), np.arange(-3, 3, 0.1))) + +ax.set(xlim=(-2, 2), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/plot_types/stats/hist_plot.py b/galleries/plot_types/stats/hist_plot.py new file mode 100644 index 000000000000..6328fe9d07c6 --- /dev/null +++ b/galleries/plot_types/stats/hist_plot.py @@ -0,0 +1,26 @@ +""" +======= +hist(x) +======= +Compute and plot a histogram. + +See `~matplotlib.axes.Axes.hist`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data +np.random.seed(1) +x = 4 + np.random.normal(0, 1.5, 200) + +# plot: +fig, ax = plt.subplots() + +ax.hist(x, bins=8, linewidth=0.5, edgecolor="white") + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 56), yticks=np.linspace(0, 56, 9)) + +plt.show() diff --git a/galleries/plot_types/stats/pie.py b/galleries/plot_types/stats/pie.py new file mode 100644 index 000000000000..bd8d555f0040 --- /dev/null +++ b/galleries/plot_types/stats/pie.py @@ -0,0 +1,27 @@ +""" +====== +pie(x) +====== +Plot a pie chart. + +See `~matplotlib.axes.Axes.pie`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + + +# make data +x = [1, 2, 3, 4] +colors = plt.get_cmap('Blues')(np.linspace(0.2, 0.7, len(x))) + +# plot +fig, ax = plt.subplots() +ax.pie(x, colors=colors, radius=3, center=(4, 4), + wedgeprops={"linewidth": 1, "edgecolor": "white"}, frame=True) + +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/stats/violin.py b/galleries/plot_types/stats/violin.py new file mode 100644 index 000000000000..2ea2161ad91c --- /dev/null +++ b/galleries/plot_types/stats/violin.py @@ -0,0 +1,29 @@ +""" +============= +violinplot(D) +============= +Make a violin plot. + +See `~matplotlib.axes.Axes.violinplot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery') + +# make data: +np.random.seed(10) +D = np.random.normal((3, 5, 4), (0.75, 1.00, 0.75), (200, 3)) + +# plot: +fig, ax = plt.subplots() + +vp = ax.violinplot(D, [2, 4, 6], widths=2, + showmeans=False, showmedians=False, showextrema=False) +# styling: +for body in vp['bodies']: + body.set_alpha(0.9) +ax.set(xlim=(0, 8), xticks=np.arange(1, 8), + ylim=(0, 8), yticks=np.arange(1, 8)) + +plt.show() diff --git a/galleries/plot_types/unstructured/README.rst b/galleries/plot_types/unstructured/README.rst new file mode 100644 index 000000000000..89b7924dd2f4 --- /dev/null +++ b/galleries/plot_types/unstructured/README.rst @@ -0,0 +1,7 @@ +.. _unstructured_plots: + +Irregularly gridded data +------------------------ + +Plots of data :math:`Z_{x, y}` on `unstructured grids `_ , +unstructured coordinate grids :math:`(x, y)`, and 2D functions :math:`f(x, y) = z`. diff --git a/galleries/plot_types/unstructured/tricontour.py b/galleries/plot_types/unstructured/tricontour.py new file mode 100644 index 000000000000..292ff551fe36 --- /dev/null +++ b/galleries/plot_types/unstructured/tricontour.py @@ -0,0 +1,29 @@ +""" +=================== +tricontour(x, y, z) +=================== +Draw contour lines on an unstructured triangular grid. + +See `~matplotlib.axes.Axes.tricontour`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +np.random.seed(1) +x = np.random.uniform(-3, 3, 256) +y = np.random.uniform(-3, 3, 256) +z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2) +levels = np.linspace(z.min(), z.max(), 7) + +# plot: +fig, ax = plt.subplots() + +ax.plot(x, y, 'o', markersize=2, color='lightgrey') +ax.tricontour(x, y, z, levels=levels) + +ax.set(xlim=(-3, 3), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/plot_types/unstructured/tricontourf.py b/galleries/plot_types/unstructured/tricontourf.py new file mode 100644 index 000000000000..aab748e73024 --- /dev/null +++ b/galleries/plot_types/unstructured/tricontourf.py @@ -0,0 +1,29 @@ +""" +==================== +tricontourf(x, y, z) +==================== +Draw contour regions on an unstructured triangular grid. + +See `~matplotlib.axes.Axes.tricontourf`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +np.random.seed(1) +x = np.random.uniform(-3, 3, 256) +y = np.random.uniform(-3, 3, 256) +z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2) +levels = np.linspace(z.min(), z.max(), 7) + +# plot: +fig, ax = plt.subplots() + +ax.plot(x, y, 'o', markersize=2, color='grey') +ax.tricontourf(x, y, z, levels=levels) + +ax.set(xlim=(-3, 3), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/plot_types/unstructured/tripcolor.py b/galleries/plot_types/unstructured/tripcolor.py new file mode 100644 index 000000000000..398877653db8 --- /dev/null +++ b/galleries/plot_types/unstructured/tripcolor.py @@ -0,0 +1,28 @@ +""" +================== +tripcolor(x, y, z) +================== +Create a pseudocolor plot of an unstructured triangular grid. + +See `~matplotlib.axes.Axes.tripcolor`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +np.random.seed(1) +x = np.random.uniform(-3, 3, 256) +y = np.random.uniform(-3, 3, 256) +z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2) + +# plot: +fig, ax = plt.subplots() + +ax.plot(x, y, 'o', markersize=2, color='grey') +ax.tripcolor(x, y, z) + +ax.set(xlim=(-3, 3), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/plot_types/unstructured/triplot.py b/galleries/plot_types/unstructured/triplot.py new file mode 100644 index 000000000000..d726c46e1e47 --- /dev/null +++ b/galleries/plot_types/unstructured/triplot.py @@ -0,0 +1,27 @@ +""" +============= +triplot(x, y) +============= +Draw an unstructured triangular grid as lines and/or markers. + +See `~matplotlib.axes.Axes.triplot`. +""" +import matplotlib.pyplot as plt +import numpy as np + +plt.style.use('_mpl-gallery-nogrid') + +# make data: +np.random.seed(1) +x = np.random.uniform(-3, 3, 256) +y = np.random.uniform(-3, 3, 256) +z = (1 - x/2 + x**5 + y**3) * np.exp(-x**2 - y**2) + +# plot: +fig, ax = plt.subplots() + +ax.triplot(x, y) + +ax.set(xlim=(-3, 3), ylim=(-3, 3)) + +plt.show() diff --git a/galleries/tutorials/artists.py b/galleries/tutorials/artists.py new file mode 100644 index 000000000000..4f93f7c71a6e --- /dev/null +++ b/galleries/tutorials/artists.py @@ -0,0 +1,721 @@ +""" +.. redirect-from:: /tutorials/intermediate/artists + +.. _artists_tutorial: + +=============== +Artist tutorial +=============== + +Using Artist objects to render on the canvas. + +There are three layers to the Matplotlib API. + +* the :class:`!matplotlib.backend_bases.FigureCanvas` is the area onto which + the figure is drawn +* the :class:`!matplotlib.backend_bases.Renderer` is the object which knows how + to draw on the :class:`!matplotlib.backend_bases.FigureCanvas` +* and the :class:`matplotlib.artist.Artist` is the object that knows how to use + a renderer to paint onto the canvas. + +The :class:`!matplotlib.backend_bases.FigureCanvas` and +:class:`!matplotlib.backend_bases.Renderer` handle all the details of +talking to user interface toolkits like `wxPython +`_ or drawing languages like PostScript®, and +the ``Artist`` handles all the high level constructs like representing +and laying out the figure, text, and lines. The typical user will +spend 95% of their time working with the ``Artists``. + +There are two types of ``Artists``: primitives and containers. The primitives +represent the standard graphical objects we want to paint onto our canvas: +:class:`~matplotlib.lines.Line2D`, :class:`~matplotlib.patches.Rectangle`, +:class:`~matplotlib.text.Text`, :class:`~matplotlib.image.AxesImage`, etc., and +the containers are places to put them (:class:`~matplotlib.axis.Axis`, +:class:`~matplotlib.axes.Axes` and :class:`~matplotlib.figure.Figure`). The +standard use is to create a :class:`~matplotlib.figure.Figure` instance, use +the ``Figure`` to create one or more :class:`~matplotlib.axes.Axes` +instances, and use the ``Axes`` instance +helper methods to create the primitives. In the example below, we create a +``Figure`` instance using :func:`matplotlib.pyplot.figure`, which is a +convenience method for instantiating ``Figure`` instances and connecting them +with your user interface or drawing toolkit ``FigureCanvas``. As we will +discuss below, this is not necessary -- you can work directly with PostScript, +PDF Gtk+, or wxPython ``FigureCanvas`` instances, instantiate your ``Figures`` +directly and connect them yourselves -- but since we are focusing here on the +``Artist`` API we'll let :mod:`~matplotlib.pyplot` handle some of those details +for us:: + + import matplotlib.pyplot as plt + fig = plt.figure() + ax = fig.add_subplot(2, 1, 1) # two rows, one column, first plot + +The :class:`~matplotlib.axes.Axes` is probably the most important +class in the Matplotlib API, and the one you will be working with most +of the time. This is because the ``Axes`` is the plotting area into +which most of the objects go, and the ``Axes`` has many special helper +methods (:meth:`~matplotlib.axes.Axes.plot`, +:meth:`~matplotlib.axes.Axes.text`, +:meth:`~matplotlib.axes.Axes.hist`, +:meth:`~matplotlib.axes.Axes.imshow`) to create the most common +graphics primitives (:class:`~matplotlib.lines.Line2D`, +:class:`~matplotlib.text.Text`, +:class:`~matplotlib.patches.Rectangle`, +:class:`~matplotlib.image.AxesImage`, respectively). These helper methods +will take your data (e.g., ``numpy`` arrays and strings) and create +primitive ``Artist`` instances as needed (e.g., ``Line2D``), add them to +the relevant containers, and draw them when requested. If you want to create +an ``Axes`` at an arbitrary location, simply use the +:meth:`~matplotlib.figure.Figure.add_axes` method which takes a list +of ``[left, bottom, width, height]`` values in 0-1 relative figure +coordinates:: + + fig2 = plt.figure() + ax2 = fig2.add_axes((0.15, 0.1, 0.7, 0.3)) + +Continuing with our example:: + + import numpy as np + t = np.arange(0.0, 1.0, 0.01) + s = np.sin(2*np.pi*t) + line, = ax.plot(t, s, color='blue', lw=2) + +In this example, ``ax`` is the ``Axes`` instance created by the +``fig.add_subplot`` call above and when you call ``ax.plot``, it creates a +``Line2D`` instance and +adds it to the ``Axes``. In the interactive `IPython `_ +session below, you can see that the ``Axes.lines`` list is length one and +contains the same line that was returned by the ``line, = ax.plot...`` call: + +.. sourcecode:: ipython + + In [101]: ax.lines[0] + Out[101]: + + In [102]: line + Out[102]: + +If you make subsequent calls to ``ax.plot`` (and the hold state is "on" +which is the default) then additional lines will be added to the list. +You can remove a line later by calling its ``remove`` method:: + + line = ax.lines[0] + line.remove() + +The Axes also has helper methods to configure and decorate the x-axis +and y-axis tick, tick labels and axis labels:: + + xtext = ax.set_xlabel('my xdata') # returns a Text instance + ytext = ax.set_ylabel('my ydata') + +When you call :meth:`ax.set_xlabel `, +it passes the information on the :class:`~matplotlib.text.Text` +instance of the :class:`~matplotlib.axis.XAxis`. Each ``Axes`` +instance contains an :class:`~matplotlib.axis.XAxis` and a +:class:`~matplotlib.axis.YAxis` instance, which handle the layout and +drawing of the ticks, tick labels and axis labels. + +Try creating the figure below. +""" +# sphinx_gallery_capture_repr = ('__repr__',) + +import matplotlib.pyplot as plt +import numpy as np + +fig = plt.figure() +fig.subplots_adjust(top=0.8) +ax1 = fig.add_subplot(211) +ax1.set_ylabel('Voltage [V]') +ax1.set_title('A sine wave') + +t = np.arange(0.0, 1.0, 0.01) +s = np.sin(2*np.pi*t) +line, = ax1.plot(t, s, color='blue', lw=2) + +# Fixing random state for reproducibility +np.random.seed(19680801) + +ax2 = fig.add_axes((0.15, 0.1, 0.7, 0.3)) +n, bins, patches = ax2.hist(np.random.randn(1000), 50, + facecolor='yellow', edgecolor='yellow') +ax2.set_xlabel('Time [s]') + +plt.show() + +# %% +# .. _customizing-artists: +# +# Customizing your objects +# ======================== +# +# Every element in the figure is represented by a Matplotlib +# :class:`~matplotlib.artist.Artist`, and each has an extensive list of +# properties to configure its appearance. The figure itself contains a +# :class:`~matplotlib.patches.Rectangle` exactly the size of the figure, +# which you can use to set the background color and transparency of the +# figures. Likewise, each :class:`~matplotlib.axes.Axes` bounding box +# (the standard white box with black edges in the typical Matplotlib +# plot, has a ``Rectangle`` instance that determines the color, +# transparency, and other properties of the Axes. These instances are +# stored as member variables :attr:`!Figure.patch` and :attr:`!Axes.patch` +# ("Patch" is a name inherited from MATLAB, and is a 2D "patch" +# of color on the figure, e.g., rectangles, circles and polygons). +# Every Matplotlib ``Artist`` has the following properties +# +# ========== ================================================================= +# Property Description +# ========== ================================================================= +# alpha The transparency - a scalar from 0-1 +# animated A boolean that is used to facilitate animated drawing +# axes The Axes that the Artist lives in, possibly None +# clip_box The bounding box that clips the Artist +# clip_on Whether clipping is enabled +# clip_path The path the artist is clipped to +# contains A picking function to test whether the artist contains the pick +# point +# figure The figure instance the artist lives in, possibly None +# label A text label (e.g., for auto-labeling) +# picker A python object that controls object picking +# transform The transformation +# visible A boolean whether the artist should be drawn +# zorder A number which determines the drawing order +# rasterized Boolean; Turns vectors into raster graphics (for compression & +# EPS transparency) +# ========== ================================================================= +# +# Each of the properties is accessed with an old-fashioned setter or +# getter (yes we know this irritates Pythonistas and we plan to support +# direct access via properties or traits but it hasn't been done yet). +# For example, to multiply the current alpha by a half:: +# +# a = o.get_alpha() +# o.set_alpha(0.5*a) +# +# If you want to set a number of properties at once, you can also use +# the ``set`` method with keyword arguments. For example:: +# +# o.set(alpha=0.5, zorder=2) +# +# If you are working interactively at the python shell, a handy way to +# inspect the ``Artist`` properties is to use the +# :func:`matplotlib.artist.getp` function (simply +# :func:`~matplotlib.pyplot.getp` in pyplot), which lists the properties +# and their values. This works for classes derived from ``Artist`` as +# well, e.g., ``Figure`` and ``Rectangle``. Here are the ``Figure`` rectangle +# properties mentioned above: +# +# .. sourcecode:: ipython +# +# In [149]: matplotlib.artist.getp(fig.patch) +# agg_filter = None +# alpha = None +# animated = False +# antialiased or aa = False +# bbox = Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0) +# capstyle = butt +# children = [] +# clip_box = None +# clip_on = True +# clip_path = None +# contains = None +# data_transform = BboxTransformTo( TransformedBbox( Bbox... +# edgecolor or ec = (1.0, 1.0, 1.0, 1.0) +# extents = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0) +# facecolor or fc = (1.0, 1.0, 1.0, 1.0) +# figure = Figure(640x480) +# fill = True +# gid = None +# hatch = None +# height = 1 +# in_layout = False +# joinstyle = miter +# label = +# linestyle or ls = solid +# linewidth or lw = 0.0 +# patch_transform = CompositeGenericTransform( BboxTransformTo( ... +# path = Path(array([[0., 0.], [1., 0.], [1.,... +# path_effects = [] +# picker = None +# rasterized = None +# sketch_params = None +# snap = None +# transform = CompositeGenericTransform( CompositeGenericTra... +# transformed_clip_path_and_affine = (None, None) +# url = None +# verts = [[ 0. 0.] [640. 0.] [640. 480.] [ 0. 480.... +# visible = True +# width = 1 +# window_extent = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0) +# x = 0 +# xy = (0, 0) +# y = 0 +# zorder = 1 +# +# The docstrings for all of the classes also contain the ``Artist`` +# properties, so you can consult the interactive "help" or the +# :ref:`artist-api` for a listing of properties for a given object. +# +# .. _object-containers: +# +# Object containers +# ================= +# +# +# Now that we know how to inspect and set the properties of a given +# object we want to configure, we need to know how to get at that object. +# As mentioned in the introduction, there are two kinds of objects: +# primitives and containers. The primitives are usually the things you +# want to configure (the font of a :class:`~matplotlib.text.Text` +# instance, the width of a :class:`~matplotlib.lines.Line2D`) although +# the containers also have some properties as well -- for example the +# :class:`~matplotlib.axes.Axes` :class:`~matplotlib.artist.Artist` is a +# container that contains many of the primitives in your plot, but it +# also has properties like the ``xscale`` to control whether the xaxis +# is 'linear' or 'log'. In this section we'll review where the various +# container objects store the ``Artists`` that you want to get at. +# +# .. _figure-container: +# +# Figure container +# ---------------- +# +# The top level container ``Artist`` is the +# :class:`matplotlib.figure.Figure`, and it contains everything in the +# figure. The background of the figure is a +# :class:`~matplotlib.patches.Rectangle` which is stored in +# :attr:`!Figure.patch`. As +# you add subplots (:meth:`~matplotlib.figure.Figure.add_subplot`) and +# Axes (:meth:`~matplotlib.figure.Figure.add_axes`) to the figure +# these will be appended to the :attr:`Figure.axes +# `. These are also returned by the +# methods that create them: +# +# .. sourcecode:: ipython +# +# In [156]: fig = plt.figure() +# +# In [157]: ax1 = fig.add_subplot(211) +# +# In [158]: ax2 = fig.add_axes((0.1, 0.1, 0.7, 0.3)) +# +# In [159]: ax1 +# Out[159]: +# +# In [160]: print(fig.axes) +# [, ] +# +# Because the figure maintains the concept of the "current Axes" (see +# :meth:`Figure.gca ` and +# :meth:`Figure.sca `) to support the +# pylab/pyplot state machine, you should not insert or remove Axes +# directly from the Axes list, but rather use the +# :meth:`~matplotlib.figure.Figure.add_subplot` and +# :meth:`~matplotlib.figure.Figure.add_axes` methods to insert, and the +# `Axes.remove ` method to delete. You are +# free however, to iterate over the list of Axes or index into it to get +# access to ``Axes`` instances you want to customize. Here is an +# example which turns all the Axes grids on:: +# +# for ax in fig.axes: +# ax.grid(True) +# +# +# The figure also has its own ``images``, ``lines``, ``patches`` and ``text`` +# attributes, which you can use to add primitives directly. When doing so, the +# default coordinate system for the ``Figure`` will simply be in pixels (which +# is not usually what you want). If you instead use Figure-level methods to add +# Artists (e.g., using `.Figure.text` to add text), then the default coordinate +# system will be "figure coordinates" where (0, 0) is the bottom-left of the +# figure and (1, 1) is the top-right of the figure. +# +# As with all ``Artist``\s, you can control this coordinate system by setting +# the transform property. You can explicitly use "figure coordinates" by +# setting the ``Artist`` transform to :attr:`!fig.transFigure`: + +import matplotlib.lines as lines + +fig = plt.figure() + +l1 = lines.Line2D([0, 1], [0, 1], transform=fig.transFigure, figure=fig) +l2 = lines.Line2D([0, 1], [1, 0], transform=fig.transFigure, figure=fig) +fig.lines.extend([l1, l2]) + +plt.show() + +# %% +# Here is a summary of the Artists the Figure contains +# +# ================ ============================================================ +# Figure attribute Description +# ================ ============================================================ +# axes A list of `~.axes.Axes` instances +# patch The `.Rectangle` background +# images A list of `.FigureImage` patches - +# useful for raw pixel display +# legends A list of Figure `.Legend` instances +# (different from ``Axes.get_legend()``) +# lines A list of Figure `.Line2D` instances +# (rarely used, see ``Axes.lines``) +# patches A list of Figure `.Patch`\s +# (rarely used, see ``Axes.patches``) +# texts A list Figure `.Text` instances +# ================ ============================================================ +# +# .. _axes-container: +# +# Axes container +# -------------- +# +# The :class:`matplotlib.axes.Axes` is the center of the Matplotlib +# universe -- it contains the vast majority of all the ``Artists`` used +# in a figure with many helper methods to create and add these +# ``Artists`` to itself, as well as helper methods to access and +# customize the ``Artists`` it contains. Like the +# :class:`~matplotlib.figure.Figure`, it contains a +# :class:`~matplotlib.patches.Patch` +# :attr:`!matplotlib.axes.Axes.patch` which is a +# :class:`~matplotlib.patches.Rectangle` for Cartesian coordinates and a +# :class:`~matplotlib.patches.Circle` for polar coordinates; this patch +# determines the shape, background and border of the plotting region:: +# +# ax = fig.add_subplot() +# rect = ax.patch # a Rectangle instance +# rect.set_facecolor('green') +# +# When you call a plotting method, e.g., the canonical +# `~matplotlib.axes.Axes.plot` and pass in arrays or lists of values, the +# method will create a `matplotlib.lines.Line2D` instance, update the line with +# all the ``Line2D`` properties passed as keyword arguments, add the line to +# the ``Axes``, and return it to you: +# +# .. sourcecode:: ipython +# +# In [213]: x, y = np.random.rand(2, 100) +# +# In [214]: line, = ax.plot(x, y, '-', color='blue', linewidth=2) +# +# ``plot`` returns a list of lines because you can pass in multiple x, y +# pairs to plot, and we are unpacking the first element of the length +# one list into the line variable. The line has been added to the +# ``Axes.lines`` list: +# +# .. sourcecode:: ipython +# +# In [229]: print(ax.lines) +# [] +# +# Similarly, methods that create patches, like +# :meth:`~matplotlib.axes.Axes.bar` creates a list of rectangles, will +# add the patches to the :attr:`!Axes.patches` list: +# +# .. sourcecode:: ipython +# +# In [233]: n, bins, rectangles = ax.hist(np.random.randn(1000), 50) +# +# In [234]: rectangles +# Out[234]: +# +# In [235]: print(len(ax.patches)) +# Out[235]: 50 +# +# You should not add objects directly to the ``Axes.lines`` or ``Axes.patches`` +# lists, because the ``Axes`` needs to do a few things when it creates and adds +# an object: +# +# - It sets the ``figure`` and ``axes`` property of the ``Artist``; +# - It sets the default ``Axes`` transformation (unless one is already set); +# - It inspects the data contained in the ``Artist`` to update the data +# structures controlling auto-scaling, so that the view limits can be +# adjusted to contain the plotted data. +# +# You can, nonetheless, create objects yourself and add them directly to the +# ``Axes`` using helper methods like `~matplotlib.axes.Axes.add_line` and +# `~matplotlib.axes.Axes.add_patch`. Here is an annotated interactive session +# illustrating what is going on: +# +# .. sourcecode:: ipython +# +# In [262]: fig, ax = plt.subplots() +# +# # create a rectangle instance +# In [263]: rect = matplotlib.patches.Rectangle((1, 1), width=5, height=12) +# +# # by default the Axes instance is None +# In [264]: print(rect.axes) +# None +# +# # and the transformation instance is set to the "identity transform" +# In [265]: print(rect.get_data_transform()) +# IdentityTransform() +# +# # now we add the Rectangle to the Axes +# In [266]: ax.add_patch(rect) +# +# # and notice that the ax.add_patch method has set the Axes +# # instance +# In [267]: print(rect.axes) +# Axes(0.125,0.1;0.775x0.8) +# +# # and the transformation has been set too +# In [268]: print(rect.get_data_transform()) +# CompositeGenericTransform( +# TransformWrapper( +# BlendedAffine2D( +# IdentityTransform(), +# IdentityTransform())), +# CompositeGenericTransform( +# BboxTransformFrom( +# TransformedBbox( +# Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0), +# TransformWrapper( +# BlendedAffine2D( +# IdentityTransform(), +# IdentityTransform())))), +# BboxTransformTo( +# TransformedBbox( +# Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88), +# BboxTransformTo( +# TransformedBbox( +# Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8), +# Affine2D( +# [[100. 0. 0.] +# [ 0. 100. 0.] +# [ 0. 0. 1.]]))))))) +# +# # the default Axes transformation is ax.transData +# In [269]: print(ax.transData) +# CompositeGenericTransform( +# TransformWrapper( +# BlendedAffine2D( +# IdentityTransform(), +# IdentityTransform())), +# CompositeGenericTransform( +# BboxTransformFrom( +# TransformedBbox( +# Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0), +# TransformWrapper( +# BlendedAffine2D( +# IdentityTransform(), +# IdentityTransform())))), +# BboxTransformTo( +# TransformedBbox( +# Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88), +# BboxTransformTo( +# TransformedBbox( +# Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8), +# Affine2D( +# [[100. 0. 0.] +# [ 0. 100. 0.] +# [ 0. 0. 1.]]))))))) +# +# # notice that the xlimits of the Axes have not been changed +# In [270]: print(ax.get_xlim()) +# (0.0, 1.0) +# +# # but the data limits have been updated to encompass the rectangle +# In [271]: print(ax.dataLim.bounds) +# (1.0, 1.0, 5.0, 12.0) +# +# # we can manually invoke the auto-scaling machinery +# In [272]: ax.autoscale_view() +# +# # and now the xlim are updated to encompass the rectangle, plus margins +# In [273]: print(ax.get_xlim()) +# (0.75, 6.25) +# +# # we have to manually force a figure draw +# In [274]: fig.canvas.draw() +# +# +# There are many, many ``Axes`` helper methods for creating primitive +# ``Artists`` and adding them to their respective containers. The table +# below summarizes a small sampling of them, the kinds of ``Artist`` they +# create, and where they store them +# +# ========================================= ================= =============== +# Axes helper method Artist Container +# ========================================= ================= =============== +# `~.axes.Axes.annotate` - text annotations `.Annotation` ax.texts +# `~.axes.Axes.bar` - bar charts `.Rectangle` ax.patches +# `~.axes.Axes.errorbar` - error bar plots `.Line2D` and ax.lines and +# `.Rectangle` ax.patches +# `~.axes.Axes.fill` - shared area `.Polygon` ax.patches +# `~.axes.Axes.hist` - histograms `.Rectangle` ax.patches +# `~.axes.Axes.imshow` - image data `.AxesImage` ax.images +# `~.axes.Axes.legend` - Axes legend `.Legend` ax.get_legend() +# `~.axes.Axes.plot` - xy plots `.Line2D` ax.lines +# `~.axes.Axes.scatter` - scatter charts `.PolyCollection` ax.collections +# `~.axes.Axes.text` - text `.Text` ax.texts +# ========================================= ================= =============== +# +# +# In addition to all of these ``Artists``, the ``Axes`` contains two +# important ``Artist`` containers: the :class:`~matplotlib.axis.XAxis` +# and :class:`~matplotlib.axis.YAxis`, which handle the drawing of the +# ticks and labels. These are stored as instance variables +# :attr:`!matplotlib.axes.Axes.xaxis` and +# :attr:`!matplotlib.axes.Axes.yaxis`. The ``XAxis`` and ``YAxis`` +# containers will be detailed below, but note that the ``Axes`` contains +# many helper methods which forward calls on to the +# :class:`~matplotlib.axis.Axis` instances, so you often do not need to +# work with them directly unless you want to. For example, you can set +# the font color of the ``XAxis`` ticklabels using the ``Axes`` helper +# method:: +# +# ax.tick_params(axis='x', labelcolor='orange') +# +# Below is a summary of the Artists that the `~.axes.Axes` contains +# +# ============== ========================================= +# Axes attribute Description +# ============== ========================================= +# artists An `.ArtistList` of `.Artist` instances +# patch `.Rectangle` instance for Axes background +# collections An `.ArtistList` of `.Collection` instances +# images An `.ArtistList` of `.AxesImage` +# lines An `.ArtistList` of `.Line2D` instances +# patches An `.ArtistList` of `.Patch` instances +# texts An `.ArtistList` of `.Text` instances +# xaxis A `matplotlib.axis.XAxis` instance +# yaxis A `matplotlib.axis.YAxis` instance +# ============== ========================================= +# +# The legend can be accessed by `~.axes.Axes.get_legend`, +# +# .. _axis-container: +# +# Axis containers +# --------------- +# +# The :class:`matplotlib.axis.Axis` instances handle the drawing of the +# tick lines, the grid lines, the tick labels and the axis label. You +# can configure the left and right ticks separately for the y-axis, and +# the upper and lower ticks separately for the x-axis. The ``Axis`` +# also stores the data and view intervals used in auto-scaling, panning +# and zooming, as well as the :class:`~matplotlib.ticker.Locator` and +# :class:`~matplotlib.ticker.Formatter` instances which control where +# the ticks are placed and how they are represented as strings. +# +# Each ``Axis`` object contains a :attr:`~matplotlib.axis.Axis.label` attribute +# (this is what :mod:`.pyplot` modifies in calls to `~.pyplot.xlabel` and +# `~.pyplot.ylabel`) as well as a list of major and minor ticks. The ticks are +# `.axis.XTick` and `.axis.YTick` instances, which contain the actual line and +# text primitives that render the ticks and ticklabels. Because the ticks are +# dynamically created as needed (e.g., when panning and zooming), you should +# access the lists of major and minor ticks through their accessor methods +# `.axis.Axis.get_major_ticks` and `.axis.Axis.get_minor_ticks`. Although +# the ticks contain all the primitives and will be covered below, ``Axis`` +# instances have accessor methods that return the tick lines, tick labels, tick +# locations etc.: + +fig, ax = plt.subplots() +axis = ax.xaxis +axis.get_ticklocs() + +# %% + +axis.get_ticklabels() + +# %% +# note there are twice as many ticklines as labels because by default there are +# tick lines at the top and bottom but only tick labels below the xaxis; +# however, this can be customized. + +axis.get_ticklines() + +# %% +# And with the above methods, you only get lists of major ticks back by +# default, but you can also ask for the minor ticks: + +axis.get_ticklabels(minor=True) +axis.get_ticklines(minor=True) + +# %% +# Here is a summary of some of the useful accessor methods of the ``Axis`` +# (these have corresponding setters where useful, such as +# :meth:`~matplotlib.axis.Axis.set_major_formatter`.) +# +# ============================= ============================================== +# Axis accessor method Description +# ============================= ============================================== +# `~.Axis.get_scale` The scale of the Axis, e.g., 'log' or 'linear' +# `~.Axis.get_view_interval` The interval instance of the Axis view limits +# `~.Axis.get_data_interval` The interval instance of the Axis data limits +# `~.Axis.get_gridlines` A list of grid lines for the Axis +# `~.Axis.get_label` The Axis label - a `.Text` instance +# `~.Axis.get_offset_text` The Axis offset text - a `.Text` instance +# `~.Axis.get_ticklabels` A list of `.Text` instances - +# keyword minor=True|False +# `~.Axis.get_ticklines` A list of `.Line2D` instances - +# keyword minor=True|False +# `~.Axis.get_ticklocs` A list of Tick locations - +# keyword minor=True|False +# `~.Axis.get_major_locator` The `.ticker.Locator` instance for major ticks +# `~.Axis.get_major_formatter` The `.ticker.Formatter` instance for major +# ticks +# `~.Axis.get_minor_locator` The `.ticker.Locator` instance for minor ticks +# `~.Axis.get_minor_formatter` The `.ticker.Formatter` instance for minor +# ticks +# `~.axis.Axis.get_major_ticks` A list of `.Tick` instances for major ticks +# `~.axis.Axis.get_minor_ticks` A list of `.Tick` instances for minor ticks +# `~.Axis.grid` Turn the grid on or off for the major or minor +# ticks +# ============================= ============================================== +# +# Here is an example, not recommended for its beauty, which customizes +# the Axes and Tick properties. + +# plt.figure creates a matplotlib.figure.Figure instance +fig = plt.figure() +rect = fig.patch # a rectangle instance +rect.set_facecolor('lightgoldenrodyellow') + +ax1 = fig.add_axes((0.1, 0.3, 0.4, 0.4)) +rect = ax1.patch +rect.set_facecolor('lightslategray') + + +for label in ax1.xaxis.get_ticklabels(): + # label is a Text instance + label.set_color('red') + label.set_rotation(45) + label.set_fontsize(16) + +for line in ax1.yaxis.get_ticklines(): + # line is a Line2D instance + line.set_color('green') + line.set_markersize(25) + line.set_markeredgewidth(3) + +plt.show() + +# %% +# .. _tick-container: +# +# Tick containers +# --------------- +# +# The :class:`matplotlib.axis.Tick` is the final container object in our +# descent from the :class:`~matplotlib.figure.Figure` to the +# :class:`~matplotlib.axes.Axes` to the :class:`~matplotlib.axis.Axis` +# to the :class:`~matplotlib.axis.Tick`. The ``Tick`` contains the tick +# and grid line instances, as well as the label instances for the upper +# and lower ticks. Each of these is accessible directly as an attribute +# of the ``Tick``. +# +# ============== ========================================================== +# Tick attribute Description +# ============== ========================================================== +# tick1line A `.Line2D` instance +# tick2line A `.Line2D` instance +# gridline A `.Line2D` instance +# label1 A `.Text` instance +# label2 A `.Text` instance +# ============== ========================================================== +# +# Here is an example which sets the formatter for the right side ticks with +# dollar signs and colors them green on the right side of the yaxis. +# +# +# .. include:: ../gallery/ticks/dollar_ticks.rst +# :start-after: .. redirect-from:: /gallery/pyplots/dollar_ticks +# :end-before: .. admonition:: References diff --git a/galleries/tutorials/coding_shortcuts.py b/galleries/tutorials/coding_shortcuts.py new file mode 100644 index 000000000000..46868482598f --- /dev/null +++ b/galleries/tutorials/coding_shortcuts.py @@ -0,0 +1,172 @@ +""" +================ +Coding shortcuts +================ + +Matplotlib's primary and universal API is the :ref:`Axes interface `. +While it is clearly structured and powerful, it can sometimes feel overly verbose and +thus cumbersome to write. This page collects patterns for condensing the code +of the Axes-based API and achieving the same results with less typing for many simpler +plots. + +.. note:: + + The :ref:`pyplot interface ` is an alternative more compact + interface, and was historically modeled to be similar to MATLAB. It remains a + valid approach for those who want to use it. However, it has the disadvantage that + it achieves its brevity through implicit assumptions that you have to understand. + + Since it follows a different paradigm, switching between the Axes interface and + the pyplot interface requires a shift of the mental model, and some code rewrite, + if the code develops to a point at which pyplot no longer provides enough + flexibility. + +This tutorial goes the other way round, starting from the standard verbose Axes +interface and using its capabilities for shortcuts when you don't need all the +generality. + +Let's assume we want to make a plot of the number of daylight hours per day over the +year in London. + +The standard approach with the Axes interface looks like this. +""" + +import matplotlib.pyplot as plt +import numpy as np + +day = np.arange(365) +hours = 4.276 * np.sin(2 * np.pi * (day - 80)/365) + 12.203 + +fig, ax = plt.subplots() +ax.plot(day, hours, color="orange") +ax.set_xlabel("day") +ax.set_ylabel("daylight hours") +ax.set_title("London") +plt.show() + +# %% +# Note that we've included ``plt.show()`` here. This is needed to show the plot window +# when running from a command line or in a Python script. If you run a Jupyter notebook, +# this command is automatically executed at the end of each cell. +# +# For the rest of the tutorial, we'll assume that we are in a notebook and leave this +# out for brevity. Depending on your context you may still need it. +# +# If you instead want to save to a file, use ``fig.savefig("daylight.png")``. +# +# +# Collect Axes properties into a single ``set()`` call +# ==================================================== +# +# The properties of Matplotlib Artists can be modified through their respective +# ``set_*()`` methods. Artists additionally have a generic ``set()`` method, that takes +# keyword arguments and is equivalent to calling all the respective ``set_*()`` methods. +# :: +# +# ax.set_xlabel("day") +# ax.set_ylabel("daylight hours") +# +# can also be written as :: +# +# ax.set(xlabel="day", ylabel="daylight hours") +# +# This is the most simple and effective reduction you can do. With that we can shorten +# the above plot to + +fig, ax = plt.subplots() +ax.plot(day, hours, color="orange") +ax.set(xlabel="day", ylabel="daylight hours", title="London") + +# %% +# +# This works as long as you only need to pass one parameter to the ``set_*()`` function. +# The individual functions are still necessary if you want more control, e.g. +# ``set_title("London", fontsize=16)``. +# +# +# Not storing a reference to the figure +# ===================================== +# Another nuisance of ``fig, ax = plt.subplots()`` is that you always create a ``fig`` +# variable, even if you don't use it. A slightly shorter version would be using the +# standard variable for unused value in Python (``_``): ``_, ax = plt.subplots()``. +# However, that's only marginally better. +# +# You can work around this by separating figure and Axes creation and chaining them :: +# +# ax = plt.figure().add_subplot() +# +# This is a bit cleaner logically and has the slight advantage that you could set +# figure properties inline as well; e.g. ``plt.figure(facecolor="lightgoldenrod")``. +# But it has the disadvantage that it's longer than ``fig, ax = plt.subplots()``. +# +# You can still obtain the figure from the Axes if needed, e.g. :: +# +# ax.figure.savefig("daylight_hours.png") +# +# The example code now looks like this: + +ax = plt.figure().add_subplot() +ax.plot(day, hours, color="orange") +ax.set(xlabel="day", ylabel="daylight hours", title="London") + +# %% +# Define Axes properties during axes creation +# =========================================== +# The ``set_*`` methods as well as ``set`` modify existing objects. You can +# alternatively define them right at creation. Since you typically don't instantiate +# classes yourself in Matplotlib, but rather call some factory function that creates +# the object and wires it up correctly with the plot, this may seem less obvious. But +# in fact you just pass the desired properties to the factory functions. You are likely +# doing this already in some places without realizing. Consider the function to create +# a line :: +# +# ax.plot(x, y, color="orange") +# +# This is equivalent to :: +# +# line, = ax.plot(x, y) +# line.set_color("orange") +# +# The same can be done with functions that create Axes. + +ax = plt.figure().add_subplot(xlabel="day", ylabel="daylight hours", title="London") +ax.plot(day, hours, color="orange") + +# %% +# .. important:: +# The Axes properties are only accepted as keyword arguments by +# `.Figure.add_subplot`, which creates a single Axes. +# +# For `.Figure.subplots` and `.pyplot.subplots`, you'd have to pass the properties +# as a dict via the keyword argument ``subplot_kw``. The limitation here is that +# such parameters are given to all Axes. For example, if you need two subplots +# (``fig, (ax1, ax2) = plt.subplots(1, 2)``) with different labels, you have to +# set them individually. +# +# Defining Axes properties during creation is best used for single subplots or when +# all subplots share the same properties. +# +# +# Using implicit figure creation +# ============================== +# You can go even further by tapping into the pyplot logic and use `.pyplot.axes` to +# create the axes: + +ax = plt.axes(xlabel="day", ylabel="daylight hours", title="London") +ax.plot(day, hours, color="orange") + +# %% +# .. warning:: +# When using this, you have to be aware of the implicit figure semantics of pyplot. +# ``plt.axes()`` will only create a new figure if no figure exists. Otherwise, it +# will add the Axes to the current existing figure, which is likely not what you +# want. +# +# Not storing a reference to the Axes +# =================================== +# If you only need to visualize one dataset, you can append the plot command +# directly to the Axes creation. This may be useful e.g. in notebooks, +# where you want to create a plot with some configuration, but as little distracting +# code as possible: + +plt.axes(xlabel="day", ylabel="daylight hours").plot(day, hours, color="orange") diff --git a/galleries/tutorials/images.py b/galleries/tutorials/images.py new file mode 100644 index 000000000000..6c4e68c32416 --- /dev/null +++ b/galleries/tutorials/images.py @@ -0,0 +1,207 @@ +""" +.. redirect-from:: /tutorials/introductory/images + +.. _image_tutorial: + +============== +Image tutorial +============== + +This tutorial will use Matplotlib's implicit plotting interface, pyplot. This +interface maintains global state, and is very useful for quickly and easily +experimenting with various plot settings. The alternative is the explicit, +which is more suitable for large application development. For an explanation +of the tradeoffs between the implicit and explicit interfaces see +:ref:`api_interfaces` and the :ref:`Quick start guide +` to start using the explicit interface. +For now, let's get on with the implicit approach: + +""" + +from PIL import Image + +import matplotlib.pyplot as plt +import numpy as np + +# %% +# .. _importing_data: +# +# Importing image data into Numpy arrays +# ====================================== +# +# Matplotlib relies on the Pillow_ library to load image data. +# +# .. _Pillow: https://pillow.readthedocs.io/en/latest/ +# +# Here's the image we're going to play with: +# +# .. image:: ../_static/stinkbug.png +# +# It's a 24-bit RGB PNG image (8 bits for each of R, G, B). Depending +# on where you get your data, the other kinds of image that you'll most +# likely encounter are RGBA images, which allow for transparency, or +# single-channel grayscale (luminosity) images. Download `stinkbug.png +# `_ +# to your computer for the rest of this tutorial. +# +# We use Pillow to open an image (with `PIL.Image.open`), and immediately +# convert the `PIL.Image.Image` object into an 8-bit (``dtype=uint8``) numpy +# array. + +img = np.asarray(Image.open('../../doc/_static/stinkbug.png')) +print(repr(img)) + +# %% +# Each inner list represents a pixel. Here, with an RGB image, there +# are 3 values. Since it's a black and white image, R, G, and B are all +# similar. An RGBA (where A is alpha, or transparency) has 4 values +# per inner list, and a simple luminance image just has one value (and +# is thus only a 2-D array, not a 3-D array). For RGB and RGBA images, +# Matplotlib supports float32 and uint8 data types. For grayscale, +# Matplotlib supports only float32. If your array data does not meet +# one of these descriptions, you need to rescale it. +# +# .. _plotting_data: +# +# Plotting numpy arrays as images +# =================================== +# +# So, you have your data in a numpy array (either by importing it, or by +# generating it). Let's render it. In Matplotlib, this is performed +# using the :func:`~matplotlib.pyplot.imshow` function. Here we'll grab +# the plot object. This object gives you an easy way to manipulate the +# plot from the prompt. + +imgplot = plt.imshow(img) + +# %% +# You can also plot any numpy array. +# +# .. _Pseudocolor: +# +# Applying pseudocolor schemes to image plots +# ------------------------------------------------- +# +# Pseudocolor can be a useful tool for enhancing contrast and +# visualizing your data more easily. This is especially useful when +# making presentations of your data using projectors - their contrast is +# typically quite poor. +# +# Pseudocolor is only relevant to single-channel, grayscale, luminosity +# images. We currently have an RGB image. Since R, G, and B are all +# similar (see for yourself above or in your data), we can just pick one +# channel of our data using array slicing (you can read more in the +# `Numpy tutorial `_): + +lum_img = img[:, :, 0] +plt.imshow(lum_img) + +# %% +# Now, with a luminosity (2D, no color) image, the default colormap (aka lookup table, +# LUT), is applied. The default is called viridis. There are plenty of +# others to choose from. + +plt.imshow(lum_img, cmap="hot") + +# %% +# Note that you can also change colormaps on existing plot objects using the +# :meth:`~matplotlib.cm.ScalarMappable.set_cmap` method: + +imgplot = plt.imshow(lum_img) +imgplot.set_cmap('nipy_spectral') + +# %% +# +# There are many other colormap schemes available. See the :ref:`list and images +# of the colormaps`. +# +# .. _`Color Bars`: +# +# Color scale reference +# ------------------------ +# +# It's helpful to have an idea of what value a color represents. We can +# do that by adding a color bar to your figure: + +imgplot = plt.imshow(lum_img) +plt.colorbar() + +# %% +# .. _`Data ranges`: +# +# Examining a specific data range +# --------------------------------- +# +# Sometimes you want to enhance the contrast in your image, or expand +# the contrast in a particular region while sacrificing the detail in +# colors that don't vary much, or don't matter. A good tool to find +# interesting regions is the histogram. To create a histogram of our +# image data, we use the :func:`~matplotlib.pyplot.hist` function. + +plt.hist(lum_img.ravel(), bins=range(256), fc='k', ec='k') + +# %% +# Most often, the "interesting" part of the image is around the peak, +# and you can get extra contrast by clipping the regions above and/or +# below the peak. In our histogram, it looks like there's not much +# useful information in the high end (not many white things in the +# image). Let's adjust the upper limit, so that we effectively "zoom in +# on" part of the histogram. We do this by setting *clim*, the colormap +# limits. +# +# This can be done by passing a *clim* keyword argument in the call to +# ``imshow``. + +plt.imshow(lum_img, clim=(0, 175)) + +# %% +# This can also be done by calling the +# :meth:`~matplotlib.cm.ScalarMappable.set_clim` method of the returned image +# plot object. + +imgplot = plt.imshow(lum_img) +imgplot.set_clim(0, 175) + +# %% +# .. _Interpolation: +# +# Array Interpolation schemes +# --------------------------- +# +# Interpolation calculates what the color or value of a pixel "should" +# be, according to different mathematical schemes. One common place +# that this happens is when you resize an image. The number of pixels +# change, but you want the same information. Since pixels are discrete, +# there's missing space. Interpolation is how you fill that space. +# This is why your images sometimes come out looking pixelated when you +# blow them up. The effect is more pronounced when the difference +# between the original image and the expanded image is greater. Let's +# take our image and shrink it. We're effectively discarding pixels, +# only keeping a select few. Now when we plot it, that data gets blown +# up to the size on your screen. The old pixels aren't there anymore, +# and the computer has to draw in pixels to fill that space. +# +# We'll use the Pillow library that we used to load the image also to resize +# the image. + +img = Image.open('../../doc/_static/stinkbug.png') +img.thumbnail((64, 64)) # resizes image in-place +imgplot = plt.imshow(img) + +# %% +# Here we use the default interpolation ("nearest"), since we did not +# give :func:`~matplotlib.pyplot.imshow` any interpolation argument. +# +# Let's try some others. Here's "bilinear": + +imgplot = plt.imshow(img, interpolation="bilinear") + +# %% +# and bicubic: + +imgplot = plt.imshow(img, interpolation="bicubic") + +# %% +# Bicubic interpolation is often used when blowing up photos - people +# tend to prefer blurry over pixelated. diff --git a/galleries/tutorials/index.rst b/galleries/tutorials/index.rst new file mode 100644 index 000000000000..76c0037dca11 --- /dev/null +++ b/galleries/tutorials/index.rst @@ -0,0 +1,177 @@ +.. _tutorials: + +Tutorials +========= + +This page contains a few tutorials for using Matplotlib. For the old tutorials, see :ref:`below `. + +For shorter examples, see our :ref:`examples page `. +You can also find :ref:`external resources ` and +a :ref:`FAQ ` in our :ref:`user guide `. + + +.. raw:: html + +
    + + +.. raw:: html + +
    + +.. only:: html + + .. image:: /tutorials/images/thumb/sphx_glr_pyplot_thumb.png + :alt: Pyplot tutorial + + :ref:`sphx_glr_tutorials_pyplot.py` + +.. raw:: html + +
    Pyplot tutorial
    +
    + + +.. raw:: html + +
    + +.. only:: html + + .. image:: /tutorials/images/thumb/sphx_glr_coding_shortcuts_thumb.png + :alt: Coding shortcuts + + :ref:`sphx_glr_tutorials_coding_shortcuts.py` + +.. raw:: html + +
    Coding shortcuts
    +
    + + +.. raw:: html + +
    + +.. only:: html + + .. image:: /tutorials/images/thumb/sphx_glr_images_thumb.png + :alt: Image tutorial + + :ref:`sphx_glr_tutorials_images.py` + +.. raw:: html + +
    Image tutorial
    +
    + + +.. raw:: html + +
    + +.. only:: html + + .. image:: /tutorials/images/thumb/sphx_glr_lifecycle_thumb.png + :alt: The Lifecycle of a Plot + + :ref:`sphx_glr_tutorials_lifecycle.py` + +.. raw:: html + +
    The Lifecycle of a Plot
    +
    + + +.. raw:: html + +
    + +.. only:: html + + .. image:: /tutorials/images/thumb/sphx_glr_artists_thumb.png + :alt: Artist tutorial + + :ref:`sphx_glr_tutorials_artists.py` + +.. raw:: html + +
    Artist tutorial
    +
    + + +.. raw:: html + +
    + + +.. toctree:: + :hidden: + + /tutorials/pyplot + /tutorials/coding_shortcuts + /tutorials/images + /tutorials/lifecycle + /tutorials/artists + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-gallery + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: tutorials_python.zip
    ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: tutorials_jupyter.zip ` + + + +.. _user_guide_tutorials: + +User guide tutorials +-------------------- + +Many of our tutorials were moved from this section to :ref:`users-guide-index`: + +Introductory +^^^^^^^^^^^^ + +- :ref:`quick_start` +- :ref:`customizing` +- :ref:`animations` + +Intermediate +^^^^^^^^^^^^ + +- :ref:`legend_guide` +- :ref:`color_cycle` +- :ref:`constrainedlayout_guide` +- :ref:`tight_layout_guide` +- :ref:`arranging_axes` +- :ref:`autoscale` +- :ref:`imshow_extent` + +Advanced +^^^^^^^^ + +- :ref:`blitting` +- :ref:`paths` +- :ref:`patheffects_guide` +- :ref:`transforms_tutorial` + +Colors +^^^^^^ + +See :ref:`tutorials-colors`. + +Text +^^^^ + +See :ref:`tutorials-text`. + +Toolkits +^^^^^^^^ + +See :ref:`tutorials-toolkits`. diff --git a/galleries/tutorials/lifecycle.py b/galleries/tutorials/lifecycle.py new file mode 100644 index 000000000000..4c009f802cf4 --- /dev/null +++ b/galleries/tutorials/lifecycle.py @@ -0,0 +1,279 @@ +""" +.. redirect-from:: /tutorials/introductory/lifecycle + +======================= +The Lifecycle of a Plot +======================= + +This tutorial aims to show the beginning, middle, and end of a single +visualization using Matplotlib. We'll begin with some raw data and +end by saving a figure of a customized visualization. Along the way we try +to highlight some neat features and best-practices using Matplotlib. + +.. currentmodule:: matplotlib + +.. note:: + + This tutorial is based on + `this excellent blog post + `_ + by Chris Moffitt. It was transformed into this tutorial by Chris Holdgraf. + +A note on the explicit vs. implicit interfaces +============================================== + +Matplotlib has two interfaces. For an explanation of the trade-offs between the +explicit and implicit interfaces see :ref:`api_interfaces`. + +In the explicit object-oriented (OO) interface we directly utilize instances of +:class:`axes.Axes` to build up the visualization in an instance of +:class:`figure.Figure`. In the implicit interface, inspired by and modeled on +MATLAB, we use a global state-based interface which is encapsulated in the +:mod:`.pyplot` module to plot to the "current Axes". See the :ref:`pyplot +tutorials ` for a more in-depth look at the +pyplot interface. + +Most of the terms are straightforward but the main thing to remember +is that: + +* The `.Figure` is the final image, and may contain one or more `~.axes.Axes`. +* The `~.axes.Axes` represents an individual plot (not to be confused with + `~.axis.Axis`, which refers to the x-, y-, or z-axis of a plot). + +We call methods that do the plotting directly from the Axes, which gives +us much more flexibility and power in customizing our plot. + +.. note:: + + In general, use the explicit interface over the implicit pyplot interface + for plotting. + +Our data +======== + +We'll use the data from the post from which this tutorial was derived. +It contains sales information for a number of companies. + +""" + +import matplotlib.pyplot as plt +# sphinx_gallery_thumbnail_number = 10 +import numpy as np + +data = {'Barton LLC': 109438.50, + 'Frami, Hills and Schmidt': 103569.59, + 'Fritsch, Russel and Anderson': 112214.71, + 'Jerde-Hilpert': 112591.43, + 'Keeling LLC': 100934.30, + 'Koepp Ltd': 103660.54, + 'Kulas Inc': 137351.96, + 'Trantow-Barrows': 123381.38, + 'White-Trantow': 135841.99, + 'Will LLC': 104437.60} +group_data = list(data.values()) +group_names = list(data.keys()) +group_mean = np.mean(group_data) + +# %% +# Getting started +# =============== +# +# This data is naturally visualized as a barplot, with one bar per +# group. To do this with the object-oriented approach, we first generate +# an instance of :class:`figure.Figure` and +# :class:`axes.Axes`. The Figure is like a canvas, and the Axes +# is a part of that canvas on which we will make a particular visualization. +# +# .. note:: +# +# Figures can have multiple Axes on them. For information on how to do this, +# see the :ref:`Tight Layout tutorial +# `. + +fig, ax = plt.subplots() + +# %% +# Now that we have an Axes instance, we can plot on top of it. + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) + +# %% +# Controlling the style +# ===================== +# +# There are many styles available in Matplotlib in order to let you tailor +# your visualization to your needs. To see a list of styles, we can use +# :mod:`.style`. + +print(plt.style.available) + +# %% +# You can activate a style with the following: + +plt.style.use('fivethirtyeight') + +# %% +# Now let's remake the above plot to see how it looks: + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) + +# %% +# The style controls many things, such as color, linewidths, backgrounds, +# etc. +# +# Customizing the plot +# ==================== +# +# Now we've got a plot with the general look that we want, so let's fine-tune +# it so that it's ready for print. First let's rotate the labels on the x-axis +# so that they show up more clearly. We can gain access to these labels +# with the :meth:`axes.Axes.get_xticklabels` method: + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() + +# %% +# If we'd like to set the property of many items at once, it's useful to use +# the :func:`pyplot.setp` function. This will take a list (or many lists) of +# Matplotlib objects, and attempt to set some style element of each one. + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') + +# %% +# It looks like this cut off some of the labels on the bottom. We can +# tell Matplotlib to automatically make room for elements in the figures +# that we create. To do this we set the ``autolayout`` value of our +# rcParams. For more information on controlling the style, layout, and +# other features of plots with rcParams, see +# :ref:`customizing`. + +plt.rcParams.update({'figure.autolayout': True}) + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') + +# %% +# Next, we add labels to the plot. To do this with the OO interface, +# we can use the :meth:`.Artist.set` method to set properties of this +# Axes object. + +fig, ax = plt.subplots() +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') +ax.set(xlim=(-10000, 140000), xlabel='Total Revenue', ylabel='Company', + title='Company Revenue') + +# %% +# We can also adjust the size of this plot using the :func:`pyplot.subplots` +# function. We can do this with the *figsize* keyword argument. +# +# .. note:: +# +# While indexing in NumPy follows the form (row, column), the *figsize* +# keyword argument follows the form (width, height). This follows +# conventions in visualization, which unfortunately are different from those +# of linear algebra. + +fig, ax = plt.subplots(figsize=(8, 4)) +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') +ax.set(xlim=(-10000, 140000), xlabel='Total Revenue', ylabel='Company', + title='Company Revenue') + +# %% +# For labels, we can specify custom formatting guidelines in the form of +# functions. Below we define a function that takes an integer as input, and +# returns a string as an output. When used with `.Axis.set_major_formatter` or +# `.Axis.set_minor_formatter`, they will automatically create and use a +# :class:`ticker.FuncFormatter` class. +# +# For this function, the ``x`` argument is the original tick label and ``pos`` +# is the tick position. We will only use ``x`` here but both arguments are +# needed. + + +def currency(x, pos): + """The two arguments are the value and tick position""" + if x >= 1e6: + s = f'${x*1e-6:1.1f}M' + else: + s = f'${x*1e-3:1.0f}K' + return s + +# %% +# We can then apply this function to the labels on our plot. To do this, +# we use the ``xaxis`` attribute of our Axes. This lets you perform +# actions on a specific axis on our plot. + +fig, ax = plt.subplots(figsize=(6, 8)) +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') + +ax.set(xlim=(-10000, 140000), xlabel='Total Revenue', ylabel='Company', + title='Company Revenue') +ax.xaxis.set_major_formatter(currency) + +# %% +# Combining multiple visualizations +# ================================= +# +# It is possible to draw multiple plot elements on the same instance of +# :class:`axes.Axes`. To do this we simply need to call another one of +# the plot methods on that Axes object. + +fig, ax = plt.subplots(figsize=(8, 8)) +ax.barh(group_names, group_data) +labels = ax.get_xticklabels() +plt.setp(labels, rotation=45, horizontalalignment='right') + +# Add a vertical line, here we set the style in the function call +ax.axvline(group_mean, ls='--', color='r') + +# Annotate new companies +for group in [3, 5, 8]: + ax.text(145000, group, "New Company", fontsize=10, + verticalalignment="center") + +# Now we move our title up since it's getting a little cramped +ax.title.set(y=1.05) + +ax.set(xlim=(-10000, 140000), xlabel='Total Revenue', ylabel='Company', + title='Company Revenue') +ax.xaxis.set_major_formatter(currency) +ax.set_xticks([0, 25e3, 50e3, 75e3, 100e3, 125e3]) +fig.subplots_adjust(right=.1) + +plt.show() + +# %% +# Saving our plot +# =============== +# +# Now that we're happy with the outcome of our plot, we want to save it to +# disk. There are many file formats we can save to in Matplotlib. To see +# a list of available options, use: + +print(fig.canvas.get_supported_filetypes()) + +# %% +# We can then use the :meth:`figure.Figure.savefig` in order to save the figure +# to disk. Note that there are several useful flags we show below: +# +# * ``transparent=True`` makes the background of the saved figure transparent +# if the format supports it. +# * ``dpi=80`` controls the resolution (dots per square inch) of the output. +# * ``bbox_inches="tight"`` fits the bounds of the figure to our plot. + +# Uncomment this line to save the figure. +# fig.savefig('sales.png', transparent=False, dpi=80, bbox_inches="tight") diff --git a/galleries/tutorials/pyplot.py b/galleries/tutorials/pyplot.py new file mode 100644 index 000000000000..8062e5aa3793 --- /dev/null +++ b/galleries/tutorials/pyplot.py @@ -0,0 +1,476 @@ +""" +.. redirect-from:: /tutorials/introductory/pyplot + +.. _pyplot_tutorial: + +=============== +Pyplot tutorial +=============== + +An introduction to the pyplot interface. Please also see +:ref:`quick_start` for an overview of how Matplotlib +works and :ref:`api_interfaces` for an explanation of the trade-offs between the +supported user APIs. + +""" + +# %% +# Introduction to pyplot +# ====================== +# +# :mod:`matplotlib.pyplot` is a collection of functions that make matplotlib +# work like MATLAB. Each ``pyplot`` function makes some change to a figure: +# e.g., creates a figure, creates a plotting area in a figure, plots some lines +# in a plotting area, decorates the plot with labels, etc. +# +# In :mod:`matplotlib.pyplot` various states are preserved +# across function calls, so that it keeps track of things like +# the current figure and plotting area, and the plotting +# functions are directed to the current Axes (please note that we use uppercase +# Axes to refer to the `~.axes.Axes` concept, which is a central +# :ref:`part of a figure ` +# and not only the plural of *axis*). +# +# .. note:: +# +# The implicit pyplot API is generally less verbose but also not as flexible as the +# explicit API. Most of the function calls you see here can also be called +# as methods from an ``Axes`` object. We recommend browsing the tutorials +# and examples to see how this works. See :ref:`api_interfaces` for an +# explanation of the trade-off of the supported user APIs. +# +# Generating visualizations with pyplot is very quick: + +import matplotlib.pyplot as plt + +plt.plot([1, 2, 3, 4]) +plt.ylabel('some numbers') +plt.show() + +# %% +# You may be wondering why the x-axis ranges from 0-3 and the y-axis +# from 1-4. If you provide a single list or array to +# `~.pyplot.plot`, matplotlib assumes it is a +# sequence of y values, and automatically generates the x values for +# you. Since python ranges start with 0, the default x vector has the +# same length as y but starts with 0; therefore, the x data are +# ``[0, 1, 2, 3]``. +# +# `~.pyplot.plot` is a versatile function, and will take an arbitrary number of +# arguments. For example, to plot x versus y, you can write: + +plt.plot([1, 2, 3, 4], [1, 4, 9, 16]) + +# %% +# Formatting the style of your plot +# --------------------------------- +# +# For every x, y pair of arguments, there is an optional third argument +# which is the format string that indicates the color and line type of +# the plot. The letters and symbols of the format string are from +# MATLAB, and you concatenate a color string with a line style string. +# The default format string is 'b-', which is a solid blue line. For +# example, to plot the above with red circles, you would issue + +plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro') +plt.axis((0, 6, 0, 20)) +plt.show() + +# %% +# See the `~.pyplot.plot` documentation for a complete +# list of line styles and format strings. The +# `~.pyplot.axis` function in the example above takes a +# list of ``[xmin, xmax, ymin, ymax]`` and specifies the viewport of the +# Axes. +# +# If matplotlib were limited to working with lists, it would be fairly +# useless for numeric processing. Generally, you will use `numpy +# `_ arrays. In fact, all sequences are +# converted to numpy arrays internally. The example below illustrates +# plotting several lines with different format styles in one function call +# using arrays. + +import numpy as np + +# evenly sampled time at 200ms intervals +t = np.arange(0., 5., 0.2) + +# red dashes, blue squares and green triangles +plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^') +plt.show() + +# %% +# .. _plotting-with-keywords: +# +# Plotting with keyword strings +# ============================= +# +# There are some instances where you have data in a format that lets you +# access particular variables with strings. For example, with `structured arrays`_ +# or `pandas.DataFrame`. +# +# .. _structured arrays: https://numpy.org/doc/stable/user/basics.rec.html#structured-arrays +# +# Matplotlib allows you to provide such an object with +# the ``data`` keyword argument. If provided, then you may generate plots with +# the strings corresponding to these variables. + +data = {'a': np.arange(50), + 'c': np.random.randint(0, 50, 50), + 'd': np.random.randn(50)} +data['b'] = data['a'] + 10 * np.random.randn(50) +data['d'] = np.abs(data['d']) * 100 + +plt.scatter('a', 'b', c='c', s='d', data=data) +plt.xlabel('entry a') +plt.ylabel('entry b') +plt.show() + +# %% +# .. _plotting-with-categorical-vars: +# +# Plotting with categorical variables +# =================================== +# +# It is also possible to create a plot using categorical variables. +# Matplotlib allows you to pass categorical variables directly to +# many plotting functions. For example: + +names = ['group_a', 'group_b', 'group_c'] +values = [1, 10, 100] + +plt.figure(figsize=(9, 3)) + +plt.subplot(131) +plt.bar(names, values) +plt.subplot(132) +plt.scatter(names, values) +plt.subplot(133) +plt.plot(names, values) +plt.suptitle('Categorical Plotting') +plt.show() + +# %% +# .. _controlling-line-properties: +# +# Controlling line properties +# =========================== +# +# Lines have many attributes that you can set: linewidth, dash style, +# antialiased, etc; see `matplotlib.lines.Line2D`. There are +# several ways to set line properties +# +# * Use keyword arguments:: +# +# plt.plot(x, y, linewidth=2.0) +# +# +# * Use the setter methods of a ``Line2D`` instance. ``plot`` returns a list +# of ``Line2D`` objects; e.g., ``line1, line2 = plot(x1, y1, x2, y2)``. In the code +# below we will suppose that we have only +# one line so that the list returned is of length 1. We use tuple unpacking with +# ``line,`` to get the first element of that list:: +# +# line, = plt.plot(x, y, '-') +# line.set_antialiased(False) # turn off antialiasing +# +# * Use `~.pyplot.setp`. The example below +# uses a MATLAB-style function to set multiple properties +# on a list of lines. ``setp`` works transparently with a list of objects +# or a single object. You can either use python keyword arguments or +# MATLAB-style string/value pairs:: +# +# lines = plt.plot(x1, y1, x2, y2) +# # use keyword arguments +# plt.setp(lines, color='r', linewidth=2.0) +# # or MATLAB style string value pairs +# plt.setp(lines, 'color', 'r', 'linewidth', 2.0) +# +# +# Here are the available `~.lines.Line2D` properties. +# +# ====================== ================================================== +# Property Value Type +# ====================== ================================================== +# alpha float +# animated [True | False] +# antialiased or aa [True | False] +# clip_box a matplotlib.transform.Bbox instance +# clip_on [True | False] +# clip_path a Path instance and a Transform instance, a Patch +# color or c any matplotlib color +# contains the hit testing function +# dash_capstyle [``'butt'`` | ``'round'`` | ``'projecting'``] +# dash_joinstyle [``'miter'`` | ``'round'`` | ``'bevel'``] +# dashes sequence of on/off ink in points +# data (np.array xdata, np.array ydata) +# figure a matplotlib.figure.Figure instance +# label any string +# linestyle or ls [ ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'steps'`` | ...] +# linewidth or lw float value in points +# marker [ ``'+'`` | ``','`` | ``'.'`` | ``'1'`` | ``'2'`` | ``'3'`` | ``'4'`` ] +# markeredgecolor or mec any matplotlib color +# markeredgewidth or mew float value in points +# markerfacecolor or mfc any matplotlib color +# markersize or ms float +# markevery [ None | integer | (startind, stride) ] +# picker used in interactive line selection +# pickradius the line pick selection radius +# solid_capstyle [``'butt'`` | ``'round'`` | ``'projecting'``] +# solid_joinstyle [``'miter'`` | ``'round'`` | ``'bevel'``] +# transform a matplotlib.transforms.Transform instance +# visible [True | False] +# xdata np.array +# ydata np.array +# zorder any number +# ====================== ================================================== +# +# To get a list of settable line properties, call the +# `~.pyplot.setp` function with a line or lines as argument +# +# .. sourcecode:: ipython +# +# In [69]: lines = plt.plot([1, 2, 3]) +# +# In [70]: plt.setp(lines) +# alpha: float +# animated: [True | False] +# antialiased or aa: [True | False] +# ...snip +# +# .. _multiple-figs-axes: +# +# +# Working with multiple figures and Axes +# ====================================== +# +# MATLAB, and :mod:`.pyplot`, have the concept of the current figure +# and the current Axes. All plotting functions apply to the current +# Axes. The function `~.pyplot.gca` returns the current Axes (a +# `matplotlib.axes.Axes` instance), and `~.pyplot.gcf` returns the current +# figure (a `matplotlib.figure.Figure` instance). Normally, you don't have to +# worry about this, because it is all taken care of behind the scenes. Below +# is a script to create two subplots. + + +def f(t): + return np.exp(-t) * np.cos(2*np.pi*t) + +t1 = np.arange(0.0, 5.0, 0.1) +t2 = np.arange(0.0, 5.0, 0.02) + +plt.figure() +plt.subplot(211) +plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k') + +plt.subplot(212) +plt.plot(t2, np.cos(2*np.pi*t2), 'r--') +plt.show() + +# %% +# The `~.pyplot.figure` call here is optional because a figure will be created +# if none exists, just as an Axes will be created (equivalent to an explicit +# ``subplot()`` call) if none exists. +# The `~.pyplot.subplot` call specifies ``numrows, +# numcols, plot_number`` where ``plot_number`` ranges from 1 to +# ``numrows*numcols``. The commas in the ``subplot`` call are +# optional if ``numrows*numcols<10``. So ``subplot(211)`` is identical +# to ``subplot(2, 1, 1)``. +# +# You can create an arbitrary number of subplots +# and Axes. If you want to place an Axes manually, i.e., not on a +# rectangular grid, use `~.pyplot.axes`, +# which allows you to specify the location as ``axes([left, bottom, +# width, height])`` where all values are in fractional (0 to 1) +# coordinates. See :doc:`/gallery/subplots_axes_and_figures/axes_demo` for an example of +# placing Axes manually and :doc:`/gallery/subplots_axes_and_figures/subplot` for an +# example with lots of subplots. +# +# You can create multiple figures by using multiple +# `~.pyplot.figure` calls with an increasing figure +# number. Of course, each figure can contain as many Axes and subplots +# as your heart desires:: +# +# import matplotlib.pyplot as plt +# plt.figure(1) # the first figure +# plt.subplot(211) # the first subplot in the first figure +# plt.plot([1, 2, 3]) +# plt.subplot(212) # the second subplot in the first figure +# plt.plot([4, 5, 6]) +# +# +# plt.figure(2) # a second figure +# plt.plot([4, 5, 6]) # creates a subplot() by default +# +# plt.figure(1) # first figure current; +# # subplot(212) still current +# plt.subplot(211) # make subplot(211) in the first figure +# # current +# plt.title('Easy as 1, 2, 3') # subplot 211 title +# +# You can clear the current figure with `~.pyplot.clf` +# and the current Axes with `~.pyplot.cla`. If you find +# it annoying that states (specifically the current image, figure and Axes) +# are being maintained for you behind the scenes, don't despair: this is just a thin +# stateful wrapper around an object-oriented API, which you can use +# instead (see :ref:`artists_tutorial`) +# +# If you are making lots of figures, you need to be aware of one +# more thing: the memory required for a figure is not completely +# released until the figure is explicitly closed with +# `~.pyplot.close`. Deleting all references to the +# figure, and/or using the window manager to kill the window in which +# the figure appears on the screen, is not enough, because pyplot +# maintains internal references until `~.pyplot.close` +# is called. +# +# .. _working-with-text: +# +# Working with text +# ================= +# +# `~.pyplot.text` can be used to add text in an arbitrary location, and +# `~.pyplot.xlabel`, `~.pyplot.ylabel` and `~.pyplot.title` are used to add +# text in the indicated locations (see :ref:`text_intro` for a +# more detailed example) + +mu, sigma = 100, 15 +x = mu + sigma * np.random.randn(10000) + +# the histogram of the data +n, bins, patches = plt.hist(x, 50, density=True, facecolor='g', alpha=0.75) + + +plt.xlabel('Smarts') +plt.ylabel('Probability') +plt.title('Histogram of IQ') +plt.text(60, .025, r'$\mu=100,\ \sigma=15$') +plt.axis([40, 160, 0, 0.03]) +plt.grid(True) +plt.show() + +# %% +# All of the `~.pyplot.text` functions return a `matplotlib.text.Text` +# instance. Just as with lines above, you can customize the properties by +# passing keyword arguments into the text functions or using `~.pyplot.setp`:: +# +# t = plt.xlabel('my data', fontsize=14, color='red') +# +# These properties are covered in more detail in :ref:`text_props`. +# +# +# Using mathematical expressions in text +# -------------------------------------- +# +# Matplotlib accepts TeX equation expressions in any text expression. +# For example to write the expression :math:`\sigma_i=15` in the title, +# you can write a TeX expression surrounded by dollar signs:: +# +# plt.title(r'$\sigma_i=15$') +# +# The ``r`` preceding the title string is important -- it signifies +# that the string is a *raw* string and not to treat backslashes as +# python escapes. matplotlib has a built-in TeX expression parser and +# layout engine, and ships its own math fonts -- for details see +# :ref:`mathtext`. Thus, you can use mathematical text across +# platforms without requiring a TeX installation. For those who have LaTeX +# and dvipng installed, you can also use LaTeX to format your text and +# incorporate the output directly into your display figures or saved +# postscript -- see :ref:`usetex`. +# +# +# Annotating text +# --------------- +# +# The uses of the basic `~.pyplot.text` function above +# place text at an arbitrary position on the Axes. A common use for +# text is to annotate some feature of the plot, and the +# `~.pyplot.annotate` method provides helper +# functionality to make annotations easy. In an annotation, there are +# two points to consider: the location being annotated represented by +# the argument ``xy`` and the location of the text ``xytext``. Both of +# these arguments are ``(x, y)`` tuples. + +ax = plt.subplot() + +t = np.arange(0.0, 5.0, 0.01) +s = np.cos(2*np.pi*t) +line, = plt.plot(t, s, lw=2) + +plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5), + arrowprops=dict(facecolor='black', shrink=0.05), + ) + +plt.ylim(-2, 2) +plt.show() + +# %% +# In this basic example, both the ``xy`` (arrow tip) and ``xytext`` +# locations (text location) are in data coordinates. There are a +# variety of other coordinate systems one can choose -- see +# :ref:`annotations-tutorial` and :ref:`plotting-guide-annotation` for +# details. More examples can be found in +# :doc:`/gallery/text_labels_and_annotations/annotation_demo`. +# +# +# Logarithmic and other nonlinear axes +# ==================================== +# +# :mod:`matplotlib.pyplot` supports not only linear axis scales, but also +# logarithmic and logit scales. This is commonly used if data spans many orders +# of magnitude. Changing the scale of an axis is easy:: +# +# plt.xscale('log') +# +# An example of four plots with the same data and different scales for the y-axis +# is shown below. + +# Fixing random state for reproducibility +np.random.seed(19680801) + +# make up some data in the open interval (0, 1) +y = np.random.normal(loc=0.5, scale=0.4, size=1000) +y = y[(y > 0) & (y < 1)] +y.sort() +x = np.arange(len(y)) + +# plot with various axes scales +plt.figure() + +# linear +plt.subplot(221) +plt.plot(x, y) +plt.yscale('linear') +plt.title('linear') +plt.grid(True) + +# log +plt.subplot(222) +plt.plot(x, y) +plt.yscale('log') +plt.title('log') +plt.grid(True) + +# symmetric log +plt.subplot(223) +plt.plot(x, y - y.mean()) +plt.yscale('symlog', linthresh=0.01) +plt.title('symlog') +plt.grid(True) + +# logit +plt.subplot(224) +plt.plot(x, y) +plt.yscale('logit') +plt.title('logit') +plt.grid(True) +# Adjust the subplot layout, because the logit one may take more space +# than usual, due to y-tick labels like "1 - 10^{-3}" +plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25, + wspace=0.35) + +plt.show() + +# %% +# It is also possible to add your own scale, see `matplotlib.scale` for +# details. diff --git a/galleries/users_explain/animations/README.txt b/galleries/users_explain/animations/README.txt new file mode 100644 index 000000000000..a7d37a0242e6 --- /dev/null +++ b/galleries/users_explain/animations/README.txt @@ -0,0 +1,9 @@ +=========================== +Animations using Matplotlib +=========================== + +Based on its plotting functionality, Matplotlib also provides an interface to +generate animations using the `~matplotlib.animation` module. An +animation is a sequence of frames where each frame corresponds to a plot on a +`~matplotlib.figure.Figure`. This tutorial covers a general guideline on +how to create such animations and the different options available. diff --git a/galleries/users_explain/animations/animations.py b/galleries/users_explain/animations/animations.py new file mode 100644 index 000000000000..dca49fc5228e --- /dev/null +++ b/galleries/users_explain/animations/animations.py @@ -0,0 +1,258 @@ +""" +.. redirect-from:: /tutorials/introductory/animation_tutorial + +.. _animations: + +=========================== +Animations using Matplotlib +=========================== + +Based on its plotting functionality, Matplotlib also provides an interface to +generate animations using the `~matplotlib.animation` module. An +animation is a sequence of frames where each frame corresponds to a plot on a +`~matplotlib.figure.Figure`. This tutorial covers a general guideline on +how to create such animations and the different options available. More information is available in the API description: `~matplotlib.animation` +""" + +import matplotlib.pyplot as plt +import numpy as np + +import matplotlib.animation as animation + +# %% +# Animation classes +# ================= +# +# The animation process in Matplotlib can be thought of in 2 different ways: +# +# - `~matplotlib.animation.FuncAnimation`: Generate data for first +# frame and then modify this data for each frame to create an animated plot. +# +# - `~matplotlib.animation.ArtistAnimation`: Generate a list (iterable) +# of artists that will draw in each frame in the animation. +# +# `~matplotlib.animation.FuncAnimation` is more efficient in terms of +# speed and memory as it draws an artist once and then modifies it. On the +# other hand `~matplotlib.animation.ArtistAnimation` is flexible as it +# allows any iterable of artists to be animated in a sequence. +# +# ``FuncAnimation`` +# ----------------- +# +# The `~matplotlib.animation.FuncAnimation` class allows us to create an +# animation by passing a function that iteratively modifies the data of a plot. +# This is achieved by using the *setter* methods on various +# `~matplotlib.artist.Artist` (examples: `~matplotlib.lines.Line2D`, +# `~matplotlib.collections.PathCollection`, etc.). A usual +# `~matplotlib.animation.FuncAnimation` object takes a +# `~matplotlib.figure.Figure` that we want to animate and a function +# *func* that modifies the data plotted on the figure. It uses the *frames* +# parameter to determine the length of the animation. The *interval* parameter +# is used to determine time in milliseconds between drawing of two frames. +# Animating using `.FuncAnimation` typically requires these steps: +# +# 1) Plot the initial figure as you would in a static plot. Save all the created +# artists, which are returned by the plot functions, in variables so that you can +# access and modify them later in the animation function. +# 2) Create an animation function that updates the artists for a given frame. +# Typically, this calls ``set_*`` methods of the artists. +# 3) Create a `.FuncAnimation`, passing the `.Figure` and the animation function. +# 4) Save or show the animation using one of the following methods: +# +# - `.pyplot.show` to show the animation in a window +# - `.Animation.to_html5_video` to create a HTML ``